Makefile 为什么make要在先决条件之前运行配方?
我有一个make文件,其中包含一些模式规则,作为另一个规则的先决条件。下面是一个最起码的例子,说明了我所困惑的症状:Makefile 为什么make要在先决条件之前运行配方?,makefile,gnu-make,Makefile,Gnu Make,我有一个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 $@
.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 $@
运行此命令时,我期望的输出是:
预请求1.a预请求1.b
目标一个先决条件一个.a一个.b
预请求2.a
预请求2.b
目标2先决条件2.a 2.b 相反,只有第一个先决条件得到构建,
make
给了我以下信息:
预请求1.a目标一个先决条件一个.a一个.b
预请求2.a
目标2先决条件2.a 2.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.a
,因此它决定构建one.b
。既然这两个先决条件都存在,我的one
将正确构建
如果我将先决条件目标变体拼写为两个单独的配方(分别对%a:
和%.b
模式重复相同的块),那么两个先决条件都将为每个目标构建。然而,这会使我的文件变得更加复杂,我想知道为什么会出现这种情况
<>这使文件有一些,但我已经把这个问题孤立到一个可重复的情况,所以我不认为它的其他特质是这里的问题。 GNUmake具有我们可以考虑的一点“怪癖”,在这个意义上,多个目标模式规则不能像几个规则一样工作。在这种情况下,规则仅由单个目标触发一次,Make希望规则同时生成所有目标。从:
模式规则可能有多个目标。与普通规则不同的是,这并不像许多具有相同先决条件和配方的不同规则那样起作用。如果模式规则有多个目标,make知道规则的配方负责生成所有目标。配方只执行一次以生成所有目标。[……]
因此,这里发生的是:
检查one
:从模式%:%.a%.b
中查找one.a
和one.b
李>
为one.a
和one.b
查找一个模式规则李>
执行配方,目标($@
)设置为one.a
(触发规则的目标)李>
将one.a
和one.b
标记为已更新,即使配方仅创建one.a
李>
认为对one
的所有依赖性都已满足,并继续对two
执行相同的操作
再次调用时,为什么要触摸不同的文件,是因为现在one.a
和two.a
是最新的。因此触发规则的目标变成one.b
和two.b
。如果在第二次调用Make之前只删除one.a
,它将创建one.a
和two.b
因此,您至少有三种可能的解决方案。一个是详细说明那些行为像你期望的那样的目标,你说这不是一个好的解决方案,但无论如何,这里值得一提。另一种方法是将%.a%.b
分为两条规则,这两条规则的效果相同,但考虑到您的需要可能更容易。第三个是按照Make的期望,在同一配方中创建两个目标,例如,您可以在配方中使用stem:
%.a %.b:
@echo Processing target $@ from stem $*
touch $*.a $*.b
在研究这个问题时,我注意到的另一件事是,您的MCVE的clean
坏了。默认情况下,Make将使用/bin/sh
,这不会扩展{a,b}
之类的内容。您可以设置SHELL=/bin/bash
来更改它。可能重复@giusti这是一个只需订单的先决条件问题。完全相同的结果只显示正常依赖项。抱歉。我错了。谢谢,这很有帮助。我理解你建议的第一和第三种选择。第一个是可行的,但令人讨厌,因为这是一个巨大的配方,有很多逻辑,我不想重复。第三个是不可能的,因为有些make运行将生成一个而不是另一个,而有些将同时生成两个(这里涉及测试、CI和部署)。并非所有的运行都能够同时为a和b提供先决条件。我完全不明白你的第二个选择。这与第一个选项有什么不同呢?为了避免重复,只需使用define
将配方放入多行变量中即可endef
然后您可以在所有规则的配方中使用该变量,而不是多次将其全部写出来。第一个建议是one.aOne.b2.a2.b:
。如果你想扩展到three.a
和three.b
,你也需要把它们列为目标。第二个规则是一个用于%.a
,另一个用于%.b
。您不需要修改任何东西来扩展three.{a,b}
。当然,如果您有一个依赖于one.a
和two.a
的规则,您将再次遇到相同的问题。因此,只有当您能够确保每个目标和规则的前缀%suffix
只匹配一个依赖项时,此建议才适用。谢谢,这很有帮助。我不明白你的第一个选择。在我的例子中,我实际上可以这样做,因为我可以通过编程生成所有可能排列的列表。遗憾的是,我发现这并不能像我的计划那样解决问题