【问题标题】:About the GNU make dependency files *.d关于 GNU make 依赖文件 *.d
【发布时间】:2016-08-17 16:41:59
【问题描述】:

在程序的生成文件中,必须编写规则来定义每个目标文件的依赖关系。考虑目标文件fileA.o。很明显,这个目标文件依赖于源文件fileA.c。但它也将取决于此源文件包含的所有头文件。所以应该在makefile中添加以下规则:

    # This rule states that fileA.o depends on fileA.c (obviously), but also
    # on the header files fileA.h, fileB.h and fileC.h
    fileA.o: fileA.c fileA.h fileB.h fileC.h

请注意,该规则没有配方。可以向其中添加配方,但严格来说没有必要,因为 GNU make 可以依赖隐式规则(带有配方)将*.c 文件编译成*.o 文件。

无论如何,手动编写这样的规则是一项艰巨的任务。想象一下使 makefile 规则与源代码中的 #include 语句保持同步的工作。

GNU make 手册在第 4.14 章“自动生成先决条件”中描述了一种自动执行此过程的方法。该过程从为每个源文件生成一个*.d 文件开始。我引用:

对于每个源文件 name.c 都有一个 makefile name.d 列出目标文件 name.o 依赖的文件.

手册继续:

以下是从名为 name.c 的 C 源文件生成名为 name.d 的先决条件文件(即 makefile)的模式规则: p>

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

遗憾的是,该手册并未详细解释此规则的实际工作原理。是的,它提供了所需的 name.d 文件,但为什么呢?规则很混乱..

当看到这条规则时,我觉得它的配方只能在 Linux 上顺利运行。我对吗?有没有办法让这个配方也能在 Windows 上正确运行?

非常感谢任何帮助:-)

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    所有错误退出

    @set -e;
    

    删除现有的 dep 文件 ($@ = target = %.d)

    rm -f $@;
    

    让编译器生成 dep 文件并输出到以 shell pid 为后缀的临时文件($&lt; = 第一个先决条件 = %.c$$$$ -> $$ -> pid)

    $(CC) -M $(CPPFLAGS) $< > $@.$$$$;
    

    捕获匹配$*.o的目标($* = match stem = %),将其替换为目标后跟依赖文件本身,输出到dep文件

    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    

    删除临时部署

    rm -f $@.$$$$
    

    让我们插入fooCC = gccCPPFLAGS = '',看看make完成扩展后会发生什么:

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

    shell 本身会将$$ 扩展为 pid,dep 文件中的最终规则类似于

    foo.o foo.d : foo.c foo.h someheader.h
    

    请注意,这是一种非常过时的生成依赖项的方式,如果您使用 GCC 或 clang,您可以使用 generate them 作为编译本身的一部分,使用 CPPFLAGS += -MMD -MP

    假设你有一个名为foo的程序:

    objs := foo.o bar.o
    deps := $(objs:.o=.d)
    
    vpath %.c $(dir $(MAKEFILE_LIST))
    
    CPPFLAGS += -MMD -MP
    
    foo: $(objs)
    
    .PHONY: clean
    clean: ; $(RM) foo $(objs) $(deps)
    
    -include $(deps)
    

    这就是您所需要的,内置规则将完成剩下的工作。显然,如果您想将目标文件放在不同的文件夹中,或者如果您想在源代码树之外构建,事情会稍微复杂一些。

    vpath 指令允许您在不同的目录中运行 make 并在那里创建文件,例如make -f path/to/source/Makefile.

    【讨论】:

    • 哇,非常感谢您的所有解释。我仍在研究您的解释以完全理解。但你肯定给了我很大的推动力:-)
    • 我确实在使用 GCC 编译器。那么有没有更优雅的方式来生成依赖呢?你能描述一下吗?问这个问题我有点害羞,因为我知道这可能会占用你很多时间。但我会很感激:-)
    • @K.Mulier 添加了一个简短的示例。
    • 哇,非常感谢这个例子。不幸的是,我想“在源代码树之外构建”。通过这种方式,我保持源文件夹整洁。但老实说,我不知道如何为此生成依赖项
    • 有几种方法可以“在源代码树之外”构建——或者在源代码树中构建时将路径放在 .o 和 .d 文件上,以便将它们放在其他位置,或者使用 VPATH 或源上的路径并构建在仅对象树中。
    【解决方案2】:

    回复 K Mulier: 试试:

    gmake -C $workdir ... 
    这将设置
    CURDIR = ${workdir}
    但单独留下 $PWD(来自 env)。 所有临时/输出文件都在 ${workdir} 中创建。 另外,我设置了变量 $. = ${PWD} 并使用它来指定与运行 gmake 的目录相关的任何内容(即源目录)。 所以,而不是
    CPPFLAGS += -I../include

    CPPFLAGS += -I$。 -I$./../include

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 2015-05-09
      • 1970-01-01
      • 2010-10-09
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多