为什么在先决条件之前运行配方?

时间:2017-01-17 10:29:35

标签: 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 $@

运行时我期望的输出是:

  

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但是我已经把这个问题分离成了一个可重现的案例,所以我不认为它的其他特性是这里的问题。

1 个答案:

答案 0 :(得分:5)

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

  

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

所以这里发生的事情是:

  1. 检查one的依赖关系:从模式one.a找到one.b%: %.a %.b;
  2. one.aone.b;
  3. 找到一个模式规则
  4. 执行配方,目标($@)设置为one.a(触发规则的那个);
  5. one.aone.b标记为已更新,即使配方仅创建了one.a;
  6. 认为one的所有相关性都已得到满足,并继续对two执行相同操作。
  7. Make再次调用时触摸不同文件的原因是因为现在one.atwo.a是最新的。因此触发规则的目标变为one.btwo.b。如果您在第二次致电之前仅删除one.a,则会创建one.atwo.b

    所以你至少有三种可能的解决方案。一个是拼出那些表现得像你期望的目标 - 你说这不是一个好的解决方案,但无论如何都值得一提。另一种方法是将%.a %.b分为两个规则,这些规则可以实现相同但考虑到您的需求可能更容易。第三个是做同样的配方 - 例如。,做出预期和创造两个目标的东西,你可以在配方中使用词干:

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

    在调查此问题时我注意到的另一件事是您的MCVE clean已被破坏。默认情况下,Make会使用/bin/sh,这不会扩展{a,b}之类的内容。您可以设置SHELL=/bin/bash来更改它。