Makefile (Auto-Dependency Generation)
Excellent answers but in my build I put the .obj files in a subdirectory based on build type (ie: debug vs. release). So for example, if I'm building debug, I put all the object files in a build/debug folder. It was a mind-numbing task to try to get the multiline sed command above to use the correct destination folder, but after some experimentation, I stumbled on a solution that works great for my build. Hopefully it'll help someone else as well.
Here's a snippet:
# List my sources
CPP_SOURCES := foo.cpp bar.cpp
# If I'm debugging, change my output location
ifeq (1,$(DEBUG))
OBJ_DIR:=./obj/debug
CXXFLAGS+= -g -DDEBUG -O0 -std=c++0x
else
CXXFLAGS+= -s -O2
OBJ_DIR:=./obj/release
endif
# destination path macro we'll use below
df = $(OBJ_DIR)/$(*F)
# create a list of auto dependencies
AUTODEPS:= $(patsubst %.cpp,$(OBJ_DIR)/%.d,$(CPP_SOURCES))
# include by auto dependencies
-include $(AUTODEPS)
.... other rules
# and last but not least my generic compiler rule
$(OBJ_DIR)/%.o: %.cpp
@# Build the dependency file
@$(CXX) -MM -MP -MT $(df).o -MT $(df).d $(CXXFLAGS) $< > $(df).d
@# Compile the object file
@echo " C++ : " $< " => " $@
@$(CXX) -c $< $(CXXFLAGS) -o $@
Now for the details: The first execution of CXX in my generic build rule is the interesting one. Note that I'm not using any "sed" commands. Newer versions of gcc do everything I needed (I'm using gcc 4.7.2).
-MM builds the main dependency rule including project headers but not system headers. If I left it like this, my .obj file would NOT have the correct path. So I use the -MT option to specify the "real" path to my .obj destination. (using the "df" macro I created).
I also use a second -MT option to make sure the resulting dependency file (ie: .d file) has the correct path, and that it is included in the target list and therefor has the same dependencies as the source file.
Last but not least is the inclusion of the -MP option. This tell gcc to also make stubbed rules for each header solving the problem that occurs if I delete a header causing make to generate an error.
I suspect that since I'm using gcc for all the dependency generation instead of piping out to sed, my build is faster (although I've yet to prove that since my build is relatively small at this point). If you see ways I can improve upon this, I'm always open to suggestions. Enjoy
To manipulate the filenames when you already know what the dependencies should be, you can use a pattern rule:
file.o: %.o : %.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
$(COMPILE)
And you can reuse the rule for other targets:
# Note these two rules without recipes:
file.o: 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
anotherFile.o: 4.h 9.h yetAnother.h
file.o anotherFile.o: %.o : %.cpp
$(COMPILE)
But if you want Make to figure out the list of dependencies automatically, the best way (that I know of) is Advanced Auto-Dependency Generation. It looks like this:
%.o : %.cc
@g++ -MD -c -o $@ $<
@cp $*.d $*.P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
rm -f $*.d
-include *.P
Basically, when it builds file.o
, it also builds file.d
. Then it runs file.d
through a bewildering sed command that turns the list of dependencies into a rule with no recipes. The last line is an instruction to include
any such rules that exist. The logic here is subtle and ingenious: you don't actually need the dependencies the first time you build foo.o
, because Make already knows that foo.o
must be built, because it doesn't exist. The next time you run Make, it will use the dependency list it created last time. If you change one of the files so that there is actually a new dependency which is not in the list, Make will still rebuild foo.o
because you changed a file which was a dependency. Try it, it really works!
For the record, this is how I generate dependencies automatically now:
CPPFLAGS = -std=c++1y -MD -MP
SRC = $(wildcard *.cpp)
all: main
main: $(SRC:%.cpp=%.o)
g++ $(CPPFLAGS) -o $@ $^
-include $(SRC:%.cpp=%.d)
The compiler flags -MD and -MP help do the trick.
Newer versions of GCC have an -MP option which can be used with -MD. I simply added -MP and -MD to the CPPFLAGS variable for my project (I did not write a custom recipe for compiling C++) and added an "-include $(SRC:.cpp=.d)" line.
Using -MD and -MP gives a dependency file which includes both the dependencies (without having to use some weird sed) and dummy targets (so that deleting header files will not cause errors).