在Make中优先考虑模式规则

5

我有一个(大致)如下的Makefile:

.PHONY: all
.SUFFIXES:

OUT = /www/web

all: $(OUT)/index.html

# rule 1
%.html: %.in
    build_html $< $@

# rule 2
$(OUT)/%: %
    cp $< $@

这个 Makefile 存在一个问题,因为有两种不同的方法可以构建 $(OUT)/index.html:

  1. 构建 ./index.html (规则 1),然后将其复制到 $(OUT) (规则 2)。
  2. ./index.in 复制到 $(OUT) (规则 2),然后构建 $(OUT)/index.html (规则 1)。

我希望 make 总是更倾向于选项1。如何表明这两个模式规则之间存在首选顺序?

(我能想到一些笨办法来解决这个特定的问题,但我希望找到一个尽可能通用的解决方案——例如,将规则2的模式改为 $(OUT)/%.html: %.html 将解决问题,但会丧失通用性,因为如果我想以同样的方式处理其他类型的文件,就需要重复自己。)


1
我观察到不良行为(选项2)。我正在使用GNU Make 3.81。我认为您误解了“最短词干”启发式如何影响此处的构建顺序:它将强制执行不良行为,而不是良好行为!顶级目标是$(OUT)/index.html,规则1最匹配(最短词干)。规则1表示我们需要$(OUT)/index.in,只有规则2与之匹配。 - Calvin
你之前没有说你在使用GNU make 3.81版本。最短茎行为是在GNU make 3.82中引入的。在之前的版本中,模式按照它们在makefile中定义的顺序进行尝试,第一个匹配的模式被使用。 - MadScientist
@MadScientist 很好的观点。即便如此,我非常确定你会看到我上述描述的原因导致的错误行为。 - Calvin
1
不,干部分是与“%”匹配的部分。如果目标是“/www/web/index.html”,那么它将与“%.html”相匹配,“%”为“/www/web/index”,它将与“$(OUT)/%”匹配,“%”为“index.html”。因此规则#2是较短的干部分。 - MadScientist
1
如果你想让这个makefile在<=3.81和>=3.82版本中表现一致,你需要先放置规则#2,然后它将对所有版本都起作用。 - MadScientist
显示剩余2条评论
2个回答

2
来自 GNU Makefile 手册的一段引用

有可能有多于一个模式规则符合这些条件。在这种情况下,make 会选择具有最短词干(即,最精确匹配的模式)的规则。如果有多个模式规则使用了最短的词干,则 make 会选择在 makefile 中找到的第一个。

因此,你可以尝试创建确保较短词干优先的规则。或者,你可以使用 静态模式规则 来限制复制到哪里以及什么范围内的内容。
%.html: %.in
      build_html $@ $<

$(expected_out) : (OBJS)/% : %
      cp $@ $<

然后,预先填充$(expected_out)中想要的内容。最后,您可以添加:
$(OUT)/index.html : index.html

在您的Makefile中,由于make更喜欢“最短路径”来构建对象,因此在这种情况下只需要一个模式规则。

静态模式规则似乎在这里非常有帮助。我认为你扩展词干长度的方法是错误的:词干只是与“%”匹配的部分,在每种情况下都相同。 - Calvin
好的,移除黑客代码。我刚试了一下,make会选择最短的词干作为要运行的第一个规则,而不是在规则链上运行的最长词干。 - blackghost

1

虽然@John的答案最适合我的用例(我知道哪些文件属于$(OUT)),但也有另一种选择:将期望的中间文件标记为“precious”。

.PRECIOUS: index.html

这也将指示Make不删除index.html,否则它会为您执行此操作。
这得益于Make选择隐式规则的算法。 Make偏爱其依赖项存在或应该存在的规则,并且如果文件具有显式规则或是另一个规则的依赖项,则该文件“应该存在”。即使它是特殊目标(如.SECONDARY.INTERMEDIATE.PRECIOUS)的依赖项也是如此。更多信息,请参见手册中关于“隐式规则链”的部分

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接