我有一个带有一些模式规则¹的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
TARGET one PRACQUISITES one.a
one.b Prereq two.a Prereq
two.b
目标两个PREREQUISITES two.a two.b
相反,只有第一个先决条件被构建,make
给我这个:
Prereq one.a
TARGET one PRACQUISITES one.a one.b Prereq
two.a
TARGET两个PREREQUISITES two.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文件有一些其他的问题,但我已经把这个问题分离成一个可重现的案例,所以我不认为它的其他特性是这里的问题.
GNU Make具有我们可能认为有点"怪癖"的意义,因为多目标模式规则不像几个规则那样起作用.在这种情况下,规则仅由单个目标触发一次,并且Make期望规则立即生成所有目标.从GNU Make文档:
模式规则可能有多个目标.与普通规则不同,这并不会使用相同的先决条件和配方来执行许多不同的规则.如果模式规则具有多个目标,则make知道规则的配方负责制作所有目标.配方仅执行一次以制作所有目标.[...]
那么这里发生的是:
检查依赖关系one
:find one.a
和one.b
from pattern %: %.a %.b
;
找到一个图案规则两者one.a
和one.b
;
执行配方,将target($@
)设置为one.a
(触发规则的那个);
标记两者one.a
并one.b
更新,即使仅创建了配方one.a
;
认为所有依赖one
关系已经满足并继续做同样的事情two
.
Make再次调用它时触摸不同文件的原因是因为现在one.a
并且two.a
是最新的.因此触发规则的目标变为one.b
和two.b
.如果您one.a
在调用Make第二次之前删除它,它将创建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
更改它.