【问题标题】:Makefile ignores dependency every other timeMakefile 每隔一段时间就会忽略依赖关系
【发布时间】:2018-10-12 16:10:55
【问题描述】:

我希望我的 Makefile 执行以下操作:

  • 当主目标需要某个文件时,总是运行一些命令
  • 如果依赖于该文件的所有内容发生更改(运行这些命令的结果),则重新构建它

在我的例子中,这个文件是一个 C 头文件,它应该包含版本信息;我希望 makefile 始终更新此版本标头并重建包含它的所有文件。

会发生什么: 当我运行 make 时,每次都会执行版本标头更新(如我所愿)。 但是依赖于这个头文件的文件只会每隔一段时间重新构建一次。

这是我精简的 Makefile:

SOURCES:= main.c

app.exe: $(SOURCES:%.c=%.o)
    gcc -o $@ $^

# always recompile when dependency information is missing
$(SOURCES:%.c=%.o): %.o: %.c %.d
    gcc -c $< -MMD

$(SOURCES:%.c=%.d):

version.h: update-version

update-version:
    touch version.h

# include C/C++ header-dependencies
-include $(SOURCES:%.c=%.d)

.PHONY: update-version

# disables builtin suffix-rules
.SUFFIXES:

谁能帮我理解为什么这不是每次都重新编译,而是每次都重新编译一次?

make 是 GNU Make 4.2.1,remake 的行为完全相同。

【问题讨论】:

  • 改进建议...而不是在每个地方都写出模式替换 ($(SOURCES:%.c=%...)),我发现它更具可读性,例如OBJECTS:=$(SOURCES:%.c=%.o)DEPENDS:=$(SOURCES:%.c=%.d) 在顶部,以及使用它们的规则(例如 -include $(DEPENDS))。 -- 此外,使用-MP 选项,GCC 将为依赖文件inside 的每个依赖文件生成“虚拟”规则(不再需要$(SOURCES:%.c=%.d):)。
  • AFAICT,-MP 选项只会为所有 header 文件添加空规则,而不是 *.d 文件本身。 (在删除头文件后使用这个标志来防止错误仍然很好)。只需要空的*.d 规则,因为我想在目标文件存在但*.d 文件丢失/删除时强制重新编译。
  • 你是对的,当然;好点子。根据您的需要,您可能希望 .o 也依赖于 Makefile,因此您可以重新编译,例如当您更改警告列表时。

标签: makefile build gnu-make


【解决方案1】:

excerpt from make's manual 说明了为什么您的 makefile 无法按您的预期方式工作:

GNU make 在两个不同的阶段完成它的工作。在第一阶段 它读取所有的makefile、包含的makefile等并内化 所有变量及其值,隐式和显式规则,以及 构建所有目标及其依赖关系图 先决条件。在第二阶段,make 使用这些内部 结构,以确定哪些目标需要重建并 调用必要的规则。

由于您在规则中执行命令(在您的示例中为touch),make 将在第二阶段执行它。但是到那个时候,依赖图和过时的目标已经确定了;这已经发生在第一阶段。

考虑到这一点,您就会明白为什么它每隔一段时间就会起作用。您想要的命令确实每次都会执行,但只有当文件version.h 在第一阶段已经比main.o 更新时,main.o 才会被认为是过时的规则已经执行。所以基本上main.o 只有在你调用make 命令的那一刻早于version.h 时才会被认为是过时的。如果您上次调用 make 时没有重新编译 main.o,就会出现这种情况。

您可以用来每次都及时执行命令的技巧是在开始的某处添加一个虚拟变量,并让它在评估期间执行您想要的命令。以touch 再次作为示例命令,您将在第一行执行以下操作:

DUMMY := $(shell touch version.h)

由于这是立即分配,因此命令将在第一阶段执行。如果该命令更新version.h,则依赖于version.h 的所有目标都将被视为过期并重新构建。这包括main.o

使用这种方法,您不再需要任何 .PHONY: update-version 机制及其相关的规则和配方。


更新

重读,我意识到你写道:“总是运行一些命令当主要目标需要某个文件时”。你是什​​么意思?再往下,你写了“当我运行 make 时,每次都会执行版本头更新(就像我想要的那样)”——这就是这个答案产生的 makefile 所做的。你能澄清一下吗? “主要目标”是什么意思?

【讨论】:

  • 并不是我的 Makefile 中的每个目标都需要“version.h”文件。但我不介意在 every 调用时更新它。您的解决方案很适合我的情况,非常感谢!
猜你喜欢
  • 1970-01-01
  • 2010-10-05
  • 2012-03-02
  • 2023-04-03
  • 1970-01-01
  • 2013-01-04
  • 2023-04-02
  • 2016-06-22
  • 2014-01-23
相关资源
最近更新 更多