当GNU更新.d文件时,确切的事件链是什么?

时间:2017-12-07 17:26:24

标签: gcc makefile gnu-make

考虑以下简单的makefile:

#------------------------------#
#    List all object files     #
#------------------------------#
objects := main.o foo.o bar.o baz.o

#------------------------------#
#    Define pattern rule       #
#    for   *.c -> *.o          #
#------------------------------#
%.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@

#------------------------------#
#    Define the rule to make   #
#    the end result            #
#------------------------------#
.PHONY all
all: myApp.elf

myApp.elf: $(objects)
    $(CC) $(objects) -o myApp.elf $(LFLAGS) $(LIBS)

如果你给出命令make all,GNU make将从目标myApp.elf开始。它会查看所有先决条件main.ofoo.obar.obaz.o并尝试更新它们。

为此,make使用makefile中间定义的模式规则。此模式规则扩展如下:

main.o: main.c
    $(CC) -c $(CFLAGS) main.c -o main.o

foo.o: foo.c
    $(CC) -c $(CFLAGS) foo.c -o foo.o

bar.o: bar.c
    $(CC) -c $(CFLAGS) bar.c -o bar.o

baz.o: baz.c
    $(CC) -c $(CFLAGS) baz.c -o baz.o

到目前为止,这么好。但是你可以清楚地看到缺少依赖项(包括h文件)。

我发现了一些推荐以下方法的消息来源:

#------------------------------#
#    List all object files     #
#    and all dependency files  #
#------------------------------#
objects := main.o foo.o bar.o baz.o
deps := $(objects:.o=.d)

#------------------------------#
#    Define pattern rule       #
#    for   *.c -> *.o          #
#------------------------------#
%.o: %.c
    $(CC) -c $(CFLAGS) -MMD -MP $< -o $@

#------------------------------#
#    Define the rule to make   #
#    the end result            #
#------------------------------#
.PHONY all
all: myApp.elf

myApp.elf: $(objects)
    $(CC) $(objects) -o myApp.elf $(LFLAGS) $(LIBS)

-include $(deps)

我正试图围绕这种方法。第一个GNU make读取makefile并查找其他包含的makefile(参见GNU make manual第3.5节“Makefile如何重新制作”)。在开始执行makefile之前,尝试更新它可以找到的每个包含的makefile(在本例中为依赖文件main.dfoo.dbar.dbaz.d)。我在下图中对此进行了总结:


enter image description here

完全。我没有看到将依赖项文件指定为目标的任何规则。

请帮助我了解接下来会发生什么。请将您的答案写成一个循序渐进的事件链。如果能够深入了解此事,那将是很好的。

注意:
当然,编译命令中的-MMD-MP参数(请参阅中间的模式规则)会导致创建依赖项文件。但是这种模式规则的目标是对象 - 文件,而不是依赖 - 文件。

注意:
我错误地认为GNU make有一个隐式规则在这里被激活。但是由于你的意见和答案,我现在知道这是错的: - )

3 个答案:

答案 0 :(得分:1)

.d个文件没有built-in make rule。您可以看到make确实考虑使用.d重建make -d个文件,但没有规则,例如:

Reading makefile 'test/debug/library.d' (search path) (don't care) (no ~ expansion)...
Updating makefiles....
 Considering target file 'test/debug/library.d'.
  File 'test/debug/library.d' does not exist.
  Looking for an implicit rule for 'test/debug/library.d'.
  No implicit rule found for 'test/debug/library.d'.
  Finished prerequisites of target file 'test/debug/library.d'.
 Must remake target 'test/debug/library.d'.
 Failed to remake target file 'test/debug/library.d'.

在上面的输出中make检查是否需要更新library.d。如果找不到规则,make会跳过更新规则,请参阅How Makefiles Are Remade

  

...在阅读所有makefile后,make会将每个作为目标目标并尝试更新它。 如果一个makefile有一条规则说明如何更新它(在那个makefile或另一个中找到),或者隐含规则适用于它,那么它将在必要时更新

这就是为什么它不会每次.c编译两次。

您可以使用make --print-data-base打印所有规则,包括内置插件。

答案 1 :(得分:1)

我会一起回答这两个问题,因为它更有意义。

Make没有.d的内置规则 - 它不需要一个。 -MMD -MP指示编译器输出依赖项作为编译的一部分。

如果您考虑它,这正是您想要的,因为您需要更新依赖项文件的唯一时间是相对源文件自身已更改,如果在编译期间生成依赖项,则只需运行编译器一次生成.d.o文件。

拼图的最后一部分是-include,告诉make如果能找到它们就包含依赖项,但如果它们不存在就不用担心,它们无论如何都会由编译器生成

答案 2 :(得分:1)

make之后运行make clean并且依赖文件尚未存在时会发生什么:

  1. make读取文本并到达-include指令。
  2. 对于-include指令make的每个参数,尝试查找将其作为目标并运行该规则的规则。 make这是处理-include指令的一部分。没有找到这样的规则,因为你没有提供这样的规则。
  3. make尝试读取-include指令中指定的文件,并跳过那些不存在的文件(在这种情况下全部都是这样)。
  4. make用于构建项目,该项目又创建了*.d个文件。
  5. 如果再次运行make

    1. [与以前相同]
    2. [与以前相同]
    3. 由于文件现在确实存在,make会读取它们。
    4. [与以前相同]
    5. 您可能会注意到这意味着make使用上一次运行的依赖项,这正是它的工作原理。 依赖性滞后于源文件中的更改。 这通常不会造成不便,并且当删除依赖项文件时,可以使用它来修复构建。