我有一个带有一些模式规则¹的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 $@
运行时我期望的输出是:
Prereq one.a
Prereq one.b
目标一个前提是一个。一个.b
Prereq two.a
Prereq two.b
目标两个前提两个.a two.b
相反,只有第一个先决条件被构建,make
给了我这个:
Prereq one.a
目标一个前提是一个。一个.b
Prereq two.a
目标两个前提两个.a two.b
正如您所看到的,配方本身正确地解析了这些,并且知道它应该在两个先决条件之后构建,但实际上还没有运行先决条件规则。
顺便说一下,我在这里使用仅限订单的先决条件,但这并不重要:同样的问题无论如何都会出现。
如果我第二次运行相同的目标,那么它会构建第二个先决条件。换句话说,两次传球最终得到了我需要的结果:
$ 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但是我已经把这个问题分离成了一个可重现的案例,所以我不认为它的其他特性是这里的问题。
答案 0 :(得分:5)
GNU Make具有我们可能认为有点“怪癖”的含义,即多目标模式规则与多个规则完全不同。在这种情况下,规则仅由单个目标触发一次,并且Make期望规则立即生成所有目标。来自GNU Make documentation:
模式规则可能有多个目标。与普通规则不同,这并不会使用相同的先决条件和配方来执行许多不同的规则。如果模式规则具有多个目标,则make知道规则的配方负责制作所有目标。配方仅执行一次以制作所有目标。 [...]
所以这里发生的事情是:
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
。如果您在第二次致电之前仅删除one.a
,则会创建one.a
和two.b
。
所以你至少有三种可能的解决方案。一个是拼出那些表现得像你期望的目标 - 你说这不是一个好的解决方案,但无论如何都值得一提。另一种方法是将%.a %.b
分为两个规则,这些规则可以实现相同但考虑到您的需求可能更容易。第三个是做同样的配方 - 例如。,做出预期和创造两个目标的东西,你可以在配方中使用词干:
%.a %.b:
@echo Processing target $@ from stem $*
touch $*.a $*.b
在调查此问题时我注意到的另一件事是您的MCVE clean
已被破坏。默认情况下,Make会使用/bin/sh
,这不会扩展{a,b}
之类的内容。您可以设置SHELL=/bin/bash
来更改它。