Make:如何在一次调用工具时处理许多输入文件?

时间:2015-06-02 11:20:31

标签: makefile gnu-make

我有一个由GNU make驱动的数据转换过程。它需要人工生成的输入文件并使用转换程序创建输出文件。显然这就像makefile一样简单:

inputs=$(wildcard *.input)
outputs=$(subst .input,.output, $(inputs))

.PHONY: all
all: $(outputs)    

%.output: %.input
    converter $< -o $@ 

它变得更加容易; converter知道输入文件中输出文件的位置,因此我们不需要$@

%.output: %.input
    converter $<

到目前为止,这么好。问题是converter与实际处理一个文件的时间相比需要很长时间才能启动。如果要处理的文件很多,则会浪费很多时间。我希望能够执行converter一次,传递需要转换的$(inputs) 的所有成员

我目前的技术是使用eval填充需要转换的所有输入文件的列表,并在以后的规则中处理该列表:

.PHONY: all
all: single_convert

.PHONY: single_convert
single_convert: $(outputs)
    converter $(newer_inputs)

%.output: %.input
    $(eval newer_inputs+=$<)

这感觉就像我正在与对抗 make,而makefile通常感觉很自然,对我有帮助。

我的问题是;有没有更好的办法?有没有我考虑过的边缘情况?我正在做什么危险?

2 个答案:

答案 0 :(得分:1)

您希望通过一次.output投放所有convert创建。实现此目的的一种方法是,每个.output文件都依赖于简单创建的.intermediate(比方说)文件。

.DELETE_ON_ERROR: # You always want this

inputs := $(wildcard *.input)
intermediates := ${inputs:.input=.intermediate}
outputs := ${inputs:.input=.output}

${outputs}: single_convert

single_convert: ${intermediates}
    convert ${?:.intermediate=.input}
    touch $@

${intermediates}: %.intermediate: %.input
    touch $@

${outputs}: single_convert

(参见手册中的Empty Target Files to Record Events。)

这很好用。从头开始:

$ touch 1.input 2.input 3.input
$ make
touch 2.intermediate
touch 1.intermediate
touch 3.intermediate
convert 2.input 1.input 3.input
touch single_convert

增量。例如,只有12过期时才会显示:

$ touch 1.input 2.input
$ make
touch 2.intermediate
touch 1.intermediate
convert 2.input 1.input
touch single_convert

但这有点像黑客。你撒谎 make ,从来不是一个好主意(如:你没有告诉 make 如何建立file.output说)。此外,这个公式排除了使用-j n 标志进行并行操作的可能性,这是 make 恕我直言的重点。

这个makefile更简单:

${outputs}: %.output: %.input
    convert $<

.PHONY: all
all: ${outputs}
如果你有8个CPU说:

,那么性能很好
$ make -j 9 all

答案 1 :(得分:0)

如果我遇到这种情况,我会做的事情如下:

<?php

//this is arbitrary

$category1 = array('prjct1', 'prjct2', 'prjct5', 'prjct8', 'prjct10', 'prjct11');
$category2 = array('prjct1', 'prjct5', 'prjct8', 'prjct9', 'prjct11', 'prjct10');
$category3 = array('prjct1', 'prjct4', 'prjct2', 'prjct9', 'prjct11', 'prjct5');
$category4 = array('prjct3', 'prjct7', 'prjct8', 'prjct9', 'prjct4', 'prjct6');
$category5 = array('prjct5', 'prjct6', 'prjct3', 'prjct7', 'prjct9', 'prjct4');
?>

这与你所做的完全相似,只是获取过时文件列表的步骤是在shell动作中完成的,而不是使用花哨的Make功能。

有几个原因:

  1. 我认为您使用的all: convert.stamp convert.stamp: $(inputs) rm convert.stamp x= ; \ for f in $(inputs:.input=); do \ test $$f.output -nt $$f.input || x="$$x $$f.input"; \ done; \ converter $$x touch convert.stamp 语法特定于GNU Make。从长期习惯来看,我尽量保持makefile尽可能的便携,并尽可能避免扩展(尽管我对此并不虔诚):+=语法不在{%.ext 3}},但仍然是常见和有用的,以至于不使用它是不正确的)。您已经暗示您很乐意拥有特定于GNU Make的解决方案,因此这个理由不会对您产生很大影响。

  2. 另外,以这种方式拼写逻辑似乎更清楚:它意味着每个规则更加独立,而您使用的+=设备意味着这两条规则以不明显的方式相互作用。

  3. 这两点意味着我对复杂动作有相当高的容忍度,并且对复杂的Make语法的容忍度低 - 你的口味可能会有所不同。

    关键点:您无法摆脱某种类型的此类并发症。 Make基本上是一次更新一个文件。在(合理地)希望以这种方式崩溃更新,你不可避免地颠覆了Make的工作模式:我们不能期望这是自然的。也就是说,我不认为你或我错过了一个技巧。