更短的问题:
让目标将文件作为依赖项;假设一个示例依赖是文件“D”。我希望Make遍历其依赖图,并且对于每个“D”,也依赖于成功记录在“D's”配方的退出状态(“D.status.log”;为简单起见,只包括进程退出状态或字符串“已启动”)。如果不自己深入研究Make的源代码并修改图形逻辑,这可能吗? (即有人已将此作为补丁或其他类似Make的实用程序编写?)
详细信息:
我很擅长使用Makefile来运行数据处理工作流程。我并不孤单,因为搜索“makefile数据”会产生一些志同道合的人:
然而,在实践中,我发现脖子上有一种光荣的痛苦。多步骤流程从不一定完成的程序生成输出。在数千个输入文件上运行多步骤工作流意味着将一些find ... rm
命令拼凑在一起,这感觉就像一个脆弱的数据管理策略。
基本上,我想要一个记录良好的Make for数据具有这种风格的界面:我将在下面称之为fantasymake
。
生成文件:
all: results1 results2
results1: script input1
script input1 >results1
results2: script input2
script input2 >results2
results2beyond: script results2
script results2 >results2beyond
之前的示例目录树:
Makefile
input1
input2
运行fantasymake
后的目录:
Makefile
input1
input2
results1
results1.err.log
results1.out.log
results1.status.log
results2
results2.err.log
results2.out.log
results2.status.log
results2beyond
results2beyond.err.log
results2beyond.out.log
results2beyond.status.log
目前,我可以使用这一点Bash获取日志,但是我没有找到一种优雅的方法将这些包装器命令集成到Makefile规则中:
echo Started. >results.status.log
some_program >results.out.log 2>results.err.log
echo $? >results.status.log
(回想一下Makefile定义中的每个非连接行都是一个单独的shell:in-Makefile包装器在some_program ...
和echo $$?
之间会有一个连续行(反斜杠),以确保它们是两者都在同一个shell中执行。)
回到fantasymake
行为,这将是运行fantasymake clean
之后的目录:
Makefile
input1
input2
假设正在运行fantasymake
,results2
失败或被终止。 (并且假设我们没有fantasymake clean
。)然后results2beyond
将不会生成;在这里,我认为我不能仅仅依赖未经修改的Make results2.status.log
日志results2
失败,因此fantasymake
无法继续到{{1在下一次调用时。
要完成构建,results2beyond
规则可以扫除错误的结果。如果你有一个更容易省去Make的数据库依赖(或实时连接),你可能需要这个。以下是运行clean-failed
而不是fantasymake clean-failed
后目录的样子:
fantasymake clean
假设在运行Makefile
input1
input2
results1
results1.err.log
results1.out.log
results1.status.log
后,fantasymake clean-failed
已更新。然后运行script
会重新生成fantasymake
及其日志results1
。
通过浏览维基百科(构建自动化软件列表),看起来results2
,makepp
或omake
都没有。该页面上的列表(我缺乏连接的声誉)有点冗长,所以我转向这个已经帮助过我很多次的可爱人群。
这是我必须一起破解的扩展,还是已经存在?
答案 0 :(得分:0)
对于包装器,如果你使用GNU make,这是微不足道的。只需使用用户定义的函数:
TARGETS = one two three
# Invoke this with $(call LOG,<cmdline>)
define LOG
echo "$$(date): Started." >'$@'.status.log
($1) >'$@'.out.log 2>'$@'.err.log
echo "$$(date): Completed: $$?" >>'$@'.status.log
endef
all: $(TARGETS)
$(TARGETS):
$(call LOG, echo "$@ out"; echo "$@ error" 1>&2)
我不确定你用“干净”的东西想要完成什么。如果您只想要一个目标clean-failed
,它将删除任何不存在的目标的日志,这很简单:
TARGETS = one two three
clean-failed:
for t in $(TARGETS); do [ -f "$$t" ] || rm -f "$$t".*.log; done
对我来说,其余的要求听起来就像标准制作功能一样。
答案 1 :(得分:0)
我认为你可以通过常规制作达到这个目标,你只需要更聪明地设置你的规则。具体而言,在确定结果文件完整且一致之前,请不要将结果文件放在适当的位置。像这样更改你的makefile:
all: results1 results2
results1: script input1
script input1 >results1.tmp && mv results1.tmp results1
results2: script input2
script input2 >results2.tmp && mv results2.tmp results2
results2beyond: script results2
script results2 >results2beyond.tmp && mv results2beyond.tmp results2beyond
现在,如果电源耗尽或者您的磁盘已经填满或类似,那么工作流程将在任何停止的地方进行拾取。保证存在的任何结果文件都是完整且一致的,因为除非前一个命令成功完成,否则shell不会执行mv
命令。
<强>更新强>
如果你正在使用GNU make,你可以稍微简化一下makefile:
PROCESS=script $< > $@.tmp && mv $@.tmp $@
all: results1 results2
results%: input% script
$(PROCESS)
results2beyond: results2 script
$(PROCESS)
根据您的确定程度,您可以更加简化这一点,但这仍然是读者的练习。