【问题标题】:GNU Makefile: multiple outputs from single rule + preventing intermediate files from being deletedGNU Makefile:单个规则的多个输出+防止中间文件被删除
【发布时间】:2023-03-03 03:37:01
【问题描述】:

这是here 的问题的延续。问题是有一个规则会从单个输入生成多个输出,并且该命令很耗时,因此我们希望避免重新计算。现在还有一个额外的转折,我们希望防止文件作为中间文件被删除,并且规则涉及通配符以允许参数。


建议的解决方案是我们设置以下规则:

file-a.out: program file.in
    ./program file.in file-a.out file-b.out file-c.out

file-b.out: file-a.out
    @

file-c.out: file-b.out
    @

然后,调用make file-c.out 会同时创建这两者,并且我们避免了与-j 开关并行运行make 的问题。到目前为止一切都很好。


问题如下。因为上面的方案在DAG中建立了一个链,make的考虑不同;文件file-a.outfile-b.out 被视为中间文件,默认情况下,一旦file-c.out 准备就绪,它们就会被删除,因为它们是不必要的。

这里提到了一种避免这种情况的方法,包括添加file-a.outfile-b.out 作为目标.SECONDARY 的依赖项,这样可以防止它们被删除。不幸的是,这并不能解决我的问题,因为我的规则使用通配符模式;具体来说,我的规则看起来更像这样:

file-a-%.out: program file.in
    ./program $* file.in file-a-$*.out file-b-$*.out file-c-$*.out

file-b-%.out: file-a-%.out
    @

file-c-%.out: file-b-%.out
    @

这样就可以传递包含在文件名中的参数,例如通过运行

make file-c-12.out

make 文档建议的解决方案是将这些作为隐式规则添加到 .PRECIOUS 的依赖项列表中,从而防止这些文件被删除。


.PRECIOUS 的解决方案有效,但它还可以防止在规则失败且文件不完整时删除这些文件。有没有其他方法可以完成这项工作?

解决这个问题的一个技巧是定义一个没有先决条件的目标.SECONDARY,即,

.SECONDARY:

它通知make 所有文件都应被视为次要文件,因此不会被删除,除非make 被中断或规则失败。不幸的是,这不允许选择带有通配符的规则子集以这种方式工作,所以我认为这只是一个 hack(尽管它很有用)。

【问题讨论】:

  • 嗯,那里的先决条件中没有词干(即您使用的是 file.in 而不是 file-%.in)。如果你从 file-$n.in 构建 file-$a-$n.out 会变得容易得多,而且它的方式很奇怪(make file-c-17.out file-c-18.out)会除非有像时间这样的隐藏输入源,否则产生具有不同名称的相同输出。规则可以改成file-a-%.out: program file-%.in 吗?
  • 这与手头的问题无关。这些只是组成的规则,关键是它们包含通配符。真正的规则要复杂得多,但这种复杂性会隐藏我面临的问题。
  • 查看我的答案,了解为什么它是相关的。
  • 不,因为file.in 不依赖于$* 的事实来自某个地方;这是有原因的。例如,file.in 保留所有常量参数,而 % 用于我想改变的东西。
  • 您是否尝试过辅助连接,而不是链。看到这个答案:stackoverflow.com/questions/2973445/…

标签: makefile wildcard gnu-make


【解决方案1】:

最简单的事情

file-a-%.out file-b-%.out file-c-%.out: program file.in
    ./program $* file.in file-a-$*.out file-b-$*.out file-c-$*.out

会做你想做的事。

(具有多个目标的模式规则不同于您所询问的具有多个目标的普通规则here。请参阅make manual 中的野牛示例。)

【讨论】:

    【解决方案2】:

    如果您的输出不是单个 file.in,而是从包含词干的先决条件文件生成的,即

    file-a.out: program file-%.in
        ./program file-$*.in file-a-$*.out file-b-$*.out file-c-$*.out
    

    然后您可以构建所有可能的目标匹配的列表:

    inputs = $(wildcard file-*.in)
    secondaries = $(patsubst file-%.in,file-a-%.out,$(inputs)) \
        $(patsubst file-%.in,file-b-%.out,$(inputs))
    

    类似地,如果词干来自有限集:

    batchnos = 17 18 19 20 
    batchnos = $(shell seq 17 20)
    secondaries = $(patsubst %,file-a-%.out,$(batchnos)) $(patsubst %,file-b-%.out,$(batchnos))
    

    然后将这些作为先决条件添加到 .SECONDARY 目标

    .SECONDARY: $(secondaries)
    

    【讨论】:

    • 这是一个可能的解决方案,但并不总是很实用。在我的情况下,离开.SECONDARY: 而不是列出所有可能性更简单。此外,它不需要将% 放在输入文件的名称中。您只需将所有可能的输出文件名称添加到辅助列表中。
    猜你喜欢
    • 1970-01-01
    • 2011-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-31
    • 1970-01-01
    相关资源
    最近更新 更多