寻找记录良好的Make输出

时间:2013-05-10 18:24:15

标签: makefile workflow data-processing

更短的问题:

让目标将文件作为依赖项;假设一个示例依赖是文件“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

假设正在运行fantasymakeresults2失败或被终止。 (并且假设我们没有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

通过浏览维基百科(构建自动化软件列表),看起来results2makeppomake都没有。该页面上的列表(我缺乏连接的声誉)有点冗长,所以我转向这个已经帮助过我很多次的可爱人群。

这是我必须一起破解的扩展,还是已经存在?

2 个答案:

答案 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)

根据您的确定程度,您可以更加简化这一点,但这仍然是读者的练习。