【问题标题】:make .SECONDEXPANSION recipe execute twice使 .SECONDEXPANSION 配方执行两次
【发布时间】:2019-05-23 21:18:09
【问题描述】:

使用 GNU make,我正在尝试解决类似于 make recipe execute twice 的问题 — 即让 Makefile 配方运行两次。然而,就我而言,配方在.SECONDEXPANSION 目标下运行,并且将使用不同的参数调用两次不同的运行,以从同一输入文件生成不同版本的输出文件。也就是说,对于输入文件foo,这个示例Makefile 应该可以通过make foo.pdfmake foo.expanded.pdf 调用来构建一个.pdf 文件,或者make all 来构建两个.pdf 文件:

.PHONY: all

all: foo.pdf foo.expanded.pdf

.SECONDEXPANSION:
%.expanded.pdf %.pdf: %
    @echo building $(basename $@)

在该答案中给出的两个解决方案中,第一个是不合适的,因为它总是运行规则两次;我希望它在用户请求时运行两次。

第二个解决方案在概念上是我正在寻找并在上面的示例 Makefile 中实现的,只有一个小问题它不起作用:尽管all 目标将两个 .pdf 文件都列为依赖项,运行make all 时只构建一个。

有没有办法告诉 GNU make 在 .SECONDEXPANSION 下使用相同的规则构建两个不同的文件?

编辑:在问题描述中澄清了相同的输入文件用于构建输出文件的两个版本,并修改了示例 Makefile 以包含此依赖项。

编辑:我想要一个尽可能可扩展的解决方案;也就是说,如果输入文件名包含点,它应该可以工作,指定额外的输出文件 foo.reduced.pdf 应该只需要适当地调整目标和配方等。这限制了执行依赖于文件名的字符串手术示例(例如,如果输入文件可能是 foofoo.bar,则将规则更改为 %.pdf: $$(firstword $$(subst ., ,$$*)) 失败)。

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    您的问题与.SECONDEXPANSION 无关。你可以放弃它,问题将是一样的。

    您的问题是您正在使用具有多个目标模式的模式规则,并期望它与具有多个目标的显式规则类似。但它没有(事实上你不能有一个既包含模式又包含显式目标的规则)。

    对于具有多个目标模式的模式规则,Make 将相同的模式匹配到所有%,包括目标中的多次,然后假设它只需要使用该模式执行一次配方,它会制作所有匹配的目标。

    在您的情况下,最好的方法是使用多个规则(我更改了您的配方,因为使用 echo 作为 Make 配方是一个坏主意):

    .PHONY: all
    all: foo.expanded.pdf foo.pdf 
    
    RECIPE = touch $@
    
    %.expanded.pdf:
        $(RECIPE)
    
    %.pdf:
        $(RECIPE)
    

    【讨论】:

    • 谢谢,马克。这将完成工作,但根据我的(来之不易的)经验,复制代码——即使复制的部分很简单(我的实际配方不是)——在接下来更新代码时会导致不一致(可能是忘记这个黑客的未来我)更改$(RECIPE) 的一个实例,但不更改另一个实例。如果添加其他变体.pdfs,这也很难扩展,每个变体都需要一个新的重复规则。任何需要注释警告未来编辑者重复代码的设计都是应该重构的,如果可能的话,以巩固重复。
    • 我的示例中的echo 仅用于说明目的,但我想知道为什么在食谱中使用它不好。
    • @Targeloid 你所说的“改变一个实例而不是另一个”是什么意思 - 这就是我使用的罐头配方,所以没有代码重复。关于echo - 在makefiles中,一个recipe应该总是写一个文件,即目标,这是一个很好的设计实践(如果你不相信我,你可以咨询Make的维护者Paul Smith)和Make已经打印配方行。即使纯粹出于测试目的,touch 总是比echo 更好。
    • @Targeloid 例如,如果您使用touch,您会看到第二次运行make 会得到不同的结果!这可能已经为您提供了问题的答案。
    【解决方案2】:

    您可能正在寻找Pattern-specific Variable Values。假设您的配方依赖于名为 BUILDFLAGS 的 make 变量,默认值为 normal,“expanded”目标为 special。然后这个 Makefile:

    BUILDER    := builder
    BUILDFLAGS := normal
    
    .PHONY: all
    
    all: foo.pdf foo.expanded.pdf
    
    %.expanded.pdf: BUILDFLAGS := special
    
    %.pdf:
        $(BUILDER) $(BUILDFLAGS) $@
    

    应该对所有目标使用相同的规则,加上一个特定于模式的变量值声明来满足您的需求。将 buildernormalspecial 替换为对您的情况有意义的内容。演示:

    $ make foo.pdf
    builder normal foo.pdf
    $ make foo.expanded.pdf
    builder special foo.expanded.pdf
    $ make
    builder normal foo.pdf
    builder special foo.expanded.pdf
    

    【讨论】:

    • 谢谢,您的回答完美地解决了我的问题,但不是我需要的。 :) 很抱歉——在将 Makefile 示例简化为最基本的部分时,我省略了一个重要的部分:foo.pdffoo.expanded.pdf 都依赖于同一个输入文件 foo。您的 %.pdf 规则适用于过于简单的示例,但添加 % 依赖项允许它构建 foo.pdf 但不能构建 foo.expanded.pdf 因为没有 foo.expanded 输入文件。我将编辑问题以包含此要求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 1970-01-01
    • 2017-04-27
    • 2011-11-30
    • 1970-01-01
    相关资源
    最近更新 更多