Im currently learning how to code without an IDE and so Im learning how to write makefiles. Here is my current test-project:
\__ /CoDstructor/
|\__ Makefile
|\__ /bin/
| \__ CoDstructor.exe
|\__ /src/
| \__ /cod/
| |\__ main.cpp
| |\__ types.cpp
| \__ types.hpp
\__ /obj/
\__ /cod/
|\__ main.o
|\__ main.d
|\__ types.o
\__ types.d
I only have one single top level makefile that handles every module in the src/ directory and creates the objects and dependency files in the obj/ directory.
Here are the files:
main.cpp
#include <iostream>
#include <cod/types.hpp>
int main() {
int s;
std::cin >> s;
return lol();
}
types.hpp
#ifndef TYPES_HPP_INCLUDED
#define TYPES_HPP_INCLUDED
int lol();
#endif // TYPES_HPP_INCLUDED
types.cpp
#include <iostream>
#include <cod/types.hpp>
int lol() {
std::cout << "lol";
return 0;
}
Makefile
APP_NAME = CoDstructor
DEBUG_TARGET = debug
RELEASE_TARGET = release
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
INC_DIR = src
INCLUDE_DIRS +=
LIBRARY_DIRS +=
CXXFLAGS += -Wall
CXXFLAGS += -Werror
CXXFLAGS += -Wextra
CXXFLAGS += -pedantic
CXXFLAGS += -std=c++11
$(DEBUG_TARGET): CXXFLAGS += -g
$(RELEASE_TARGET): CXXFLAGS += -O3
LDFLAGS += -static
LDFLAGS += -static-libstdc++
LDFLAGS += -static-libgcc
$(DEBUG_TARGET): LDFLAGS += -g
$(RELEASE_TARGET): LDFLAGS +=
CPPMACROS =
$(DEBUG_TARGET): CPPMACROS += DEBUG
$(RELEASE_TARGET): CPPMACROS += NDEBUG
CXXFLAGS += $(foreach i,$(INC_DIR),$(addprefix -I,$(i)))
CXXFLAGS += $(foreach i,$(INCLUDE_DIRS),$(addprefix -I,$(i)))
CXXFLAGS += $(foreach i,$(CPPMACROS),$(addprefix -D,$(i)))
LIBS = $(foreach i,$(LIBRARY_DIRS),$(addprefix -L,$(i)))
SOURCES = $(subst ./,,$(shell find . -name *.cpp))
OBJS = $(subst $(SRC_DIR),$(OBJ_DIR),$(SOURCES:.cpp=.o))
DEPS = $(OBJS:.o=.d)
all: $(DEBUG_TARGET) clean
$(RELEASE_TARGET): $(BIN_DIR)/$(APP_NAME) clean
#echo Building release...
$(DEBUG_TARGET): $(BIN_DIR)/$(APP_NAME) clean
#echo Building debug...
$(OBJS): $(SOURCES)
#mkdir -p $(#D)
$(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $#
$(BIN_DIR)/$(APP_NAME): $(OBJS)
$(CXX) $(LDFLAGS) $^ -o $# $(LIBS)
#echo $^
.PHONY: clean
clean:
#echo clean
# #-rmdir $(OBJ_DIR)
-include $(DEPS)
And here is the error:
obj/cod/types.o: In function `main':
D:\PROJECTS\CoDstructor/src/cod/main.cpp:4: multiple definition of `main'
obj/cod/main.o:D:\PROJECTS\CoDstructor/src/cod/main.cpp:4: first defined here
obj/cod/main.o:main.cpp:(.text+0x2a): undefined reference to `lol()'
obj/cod/types.o:main.cpp:(.text+0x2a): undefined reference to `lol()'
collect2.exe: error: ld returned 1 exit status
I searched for almost two days now how to resolve these errors.
For the first one (multiple definitions of main):
I already checked the OBJS variable, and it contains and links only one time the main.o. Also why says it obj/cod/types.o in the first line? There is no main in types.hpp/types.cpp.
The second error (undefined reference to lol()):
Why is the reference undefined, but gives no compiler error instead?
The third error (it rebuilds everything everytime, instead of only changed ones or instead of looking up the .d dependency files)
I am running the latest MinGW32 build (g++ 4.9.1) and latest MSYS (make).
What am I doing wrong here?
Your $(OBJS): $(SOURCES) rule is not what you think it is. As a result, you're building both main.o and types.o from the same main.cpp file ($< param in command line). Hence you have two identical files which are conflicting, while types.cpp wasn't even built.
Correct rule would be $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
Your key problem is here:
$(OBJS): $(SOURCES)
#mkdir -p $(#D)
$(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $#
This rule has two problems.
You have multiple object files. The first action won't work when you go beyond having just one subdirectory under your src directory.
The second action compiles your src/cod/main.cpp twice, once into obj/cod/main.o and then into obj/cod/types.o. That's because $< is the first item in the dependency list, and that first item is src/cod/main.cpp.
The second problem is easier to fix than the first. You need a pattern rule:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $#
Now to address the first problem. What if you have multiple source directories, each a subdirectory of your src directory? You want to make a corresponding obj subdirectory for each one of those. Also note that there's a directory you aren't making, the bin directory. The first thing to do is build a list of the directories you need to make.
MKDIRS = $(sort $(foreach i,$(OBJS),$(dir $i)))
MKDIRS += bin
Then you need a rule to make them. Let's start simply:
mkdirs:
mkdir -p $(MKDIRS)
This however is problematic. You'll get error messages from mkdir and the build will stop if any one of those directories already exists. We need to make those directories only if they don't exist. Make does provide the tools to filter that list down to only the directories that don't exist, but I'd rather not do that. To me it's better to use the shell to make some decisions:
mkdirs:
#sh -c \
'for d in $(MKDIRS); do \
if [ ! -d $$d ]; then echo mkdir -p $$d; mkdir -p $$d; fi \
done'
Now we need to add that rule as a dependency.
$(RELEASE_TARGET): mkdirs $(BIN_DIR)/$(APP_NAME)
#echo Release built
$(DEBUG_TARGET): mkdirs $(BIN_DIR)/$(APP_NAME)
#echo Debug built
Note that I've done three things to those targets.
I added the mkdirs target.
I deleted the clean target. You really don't want to do that here. It defeats the purpose of separate compilations. When you have hundreds of source files and make a change in one of them, you just want to recompile that one source file and then rebuild the executable from the hundreds of object files that already exist. Don't do a clean right after you built the executable! You can always do a make clean from the command line if you feel compelled to do so.
I changed the messages. Those messages will be issued after the dependencies have been satisfied. They'll be the last thing you see. To get messages before action starts it's easiest to build a phony target that prints the desired message.
A couple of final notes:
Note that mkdirs is a phony target (and so is all). It's best to add it to your .PHONY list, and that list is best placed up-front.
Finally, the target $(DEBUG_TARGET): mkdirs $(BIN_DIR)/$(APP_NAME) is a ticking time bomb. The same goes for $(RELEASE_TARGET). One day you'll find out about parallel make. There's no guarantee that the directories will be made before the compiler tries to compile the code. The directories won't exist, and kaboom, your make just failed. Making your makefile robust against parallel execution is a matter of a different stackexchange question.
From memory, did not check the docs, problem is this:
$(OBJS): $(SOURCES)
#mkdir -p $(#D)
$(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $#
That means that every object file depends on all sources. And then the compile command uses $< which I think means the first dependency. So in effect, you compile both types.o and main.o from main.cpp (which is first of the $(SOURCES), I suppose).
One solution would be to use a pattern rule, something like:
%.o : %.c
#mkdir -p $(#D)
$(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $#
Object files are already requied by your rule $(BIN_DIR)/$(APP_NAME): $(OBJS), and this pattern rule will tell make how to generate them.
Related
I have a simple project, whose folder structure is something like:
ls -R
.:
build include makefile src
./build:
./include:
myfunc.h
./src:
main.cpp myfunc.cpp
I want to compile the .cpp sources into .o object files, which should end into ./build folder. Using the GNUmake documentation and other sources (e.g. Proper method for wildcard targets in GNU Make), I wrote this makefile:
CXX := g++
CXXFLAGS += -I./include
CXXFLAGS += -Wall
OBJDIR := ./build
SRCDIR := ./src
PROGRAM = release
DEPS = myfunc.h
SRC = $(wildcard $(SRCDIR)/*.cpp)
OBJ = $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(SRC))
all: $(PROGRAM)
$(PROGRAM): $(OBJ)
$(CXX) $(CXXFLAGS) -o $(PROGRAM) $(OBJ)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
$(CXX) $(CXXFLAGS) -c $< -o $#
.PHONY: clean
clean:
rm $(PROGRAM) $(OBJ)
But I get the error message: make: *** No rule to make target 'build/main.o', needed by 'release'. Stop.. I tried a lot of different ways but I cannot manage to have my .o files end up in the ./build directory. Instead, everything works if I put them in the root directory of the project. I can also make it work by specifying a rule for each object file, but I'd like to avoid that. What am I missing?
(I am using GNUmake version 4.3)
The problem is here:
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
$(CXX) $(CXXFLAGS) -c $< -o $#
See the $(DEPS)? That expands to myfunc.h. The compiler knows where to find that file (or would if this recipe were executed), because you've given it -I./include, but Make doesn't know where to find it (so it passes over this rule).
Add this line:
vpath %.h include
P.S. If you want to be really clean, you can add a variable:
INCDIR := ./include
CXXFLAGS += -I$(INCDIR)
vpath %.h $(INCDIR)
I am quite new to Make. I am attempting to write a Makefile to build a medium-sized Linux C/C++ application as below.
Making a simple Makefile by having all source files in one location and explicitly listing the source files works ok for me but I would like it to be more generic.
I have all my source files (C and C++) in the src folder in different subdirectories. I have header files inside an inc and inc/common folder, and then libs inside a lib folder.
The Makefile is run on the same level :
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin
CXX := /bin/arm-linux-gnueabi-g++
EXE := $(BIN_DIR)/runfile
SRC := $(shell find $(SRC_DIR) -name *.cpp -or -name *.c)
OBJ := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(addsuffix .o,$(basename $(SRC))))
CPPFLAGS := -Iinc -Iinc/common -MMD -MP
CXXFLAGS := -std=c++11 -Wall
LDFLAGS := -Llib
LDLIBS :=
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ) | $(BIN_DIR)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJ_DIR)/%.o: $(SRC) | $(OBJ_DIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
-include $(OBJ:.o=.d)
I get lots of errors such as below when I run it, including problems opening dependency files. I think i'm almost there, but can't see my error exactly :
compilation terminated.
/bin/arm-linux-gnueabi-g++ -Iinc -Iinc/common -MMD -MP -std=c++11 -Wall -c -o obj/main.d.o
cc -Llib obj/main.d.o -o obj/main.d
/usr/bin/ld: obj/main.d.o: relocations in generic ELF (EM: 40)
/usr/bin/ld: obj/main.d.o: relocations in generic ELF (EM: 40)
/usr/bin/ld: obj/main.d.o: error adding symbols: file in wrong format
I don't see how the output you show can be generated from the makefile you show here but anyway.
This is not right:
$(OBJ_DIR)/%.o: $(SRC) | $(OBJ_DIR)
A pattern rule is a template that tells make "if you want to build a target that matches this pattern, then you can build it from the prerequisites that match this pattern".
Here you are listing ALL your source files as a prerequisite for EVERY object file. Suppose SRC is set to foo.c bar.c biz.c baz.c, then this expands to:
obj/%.o : foo.c bar.c biz.c baz.c | obj
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $#
You're telling make that every single .o target depends on ALL the source files, not just the one for that object file. Further, the automatic variable $< always expands to the first prerequisite, which here will always be foo.c. So, you're compiling foo.c four times, creating each of the object files.
The very first important rule when debugging makefiles is to look carefully at the output (command lines) that make prints. If they are not right, then your makefile is not right. If you do that you'll see all the compile lines are compiling the same source, like:
g++ -c foo.c -o obj/foo.o
g++ -c foo.c -o obj/bar.o
g++ -c foo.c -o obj/biz.o
g++ -c foo.c -o obj/baz.o
That clearly cannot work and it's why you get link errors trying to link together all these object files: they all have the same content.
You need this:
$(OBJ_DIR)/%.o : $(SRC_DIR)/%.cpp
#mkdir -p $(#D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $#
which tells make how to build an object file from a single source file.
You also need to create the actual output directory that the object file will go into. Just creating $(OBJ_DIR) is not enough, if the object file appears in a subdirectory.
I have read other questions asking similar things, alas I am still confused.
This is my current Makefile:
CC = g++
EXEFILE = template
IFLAGS= -I/usr/include/freetype2 -I../Camera
LFLAGS= -L/usr/lib/nvidia-375 -L/usr/local/lib -L/usr/include/GL -L/usr/local/include/freetype2 -L/usr/local/lib/
LIBS = -lglfw -lGL -lGLU -lOpenGL -lGLEW -pthread -lfreetype
SRC=*.cpp
DEPS=*.h
$(EXEFILE):
$(CC) -std=c++11 -o $(EXEFILE) -Wall -Wno-comment $(SRC) $(IFLAGS) $(LFLAGS) $(LIBS)
all: run clean
run: $(EXEFILE)
./$(EXEFILE)
clean:
rm $(EXEFILE)
Right now all of my .h files and .cpp files are on the working directory, and everything compiles and runs just fine. My issue is that I have already a large number of files, and it is getting quite messy. I want to create multiple directories (and maybe even directories inside these directories) to organize my files. But as soon as I move a header file and it's corresponding cpp file(s) to a directory inside of the current directory the compiler doesn't know how to link them anymore.
How do I tell my make file to compile and link everything under the current root?
Alternatively, is there a ELI5 guide to makefile syntax?
The quickest way to solve your problem is to add SRC and DEPS the files contains in all your sub-directories, something like:
SRC=*.cpp src/*.cpp
DEPS=*.h inc/*.h
Now you may consider writing a rule to first compile every file in a separate directory:
# Group compilation option
CXXFLAGS := -std=c++11 -Wall -Wno-comment $(IFLAGS)
# A directory to store object files (.o)
ODIR := ./objects
# Read this ugly line from the end:
# - grep all the .cpp files in SRC with wildcard
# - add the prefix $(ODIR) to all the file names with addprefix
# - replace .cpp in .o with patsubst
OBJS := $(patsubst %.cpp,%.o,$(addprefix $(ODIR)/,$(wildcard $(SRC)/*.cpp)))
# Compile all the files in object files
# $# refers to the rule name, here $(ODIR)/the_current_file.o
# $< refers to first prerequisite, here $(SRC)/the_current_file.cpp
$(ODIR)/%.o:$(SRC)/%.cpp $(DEPS)/%.h
$(CXX) $(CXXFLAGS) -c -o $# $<
# Finally link everything in the executable
# $^ refers to ALL the prerequisites
$(EXEFILE): $(OBJS)
$(CXX) $(CXXFLAGS) -o $# $^ $(LFLAGS) $(LIBS)
I found a solution, that, to my tastes seems elegant, or at least easy to trace using the wildcard operator. Here is my current makefile:
EXEFILE := $(shell basename $(CURDIR))
DIRECTORIES = $(filter-out ./ ./.%, $(shell find ./ -maxdepth 10 -type d))
IFLAGS= -I/usr/include/freetype2
LOCAL_I_DIRS =$(addprefix -I./, $(DIRECTORIES))
LFLAGS= -L/usr/lib/nvidia-375 -L/usr/local/lib -L/usr/include/GL -L/usr/local/include/freetype2 -L/usr/local/lib/
LIBS = -lglfw -lGL -lGLU -lOpenGL -lGLEW -pthread -lfreetype
SRC := $(wildcard *.cpp) $(wildcard **/*.cpp)
$(EXEFILE): $(EXEFILE).cpp
g++ -std=c++11 -o $(EXEFILE) -Wall -Wno-comment $(SRC) $(IFLAGS) $(LOCAL_I_DIRS) $(LFLAGS) $(LIBS)
all: run clean
run: $(EXEFILE)
./$(EXEFILE)
clean:
rm $(EXEFILE)
print-%: ; #echo $* = $($*)
So I get all directories up to depth 10. I then take out the current root (./) and any hidden directory (./.) leaving me with standard subdirectories stored under "DIRECTORIES", I then add -I to every directory to make it an include directory and store them in LOCAL_I_DIRS
So I can now create as many subdirectories as needed (up to 10 levels) and the compiler will be happy.
My Makefile:
compiler=g++
cflags=-g -Wall -I.
src=$(shell find . -name *.cc) #find all .cc files, with path name
srcBaseName=$(shell basename -a $(src)) # extract base names by stripping off the path
header=$(shell find . -name *.h) # all header files
obj=$(patsubst %.cc, %.o, $(srcBaseName)) # Problematic line
bin=bin/myProgram
all: $(bin)
$(bin): $(obj)
$(compiler) $^ -o $#
%.o: %.cc
$(compiler) $(cflags) -c $^ -o $#
clean:
rm $(obj) $(bin)
results in the following error:
make: *** No rule to make target 'SomeObjectFile.o', needed by
'bin/myProgram'. Stop.
The problematic line:
obj=$(patsubst %.cc, %.o, $(srcBaseName))
If I change $(srcBaseName) to $(src), then everything builds fine. But in that case the .o files are going to be scattered in the corresponding folders with .cc files, which I don't want.
I'd like to have a dedicated (obj/) folder to store all the .o files.
How can I do it?
First try:
obj=$(patsubst %.cc, %.o, obj/$(srcBaseName))
Second try:
obj=$(patsubst %.cc, %.o, obj\/$(srcBaseName))
Why did they NOT work?
/**********************Edited on 16th Feb 2015**********************/
Based on the suggestions in the answers, I updated my Makefile to be this:
compiler=g++
# source compilation flags
cflag=-g -Wall -std=gnu++0x -I./header/
# source link flags
lflag=
# lib used by proj
lib=
tflag=-g -Wall -std=gnu++0x
# test link flags
tlflag=
# test libs
testLib=lib/libgtest.a
# source code
src=$(shell find "./src" -name "*.cc")
srcBaseName=$(shell basename -a $(src))
obj=$(addprefix obj/, $(patsubst %.cc, %.o, $(srcBaseName)))
vpath %.cc $(dir $(src))
# header files
header=$(shell find "./header" -name "*.h")
# test files
testSrc=$(shell find "./test" -name "*.cc")
testSrcBase=$(shell basename -a $(testSrc))
testObj=$(addprefix obj/, $(patsubst %.cc, %.o, $(testSrcBase)))
vpath %.cc $(dir $(testSrc))
# binary files
bin=bin/Driver
testBin=bin/Test
all: prog test
prog: $(bin)
$(bin): $(obj)
$(compiler) $(lflag) $^ $(lib) -o $#
#$(obj): $(src) $(header)
obj/%.o: %.cc $(header)
$(compiler) $(cflag) -c $< -o $#
test: $(testBin)
$(testBin): $(testObj)
$(compiler) $(tlflag) $^ $(testLib) -o $#
obj/%.o: %.cc
$(compiler) $(tflag) -c $< -o $#
clean:
rm $(obj) $(bin) $(testObj) $(testBin)
Here's the intention behind the make:
make prog:
make should find all the source files(.cc) under ./src directory, and produce an .o file with the same file name in the ./obj directory, insensitive to the levels of subdirectories so that I can freely add new cc files without the need to update the Makefile. Each .o file depends on the corresponding(just the one with the same name, not all) .cc file and ALL headers(make does not automatically know what header files a cc file includes without parsing the file; if you have a clever method to achieve this, do let me know!). For example, ./src/subdirectory1/sample1.cc should produce ./obj/sample1.o and ./obj/sample1.o depends on ./src/subdirectory1/sample1.cc + ./header/sample1.h + ./header/sample2.h + ...
make test:
it should do similar things to the test source files in the ./test folder, with the exception that there's no header involved. In case this detail helps, I'm using Google Test.
However, my Makefile is not quite working in the intended way because it has the following problems:
1, if I run make test, the recipe $(compiler) $(tflag) -c $< -o $# is not executed(tflag means 'test compilation flag', which doesn't have the -I./header/ part; cflag means 'source code compilation flag', it has the -I./header/ part). Instead, the recipe in phony prog $(compiler) $(cflag) -c $< -o $# is executed. This observation comes from the output in which '-I./header/' shows up. I guess this is because the cflag pattern rule in phony prog overrides the tflag one in phony test? I vaguely remember make picks the best matching pattern rule - the two are essentially the same(I have the intention that the pattern rule under a specific phony should get executed when that phony is executed, which doesn't seem to be feasible?), so make will always pick the first one. This conclusion is verified by swapping the order of two pattern rules in the Makefile, and this time the tflag rule always gets picked. So a natural question to ask is, how do I execute the pattern rule under a specific phony when that phone is executed?
2, Assuming it's not feasible to do what I wanted to do in point 1, I start to think about alternatives. Can I do something like: #$(obj): $(src) $(header) so that I can get rid of the pattern rule to work around the way make picks the pattern rule. However, this is obviously not correct as it's saying, each .o file in $(obj) depends on ALL src files and ALL header files. Is it at all a right direction?
Thanks a lot, looking forward to hearing from you.
3 key questions have been highlighted in bold and italicized.
Your problem is this line:
%.o: %.cc
That line tells make that to create some/path/file.o you will use some/path/file.cc.
If you want all .o files in one single directory, but still want to have the source files in different directories you will need one such rule for each source directory. Or, you could add all directories to the VPATH variable, something like:
VPATH=$(dir $(src))
Or maybe better:
VPATH=$(dir $(SRC))
Usuing capital letters for your variables in a Makefile is a good way to avoid confusing them with function names.
I will do my best to also answer your new questions:
1) As you are using the gnu compilers it is possible to automatic find the dependencies of .h files in .c files. You could add rules like this to your Makefile:
# Where .d-files will be created for dependencies
DEP=dep
# Dependency files
DEPS = $(srcBaseName:%.cc=$(DEP)/%.d)
# Before compiling object file, also make sure dependency file is
# created to test the need for future recompilation
obj/%.o: %.cc $(DEP)/%.d
$(compiler) $(cflags) -c $< -o $#
# Use gnu compiler to create dependency files
$(DEPS): $(DEP)/%.d: %.cc $(filter-out $(wildcard $(DEP)), $(DEP))
$(compiler) -MM $(cflags) -MT $# $< > $#
# Create directories which might be needed
$(DEP) $(OBJ) $(BIN) $(MO):
mkdir -p $#
# Let Makefile use any generated dependency files
ifneq ($(wildcard $(DEPS)),)
include $(wildcard $(DEPS))
endif
Please note that in the compilation rule I replaced $^ with $< as we don't want to compile the dependency file.
2) I would avoid two pattern rules looking the same. Instead I would change cflag depending on the target, something like this:
ifeq ($(MAKECMDGOALS),debug)
CFLAGS += -g
else
CFLAGS += -O2
endif
I hope these answers will guide you in the right direction
The main problem is that make cannot use the pattern rule
%.o: %.cc
$(compiler) $(cflags) -c $^ -o $#
to build obj since it cannot find common stems between your .o files and your .cc files to match %. An easy way to fix this is to tell make where those files are through the vpath directive, e.g. by adding
vpath %.cc $(dir $(src))
vpath %.o obj/ #not a good idea for .o files though, see below
and changing the pattern rule (needed by the use of vpath %.o) into
%.o: %.cc
$(compiler) $(cflags) -c $^ -o obj/$#
EDIT: MadScientist has made a very good point that I completely missed, based on which, a better solution that does not involve vpath %.o is
vpath %.cc $(dir $(src))
obj=$(addprefix obj/,$(patsubst %.cc, %.o, $(srcBaseName)))
obj/%.o: %.cc
$(compiler) $(cflags) -c $^ -o $#
I'm currently trying to build a proper Makefile.
What I want is full control of what's happening, so I don't want any third party software.
My current attempt seems logic to me, but since the dependency generation is not valid, I'm kind of stuck.
For better readabilty, the full Makefile is broken into little pieces. I would appreciate any comment on any section if there's something to improve.
First of all, I have the following static definitions
CXX = g++
CXXFLAGS = -Wall \
-Wextra \
-Wuninitialized \
-Wmissing-declarations \
-pedantic \
-O3 \
-p -g -pg
LDFLAGS = -p -g -pg
DEPFLAGS = -MM
Afaik this should be fine. It would be perfect to make the profiling flags optional but that's not important.
SRC_DIR = ./src
OBJ_DIR = ./obj
SRC_EXT = .cpp
OBJ_EXT = .o
TARGET = ./bin/my_target
SRCS = $(wildcard $(SRC_DIR)/*$(SRC_EXT))
OBJS = $(subst $(SRC_DIR), $(OBJ_DIR), $(SRCS:$(SRC_EXT)=$(OBJ_EXT)))
DEP = depend.main
Basically, this should just extract all the *.cpp files out of the subfolder src and additionally replace ./src with ./obj and .cpp with .o as names of the objects.
.PHONY: clean all depend
all: $(TARGET)
$(TARGET): $(OBJS)
#echo "-> linking $#"
#$(CXX) $^ $(LDFLAGS) -o $#
$(OBJ_DIR)/%.$(EXT_OBJ):
#echo "-> compiling $#"
#$(CXX) $(CXXFLAGS) -c $< -o $#
Afaik, this block - provided a valid dependency file exists - should do all necessary compiling and linking.
clean:
#echo "removing objects and main file"
#rm -f $(OBJS) $(TARGET)
Should be self-explanatory and correct, or am I missing something here?
$(SRC_DIR)/%.$(SRC_EXT):
$(CXX) $(DEPFLAGS) -MT \
"$(subst $(SRC_DIR),$(OBJ_DIR),$(subst $(SRC_EXT),$(OBJ_EXT),$#))" \
$(addprefix ,$#) >> $(DEP);
clear_dependencies:
#echo "-> (re-)building dependencies";
#$(RM) $(DEP)
depend: clear_dependencies $(SRCS)
This is the non-functional part. What I intend to do is using the g++ Compiler flag -MM to auto-create dependencies and using -MT to use a different path than the default one.
The resulting dependency should look like
./obj/main.o: ./src/main.cpp ./src/some_header_file.h
Unfortunately, this will never be called and I lack the knowledge why this is the case.
In a similar question, user Beta gladly provided a temporary solution by adding a .Phony but this has the side effect on rebuilding every object without any change.
Finally, there is just the one line
-include $(DEP)
to include the dependency file, once created.
Any answer providing some hints about any part are very welcome.
So my question is: What can I do better or maybe "cleaner" and why doesn't the dependency generation work?
Here goes.
Assign simply expanded variables where possible:
SRCS := $(wildcard $(SRC_DIR)/*$(SRC_EXT))
From GNU Make manual:
Another disadvantage [of recursively expanded variables] is that any functions referenced in the definition will be executed every time the variable is expanded. This makes make run slower; worse, it causes the wildcard and shell functions to give unpredictable results because you cannot easily control when they are called, or even how many times.
Use substitution references or patsubst function to convert sources into objects:
OBJS := $(SRCS:$(SRC_DIR)/%$(SRC_EXT)=$(OBJ_DIR)/%$(OBJ_EXT))
Specify proper prerequisites in compilation pattern rule. This is mandatory to get Make keeping your object files up to date and updating them on source changes.
$(OBJ_DIR)/%$(OBJ_EXT) : $(SRC_DIR)/%$(SRC_EXT)
#echo "-> compiling $#"
#$(CXX) $(CXXFLAGS) -o $# -c $<
Compile sources and generate dependency files for them at the same time. Use -MMD -MP flags to get things work (just append them to CXXFLAGS).
CXXFLAGS += -MMD -MP
-include $(OBJS:$(OBJ_EXT)=.d)
From GCC manual:
-MD
-MD is equivalent to -M -MF file, except that -E is not implied. The driver determines file based on whether an -o option is given. If it is, the driver uses its argument but with a suffix of .d, otherwise it takes the name of the input file, removes any directory components and suffix, and applies a .d suffix.
-MMD
Like -MD except mention only user header files, not system header files.
-MP
This option instructs CPP to add a phony target for each dependency other than the main file, causing each to depend on nothing. These dummy rules work around errors make gives if you remove header files without updating the Makefile to match.
Also consider studying this article of Paul Smith (he is a maintainer of GNU Make). It gives a rather good overview of different autodep-generation approaches.