首页 文章

GNU Makefile规则从单个源文件生成一些目标

提问于
浏览
88

我试图做以下事情 . 有一个程序,称之为 foo-bin ,它接收一个输入文件并生成两个输出文件 . 一个愚蠢的Makefile规则是:

file-a.out file-b.out: input.in
    foo-bin input.in file-a.out file-b.out

但是,这并不能以任何方式告诉 make 两个目标将同时生成 . 在串行运行 make 时这很好,但如果尝试 make -j16 或同样疯狂的话,可能会造成麻烦 .

The question is whether there exists a way to write a proper Makefile rule for such a case? 显然,它会生成一个DAG,但不知何故GNU make手册没有说明如何处理这种情况 .

两次运行相同的代码并仅生成一个结果是不可能的,因为计算需要时间(想想:小时) . 仅输出一个文件也相当困难,因为它经常用作GNUPLOT的输入,GNUPLOT不知道如何只处理数据文件的一小部分 .

5 回答

  • 47

    我会解决如下:

    file-a.out: input.in
        foo-bin input.in file-a.out file-b.out   
    
    file-b.out: file-a.out
        #do nothing
        noop
    

    在这种情况下,并行make将'serialize'创建a和b但是因为创建b不会做任何事情,所以它会花费时间 .

  • 9

    诀窍是使用具有多个目标的模式规则 . 在这种情况下,make将假定两个目标都是通过单个调用命令创建的 .

    all: file-a.out file-b.out
    file-a%out file-b%out: input.in
        foo-bin input.in file-a$*out file-b$*out
    

    模式规则和正常规则之间的这种解释差异并不完全有意义,但它对于这样的情况很有用,并且在手册中有记录 .

    这个技巧可以用于任意数量的输出文件,只要它们的名称有一些共同的子字符串供 % 匹配 . (在这种情况下,公共子串是".")

  • 119

    Make没有任何直观的方法来做到这一点,但有两个不错的解决方法 .

    首先,如果涉及的目标具有共同的词干,则可以使用前缀规则(使用GNU make) . 也就是说,如果您想修复以下规则:

    object.out1 object.out2: object.input
        foo-bin object.input object.out1 object.out2
    

    你可以这样写:

    %.out1 %.out2: %.input
        foo-bin $*.input $*.out1 $*.out2
    

    (使用模式规则变量$ *,它代表模式的匹配部分)

    如果您希望可移植到非GNU Make实现,或者如果您的文件无法命名以匹配模式规则,则还有另一种方法:

    file-a.out file-b.out: input.in.intermediate ;
    
    .INTERMEDIATE: input.in.intermediate
    input.in.intermediate: input.in
        foo-bin input.in file-a.out file-b.out
    

    这告诉make在make运行之前不会存在input.in.intermediate,因此它的缺失(或它的时间戳)不会导致foo-bin虚假运行 . 无论file-a.out还是file-b.out或者两者都是过时的(相对于input.in),foo-bin只会运行一次 . 您可以使用.SECONDARY而不是.INTERMEDIATE,它将指示make NOT删除假设的文件名input.in.intermediate . 此方法对于并行make构建也是安全的 .

    第一行的分号很重要 . 它为该规则创建了一个空的配方,因此Make知道我们将真正更新file-a.out和file-b.out(感谢@siulkiulki和其他指出这一点的人)

  • 3

    这是基于@ deemer的第二个答案,它不依赖于模式规则,它修复了我遇到的嵌套使用变通方法时遇到的问题 .

    file-a.out file-b.out: input.in.intermediate
        @# Empty recipe to propagate "newness" from the intermediate to final targets
    
    .INTERMEDIATE: input.in.intermediate
    input.in.intermediate: input.in
        foo-bin input.in file-a.out file-b.out
    

    我会在@ deemer的答案中添加这个作为评论,但我不能,因为我刚创建了这个帐户并且没有任何声誉 .

    说明:需要空配方才能允许Make执行正确的簿记以将 file-a.outfile-b.out 标记为已重建 . 如果你有另一个依赖于 file-a.out 的中间目标,那么Make将选择不构建外部中间体,声称:

    No recipe for 'file-a.out' and no prerequisites actually changed.
    No need to remake target 'file-a.out'.
    
  • 5

    我就是这样做的 . 首先,我总是将预先请求与食谱分开 . 然后在这种情况下一个新的目标来做配方 .

    all: file-a.out file-b.out #first rule
    
    file-a.out file-b.out: input.in
    
    file-a.out file-b.out: dummy-a-and-b.out
    
    .SECONDARY:dummy-a-and-b.out
    dummy-a-and-b.out:
        echo creating: file-a.out file-b.out
        touch file-a.out file-b.out
    

    第一次:
    1.我们尝试构建file-a.out,但是dummy-a-and-b.out需要先做,所以make运行dummy-a-and-b.out配方 .
    2.我们尝试构建file-b.out,dummy-a-and-b.out是最新的 .

    第二次及以后的时间:
    1.我们尝试构建file-a.out:查看先决条件,正常的先决条件是最新的,缺少次要先决条件,因此被忽略 .
    2.我们尝试构建file-b.out:查看先决条件,正常的先决条件是最新的,缺少次要先决条件,因此被忽略 .

相关问题