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

运行时我期望的输出是:



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



如您所见,配方本身正确解析了这些,并且知道它应该在两个先决条件之后构建,但实际上并没有运行先决条件规则。

顺便说一句,我在这里为第二项使用了仅限订单的先决条件,但这并不重要:无论哪种方式都会出现相同的问题。

如果我第二次运行相同的目标,那么它会构建第二个先决条件。换句话说,两次传球终于让我得到了我需要的结果:
$ 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 但我已经将此问题隔离到一个可重现的案例中,所以我认为它的其他特性不是这里的问题。

最佳答案

GNU Make 有一些我们可能认为的“怪癖”,因为多目标模式规则不像多个规则那样工作。在这种情况下,规则仅由单个目标触发一次,而 Make 期望规则一次生成所有目标。从 GNU Make documentation :



所以这里发生的事情是:

  • 检查 one 的依赖关系:从模式 one.a 中找到 one.b%: %.a %.b
  • one.aone.b 找到一个模式规则;
  • 执行配方,目标( $@ )设置为 one.a (触发规则的那个);
  • one.aone.b 标记为更新,即使配方只创建了 one.a
  • 认为 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 来改变它。

    关于makefile - 为什么要在先决条件之前运行配方?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41694704/

    10-14 19:53