如何自动生成文件之间的依赖关系?

%.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,这个想法是相当简单的:

  1. 要从文件xyz.d创建文件xyz.c ,请运行显示的命令…
  2. $(CC)行应该包含$(CFLAGS),但是假定编译器是GCC( -MM选项在其他地方不是标准的)。 -MM标志请求编译器生成输出,列出源中包含的头文件(或其他文件),以便为当前平台获取正确的相关性信息。
  3. 编译器编译源代码并将依赖项写入标准输出,该输出被重定向到一个临时文件。 makefile中$@.$$$$的扩展是(例如),shell中的xyz.$$ ,其中$$成为运行脚本的shell的PID。
  4. sed命令从编译器输出,这样一个以xyz.o开始的行以xyz.o xyz开始(所以从目标文件创建的目标文件和程序都依赖冒号 xyz.o xyz.d 的文件) xyz.o xyz.d ,所以如果任何源文件或头文件改变,则目标文件和依赖文件都需要更新。
  5. 输出写入xyz.d
  6. 顶部和底部都有清理。
  7. sinclude行读取尽可能多的.d文件。

[ 修改了Beta指出的差异]