How to automatically create a dependency between files?
%.d: %.c
@set -e; rm -f $@; \
$(CC) -MM $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
sinclude $(SOURCES:.c=.d)
above, I saw someone's blog post, but it doesn't explain how the code for creating a dependency between .c and .h files in a makefile works.
Is there anyone who can explain this to me or provide some material?
I would really appreciate your help! Thank you!
source to share
Suppose your only new source file foo.c
that contains the lines
#include "foo.h"
#include "bar.h"
Make detects that it is foo.d
deprecated (because it doesn't exist or foo.c is newer), so it executes the rule.
%.d: %.c
@set -e; rm -f $@; \
$(CC) -MM $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
sinclude $(SOURCES:.c=.d)
Make an estimate $$$$
as $$
well as transfer it to the shell; the shell interprets this as the value of a parameter $
, which is the process ID of the shell that the rule wants to use to construct a unique filename. This will only remain constant within one command, so the rule is written with line continuation ("\"). This is not a good way to do it; if there are different processes trying to spawn foo.d
at the same time, you will probably be closed anyway. Therefore, we can rewrite the rule:
%.d: %.c
@set -e; rm -f $@
$(CC) -MM $< > $@.temp
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.temp > $@
rm -f $@.temp
sinclude $(SOURCES:.c=.d)
We can abandon the first rule, it doesn't really matter.
The second command $(CC) -MM $< > $@.temp
expands to gcc -MM foo.c > foo.d.temp
(or another compiler). The flag -MM
tells the compiler to create a list of dependencies:
foo.o: foo.c foo.h bar.h
The next line is chewing this list with sed
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g'
Which translates roughly as "change foo.o:
to foo.o foo.d :
":
foo.o foo.d : foo.c foo.h bar.h
(And the last command deletes the temporary file.)
This is not the best way to handle dependencies, as it will rebuild all files %.d
every time you run Make, even those that have no purpose for you. A more polished approach is Advanced Auto-Dependency Creation .
source to share
-M
Instead of outputting the result of preprocessing, output a rule suitable for make describing the dependencies of the main source file. ...
-MM
Like -M but do not mention header files that are found in system header directories, ...
So the command is here:
$(CC) -MM $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
Creates a temporary ($ @. $$$$, i.e. target filename appended with a unique number) output file gcc -MM
and using sed files to be included in the makefile, the dependency file will look like target file: [gcc generated dependencies]
. Then it removes the original output gcc -MM
.
source to share
The actual tracking #include
is done by the preprocessor (see and options ). -M
-MM
-M
Instead of outputting the result of preprocessing, output a rule suitable for
make
describing the dependencies of the main source file. The preprocessor outputs a single rulemake
containing the name of the object file for that source file, a colon, and the names of all included files, including those included in the command line parameters-include
or-imacros
.
-MM
Likewise
-M
, but do not mention header files that are found in system header directories, or header files that are directly or indirectly included from such a header.
Also a good overview of possible ways to track these types of dependencies is available here .
source to share
For all gobbledegooks with four dollar signs per line, etc. the idea is pretty simple:
- To create a file
xyz.d
from a filexyz.c
, run the commands displayed ... - The line
$(CC)
should probably include $ (CFLAGS), but assumes the compiler is GCC (the option is-MM
not standard elsewhere). The flag-MM
asks the compiler to generate output that lists the headers (or other files) included in the source so that you get the correct dependency information for the current platform. - The compiler compiles the source and writes the dependencies to standard output, which is redirected to a temporary file. The extension
$@.$$$$
in the makefile (for example),xyz.$$
in the shell, which$$
becomes the PID of the shell that the script is running on. - The command
sed
outputs the output from the compiler such that the line starting withxyz.o
starts with(so the object file and the program created from the object file depend on the files after the colon), so both the object file and the dependency file need to be updated if any from source or header files will change.xyz.o xyz
xyz.o xyz.d
- The output is written to
xyz.d
. - Cleaning up and down.
- The line
sinclude
reads as many files as possible.d
.
[Edited to correct the inconsistency indicated by Beta .]
source to share