%.d: %.c @set -e; rm -f $@; \ $(CC) -MM $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ sinclude $(SOURCES:.c=.d)
以上是我在别人的博客上看到的,但是并没有解释代码如何在makefile中创build.c和.h文件之间的依赖关系。
有没有人会为我解释或提供一些材料?
我会很感激你的帮助!谢谢!
假设你唯一的新源文件是foo.c
,它包含行
#include "foo.h" #include "bar.h"
Make确定foo.d
是过时的(因为它不存在或者foo.c更新),所以它执行规则。
%.d: %.c @set -e; rm -f $@; \ $(CC) -MM $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ sinclude $(SOURCES:.c=.d)
将$$
$$$$
评估为$$
,并将其传递给shell; shell将其解释为$
参数的值,该参数是shell的进程ID,规则要使用该参数来构造唯一的文件名。 这将只在一个命令内保持不变,这就是为什么规则是用行连续(“\”)写的。 这不是一个很好的办法, 如果有不同的进程在同一时间尝试构建foo.d
,那么无论如何你可能会被彻底打败。 所以我们可以重写这个规则:
%.d: %.c @set -e; rm -f $@ $(CC) -MM $< > $@.temp sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.temp > $@ rm -f $@.temp sinclude $(SOURCES:.c=.d)
我们可以放弃第一条规则,这并不是真正的问题。
第二个命令$(CC) -MM $< > $@.temp
gcc -MM foo.c > foo.d.temp
,展开为gcc -MM foo.c > foo.d.temp
(或其他编译器)。 -MM
标志告诉编译器产生一个依赖关系列表:
foo.o: foo.c foo.h bar.h
下一行用sed咀嚼这个列表
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g'
这大致转换为“改变foo.o:
到foo.o foo.d :
”:
foo.o foo.d : foo.c foo.h bar.h
(最后一个命令删除临时文件。)
这不是处理依赖关系的最好方法,因为每次运行Make时都会重新生成所有的%.d
文件,甚至与您的目标无关。 高级自动依赖生成是一个更加优化的方法。
从gcc手册 :
-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, ...
所以这里的命令是:
$(CC) -MM $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
用gcc -MM
输出创建一个临时文件($ @。$ $ $ $,即目标文件名附加一个唯一编号),并使用sed格式,这样当makefile包含的时候,依赖文件看起来就像target file: [gcc generated dependencies]
。 然后删除原始的gcc -MM
输出。
#include
的实际跟踪由预处理器执行(请参阅-M
和-MM
选项 )。
-M
输出一个适用于描述主源文件依赖关系的规则,而不是输出预处理结果。 预处理器输出一个
make
规则,该规则包含该源文件的目标文件名,冒号以及所有包含文件的名称,包括来自-include
或-imacros
命令行选项的名称。
-MM
和
-M
一样,但是不要提到在系统头文件-M
中找到的头文件,也不要直接或间接地从头文件中提到头文件。
这里还有一个相当不错的关于跟踪这些类型的依赖关系的方法。
对于所有的连续四个美元符号的gobbledegook,这个想法是相当简单的:
xyz.d
创建文件xyz.c
,请运行显示的命令… $(CC)
行应该包含$(CFLAGS),但是假定编译器是GCC( -MM
选项在其他地方不是标准的)。 -MM
标志请求编译器生成输出,列出源中包含的头文件(或其他文件),以便为当前平台获取正确的相关性信息。 $@.$$$$
的扩展是(例如),shell中的xyz.$$
,其中$$
成为运行脚本的shell的PID。 sed
命令从编译器输出,这样一个以xyz.o
开始的行以xyz.o xyz
xyz.o xyz.d
xyz.o xyz.d
,所以如果任何源文件或头文件改变,则目标文件和依赖文件都需要更新。 xyz.d
sinclude
行读取尽可能多的.d
文件。 [ 修改了Beta指出的差异]