【问题标题】:Why is make running a recipe before its prerequisites?为什么在其先决条件之前运行配方?
【发布时间】:2017-01-17 10:29:34
【问题描述】:

我有一个包含一些模式规则的 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 $@

运行时我期望的输出是:

先决条件一.a
先决条件一.b
目标一 先决条件 one.a one.b
先决条件二.a
先决条件二.b
目标两个先决条件二.a 二.b

相反,只构建了第一个先决条件,make 给了我这个:

先决条件一.a
目标一 先决条件 one.a one.b
先决条件二.a
目标两个先决条件二.a 二.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,但我已经将此问题隔离到一个可重现的案例中,所以我认为它的其他特质不是这里的问题。

【问题讨论】:

标签: makefile gnu-make


【解决方案1】:

GNU Make 有一个我们可能认为有点“怪癖”的地方,即多目标模式规则的工作方式与多个规则不同。在这种情况下,该规则仅由单个目标触发一次,并且 Make 期望该规则一次生成所有目标。来自GNU Make documentation

模式规则可能有多个目标。与普通规则不同,这不会像许多具有相同先决条件和配方的不同规则一样起作用。如果模式规则有多个目标,make 知道规则的配方负责生成所有目标。该配方仅执行一次以生成所有目标。 [...]

所以这里发生的是:

  1. 检查one的依赖关系:从模式%: %.a %.b中找到one.aone.b
  2. one.aone.b 找到一个模式规则;
  3. 执行配方,目标 ($@) 设置为 one.a(触发规则的那个);
  4. one.aone.b 标记为已更新,即使配方仅创建one.a
  5. 认为one 的所有依赖都已得到满足,并继续对two 做同样的事情。

Make 再次调用它时会触及不同文件的原因是因为现在one.atwo.a 是最新的。所以触发规则的目标变成one.btwo.b。如果在第二次调用 Make 之前只删除 one.a,它将创建 one.atwo.b

所以你至少有三个可能的解决方案。一种是说明行为符合您预期的目标——您说这不是一个好的解决方案,但无论如何这里值得一提。另一个是将%.a %.b 分解为两条规则,这样可以达到相同的效果,但考虑到您的需求可能会更容易。第三个是做 Make 期望的事情,并在同一个配方中创建两个目标——例如,您可以在配方中使用茎:

%.a %.b:
        @echo Processing target $@ from stem $*
        touch $*.a $*.b

我在调查此问题时注意到的另一件事是您的 MCVE 的 clean 已损坏。默认情况下,Make 将使用/bin/sh,它不会扩展像{a,b} 这样的东西。你可以设置SHELL=/bin/bash来改变它。

【讨论】:

  • 谢谢,这很有帮助。我了解您建议的第一个和第三个选项。第一个是可行的,但令人讨厌,因为它是一个包含很多逻辑的巨大配方,我不想重复。第三个几乎是不可能的,因为有些 make 运行会生成一个,而不是另一个,而有些会两者都做(这里涉及测试和 CI 和部署的东西)。并非所有运行都能够为 a 和 b 提供先决条件。我完全不明白你的第二个选择。这与第一个选项有何不同。
  • 为了避免重复自己,只需使用 define ... endef 将配方放入多行变量中,然后您可以在所有规则的配方中使用该变量,而不是编写多次出现。
  • 第一个建议是one.a one.b two.a two.b:。如果您想扩展到three.athree.b,您也需要将它们拼写为目标。第二个是%.a 的一条规则和%.b 的另一条规则。您无需修改​​任何内容即可扩展 three.{a,b}。当然,如果您有一个依赖于one.atwo.a 的规则,您将再次遇到同样的问题。因此,仅当您可以确保 prefix%suffix 将仅匹配每个目标和规则的一个依赖项时,该建议才适用。
  • 谢谢,这很有帮助。我错过了你的第一个选择。就我而言,我实际上可以这样做,因为我可以以编程方式生成所有可能排列的列表。可悲的是,我发现这实际上并没有像我的 MWE 建议的那样解决问题。我在一个没有有多种模式的项目中遇到了同样的问题(即只有%.a:而不是%.a %.b)。提供完整的固定名称列表也不能解决这个问题,我仍然遇到目标配方在它的先决条件之前运行!
  • 这很有趣。这是关于另一个问题you linked from here 还是完全不同?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-01
  • 2018-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多