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

时间:2012-03-26 14:56:36

标签: c linux makefile header-files

%.d: %.c
        @set -e; rm -f $@; \
        $(CC) -MM  $< > $@.$$$$; \
        sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
        rm -f $@.$$$$

sinclude $(SOURCES:.c=.d)

上面是我在某人的博客上看到的,但它没有解释代码如何在makefile中创建.c和.h文件之间的依赖关系。

有人会为我解释或提供一些材料吗?

我非常感谢您的帮助!谢谢!

4 个答案:

答案 0 :(得分:3)

From gcc manual

-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输出。

答案 1 :(得分:0)

#include的实际跟踪由预处理器执行(参见-M and -MM options)。

  

-M

     

输出适用于描述主源文件依赖关系的make的规则,而不是输出预处理的结果。预处理器输出一个make规则,其中包含该源文件的目标文件名,冒号以及所有包含文件的名称,包括来自-include-imacros命令行选项的文件

     

-MM

     

-M类似,但不提及在系统头目录中找到的头文件,也不提及直接或间接包含在此头文件中的头文件。

另外,我们可以很好地了解跟踪这些类型依赖关系的可能方法here

答案 2 :(得分:0)

假设您唯一的新源文件是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)

Make将$$$$评估为$$并将其传递给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(或其他一些编译器)。 -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个文件,即使是与您无关的目标也是如此。更加精致的方法是Advanced Auto-Dependency Generation

答案 3 :(得分:0)

对于所有带有四个美元符号的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,因此如果任何源文件或头文件发生变化,则需要更新目标文件和依赖文件。
  5. 输出将写入xyz.d
  6. 顶部和底部都有清理。
  7. sinclude行会尽可能多地读取.d个文件。
  8. [已编辑以修复Beta指出的差异。]