Makefile 寻找记录良好的Make输出

Makefile 寻找记录良好的Make输出,makefile,workflow,data-processing,Makefile,Workflow,Data Processing,简短问题: 使目标具有作为依赖项的文件;假设一个依赖关系的例子是文件“D”。我希望遍历它的依赖关系图,对于每个“D”,也取决于成功记录在“D”配方的退出状态的日志文件(“D.status.log”;为了简单起见,只包括进程退出状态或字符串“Started”)。如果不亲自深入Make的源代码并修改图形逻辑,这是可能的吗?(即,是否有人已将此作为补丁或其他类似于Make的实用程序编写?) 详细信息: 我是一个使用Makefiles来运行数据处理工作流的爱好者。我并不孤单,因为搜索“makefile数

简短问题:

使目标具有作为依赖项的文件;假设一个依赖关系的例子是文件“D”。我希望遍历它的依赖关系图,对于每个“D”,也取决于成功记录在“D”配方的退出状态的日志文件(“D.status.log”;为了简单起见,只包括进程退出状态或字符串“Started”)。如果不亲自深入Make的源代码并修改图形逻辑,这是可能的吗?(即,是否有人已将此作为补丁或其他类似于Make的实用程序编写?)

详细信息:

我是一个使用Makefiles来运行数据处理工作流的爱好者。我并不孤单,因为搜索“makefile数据”会产生一些志同道合的人:

然而,在实践中,我发现这是一个光荣的痛苦的脖子。多步骤流程从不一定完成的程序生成输出。在数千个输入文件上运行多步骤工作流意味着拼凑一些
find。。。rm
命令,这感觉像是一种脆弱的数据管理策略

基本上,我想要一个记录良好的Make for data,它具有这种风格的界面:我将在下面称之为
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:Makefile包装器中的
some\u program…
echo$$?
之间会有一个连续行(反斜杠),以确保它们都在同一shell中执行。)

回到
fantasymake
行为,这将是运行
fantasymake clean
后的目录:

Makefile
input1
input2
Makefile
input1
input2
results1
results1.err.log
results1.out.log
results1.status.log
假设运行
fantasymake
results2
失败或终止。(假设我们没有
fantasymakeclean
),则不会生成
results2beyond
;在这里,我不认为我可以仅仅依靠未修改的Make:
results2.status.log
记录
results2
失败的日志,因此
fantasymake
不会在下一次调用时继续执行
results2beyond

为了完成构建,一个
清除失败的
规则可以清除错误的结果。如果您有一个数据库依赖项(或实时连接),那么您可能需要它,而这个数据库依赖项(或实时连接)更容易忽略。以下是运行
fantasymakeclean失败
而不是
fantasymakeclean
后目录的外观:

Makefile
input1
input2
Makefile
input1
input2
results1
results1.err.log
results1.out.log
results1.status.log
假设在运行
fantasymake clean失败后,
脚本更新。然后运行
fantasymake
将重新生成
results1
及其日志和
results2

从维基百科(构建自动化软件列表)上看,似乎没有一个
makepp
omake
,或者
cmake
能做到这一点。该页面上的列表(我再也没有链接的名声了)有点长,所以我求助于这个可爱的人群,他们已经帮助我很多次了


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

对于包装器,如果使用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)

对我来说,您的其他需求听起来像是标准的make功能。

我认为您可以通过常规make来实现这一点,您只需在设置规则方面稍微聪明一点。具体地说,在确定结果文件的完整性和一致性之前,不要将其放在适当的位置。按如下方式更改生成文件:

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,则可以稍微简化make文件:

PROCESS=script $< > $@.tmp && mv $@.tmp $@
all: results1 results2
results%: input% script
    $(PROCESS)

results2beyond: results2 script
    $(PROCESS)
PROCESS=script$<>$@.tmp&&mv$@.tmp$@
全部:结果1结果2
结果%:输入%脚本
$(过程)
results2beyond:results2脚本
$(过程)

根据您的决心,您可能可以进一步简化此操作,但这是留给读者的一个练习。

这很接近,在创建日志这一点上肯定很好(不过,我忘了连接命令外壳和退出状态行,所以在我们编写时,日志总是以“0”结尾)。但是,我不清楚“.status.log”:用于检测中间作业何时失败。我会更新这个问题,现在更新了。我很想知道遍历依赖关系图时的钩子是否是标准make功能的另一部分,但我查找并没有发现任何迹象表明这是如此。正确的,您必须在单个语句中编写命令才能使用这样的函数。至于清洁行为,一种方法是将每个目标列为下一个目标的先决条件,但这将失去所有并行性。唯一的另一种方法就是在壳里做。谢谢你,疯狂的科学家。听起来你好像在投票支持这是一个实施项目?这几乎就是我想要的。“帮助制作”邮件列表上的某个人认为这就是我问题的答案;所以,我认为我的问题还没有问对。好吧,我希望现在问得更好。我对你的回答有一个问题,那就是它不够通用,不能像@MadScientist建议的那样封装在函数中。依赖关系图将以大量的样板文件结束,这意味着所有这些额外代码都有一个编程解决方案。我是你的两个a