我有一个包含一些模式规则¹的 make 文件,这些规则最终成为另一个规则的先决条件。显示我感到困惑的症状的最小示例是:
.PHONY: clean default one two
default: clean one two
clean:
@rm -f {one,two}.{a,b}
one two: %: %.a %.b
@echo TARGET $@ PREREQUISITES $^
%.a %.b:
@echo Prereq $@
@touch $@
运行时我期望的输出是:
相反,只有第一个先决条件被构建,
make
给了我这个:如您所见,配方本身正确解析了这些,并且知道它应该在两个先决条件之后构建,但实际上并没有运行先决条件规则。
顺便说一句,我在这里为第二项使用了仅限订单的先决条件,但这并不重要:无论哪种方式都会出现相同的问题。
如果我第二次运行相同的目标,那么它会构建第二个先决条件。换句话说,两次传球终于让我得到了我需要的结果:
$ make clean
$ make one
Prereq one.a
TARGET one PREREQUISITES one.a one.b
$ make one
Prereq one.b
TARGET one PREREQUISITES one.a one.b
它第一次运行时,只存在第一个先决条件,我留下了一个损坏的
one
。在第二遍 one.a
存在时,它决定构建 one.b
。现在两个先决条件都存在,我的 one
构建正确。如果我将先决条件目标变体拼写为两个单独的配方(分别为
%a:
和 %.b
模式重复相同的块),那么将为每个目标构建两个先决条件。但是,这会使我的文件变得更加复杂,我想了解为什么会中断。¹ 这个 make 文件有一些 other problems 但我已经将此问题隔离到一个可重现的案例中,所以我认为它的其他特性不是这里的问题。
最佳答案
GNU Make 有一些我们可能认为的“怪癖”,因为多目标模式规则不像多个规则那样工作。在这种情况下,规则仅由单个目标触发一次,而 Make 期望规则一次生成所有目标。从 GNU Make documentation :
所以这里发生的事情是:
one
的依赖关系:从模式 one.a
中找到 one.b
和 %: %.a %.b
; one.a
和 one.b
找到一个模式规则; $@
)设置为 one.a
(触发规则的那个); one.a
和 one.b
标记为更新,即使配方只创建了 one.a
; one
的所有依赖项都已得到满足,然后继续对 two
做同样的事情。 再次调用时,Make 接触不同文件的原因是因为现在
one.a
和 two.a
是最新的。所以触发规则的目标变成 one.b
和 two.b
。如果在第二次调用 Make 之前只删除 one.a
,它将创建 one.a
和 two.b
。所以你至少有三种可能的解决方案。一个是列出行为符合您预期的目标——您说这不是一个好的解决方案,但无论如何这里都值得一提。另一种方法是将
%.a %.b
分成两个规则,这可以实现相同但考虑到您的需求可能更容易。第三个是做 Make 期望的事情并在同一个配方中创建两个目标——例如,你可以在配方中使用词干:%.a %.b:
@echo Processing target $@ from stem $*
touch $*.a $*.b
我在调查这个问题时注意到的另一件事是你的 MCVE 的
clean
被破坏了。默认情况下,Make 将使用 /bin/sh
,它不会扩展像 {a,b}
之类的东西。你可以设置 SHELL=/bin/bash
来改变它。关于makefile - 为什么要在先决条件之前运行配方?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41694704/