我有一个由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通常感觉很自然,对我有帮助。
我的问题是;有没有更好的办法?有没有我考虑过的边缘情况?我正在做什么危险?
答案 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
增量。例如,只有1
和2
过期时才会显示:
$ 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功能。
有几个原因:
我认为您使用的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的解决方案,因此这个理由不会对您产生很大影响。
另外,以这种方式拼写逻辑似乎更清楚:它意味着每个规则更加独立,而您使用的+=
设备意味着这两条规则以不明显的方式相互作用。
这两点意味着我对复杂动作有相当高的容忍度,并且对复杂的Make语法的容忍度低 - 你的口味可能会有所不同。
关键点:您无法摆脱某种类型的此类并发症。 Make基本上是一次更新一个文件。在(合理地)希望以这种方式崩溃更新,你不可避免地颠覆了Make的工作模式:我们不能期望这是自然的。也就是说,我不认为你或我错过了一个技巧。