0

较短的问题:

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

细节:

我热衷于使用 Makefile 来运行数据处理工作流。我并不孤单,因为搜索“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:一个 in-Makefile 包装器将在 和 之间有一个续行(反斜杠)some_program ...echo $$?以确保它们都在同一个 shell 中执行。)

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

Makefile
input1
input2

假设 running fantasymakeresults2failed 或被终止。(假设我们没有fantasymake clean。) Thenresults2beyond不会生成;在这里,我认为我不能只依赖未修改的 Make:失败的results2.status.log日志,因此不会继续进行下一次调用。results2fantasymake results2beyond

为了完成构建,clean-failed规则可以清除错误的结果。如果你有一个更容易从 Make 中排除的数据库依赖项(或实时连接),你可能需要这个。这是目录在运行后的样子,fantasymake clean-failed而不是fantasymake clean

Makefile
input1
input2
results1
results1.err.log
results1.out.log
results1.status.log

假设运行后fantasymake clean-failedscript更新。然后运行fantasymake将重新生成results1,并且它的日志并排在results2.

从 Wikipedia(构建自动化软件列表)一目了然,它看起来不像makepp, omake, 或cmake做的伎俩。该页面上的列表(我不再享有链接的声誉)有点长,所以我转向这个已经帮助我多次潜伏的可爱人群。

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

4

2 回答 2

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

现在,如果电源中断或磁盘已满或类似情况,工作流程将从中断处继续。mv任何存在的结果文件都保证是完整和一致的,因为除非前面的命令成功完成,否则shell 不会执行该命令。

更新:

如果您使用 GNU make,您可以稍微简化 makefile:

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

results2beyond: results2 script
    $(PROCESS)

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

于 2013-05-14T21:28:31.290 回答
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

对我来说,您的其他要求听起来像是标准的制造功能。

于 2013-05-10T18:54:47.557 回答