For years I’ve known that some compilers can generate dependency files — text files that can be included in a Makefile to declare dependencies — but I’ve only lately cracked the nut on how to do this. My attempts at Googling the solution to this did not turn up a simple example, so I decided to write this note on the subject.
First off, here’s the goal: after the initial build of a project, if a header file in the project is subsequently changed, then re-executing make should rebuild each (but only!) object file that depends on the header.
(Rebuilding from scratch is a waste of time, but imagine the potential frustration if dependent files are not rebuilt.)
The manual approach would be to add dependency lines with no recipes for every header in a source file:
foo.o: includes/foo.h includes/bar.h objs/%.o: src/%.cpp g++ -c -Iincludes -o"$@" "$<"
Clearly (rignt?), the above will not scale, especially since it depends on the developer updating the build script every time he/she adds or deletes an “#include” directive. But g++
and no doubt other compilers support the optional to generate snippets of makefile with the correct and complete dependency declarations. Here’s how:
objs/%.o: src/%.cpp g++ -c -Iinlcudes -MMD -MF"$(@:objs/%.o=deps/%.d)" -o"$@" "$<"
The -MMD
option instructs g++
to create dependency files, but not for system headers. (If the system header files have changed, I recommend rebuilding your project from scratch.) The -MF
option specifies the name of the dependency file. In the example above, compiling source file src/foo.cpp
would generate objs/foo.o
and deps/foo.d
.
A dependency file is a text file of legal makefile syntax. For example, if foo.cpp
included foo.h
and bar.h
, then foo.d
would be
objs/foo.o: src/foo.cpp includes/foo.h includes/bar.h
To make use of the generated dependency files, assuming they are deposited in directory deps
, add the following line to your Makefile:
-include deps/*
The leading dash instructs make
to simply ignore an include file that does not exist or cannot be remade.
Finally, I like to have the clean
target remove the *.d
files along with the *.o
files and executable. And if you are using git, you will probably want to add line *.d
to .gitignore
A simple makefile that illustrates the use of generated dependencies can be found here. It is part of a project that was built on Linux Mint 17.2 Rafaela, using g++ 4.9.3 and GNU Make 3.81.
Thanks for the tip Bob!
How about a slight simplification: use a single “build” dir/tree for both .o’s and .d’s instead of objs/ and deps/ (just a single build artifacts dir/tree to remember to clean / be distracted by), then a little change:
-include $(shell find ./build -name *.d)
Anyway, nice post – finally got me running auto dependencies now, after being lazy for so long 😉
LikeLiked by 1 person
Good point, Ash. I was being sorta anal about separating the *.d and *.o, but the linker and compiler don’t care. 😀
LikeLike