我正在使用make来自动化我的一些数据分析。我有几个目录,每个目录都包含了数据的不同实现,它由几个文件组成,代表了数据在某个时间的状态,像这样。
├── a
│ ├── time_01.dat
│ ├── time_02.dat
│ ├── time_03.dat
│ └── ...
├── b
│ ├── time_01.dat
│ └── ...
├── c
│ ├── time_01.dat
│ └── ...
├── ...
每个目录中的数据文件数量是未知的,随时可以添加更多的数据文件。每个目录中的文件都有相同的命名规则。
我想用make在每个目录中运行一套完全相同的配方(分别统一分析每个数据集)。特别是,有一个脚本应该在添加新数据文件时随时运行,并创建一个输出文件(analysis_time_XX.txt
),对目录中的每个数据文件进行更新。这个脚本不会更新之前创建的任何文件,但会创建所有缺失的文件。不幸的是,重构这个脚本是不可能的。
所以我有一个配方创建了许多目标,但它必须为每个目录分别运行。我找到的解决方案是用一个配方创建多个目标(例如 在这里,我找到的解决方案是用一个配方创建多个目标(例如)在我的情况下不起作用,因为我需要一条规则来分别对不同目录下的多组文件进行处理。
这些中间文件本身是需要的(因为它们有助于验证所收集的数据),但也用于创建数据集之间的最终比较图。
我目前的设置是一个丑陋的函数和程序的组合。.SECONDEXPANSION
dirs = a b c
datafiles = $(foreach dir,$(dirs),$(wildcard $(dir)/*.dat))
df_to_analysis = $(subst .dat,.txt,$(subst time_,analysis_time_,$(1)))
analysis_to_df = $(subst .txt,.dat,$(subst analysis_time_,time_,$(1)))
analysis_files = $(foreach df,$(datafiles),$(call df_to_analysis,$(df)))
all: final_analysis_plot.png
.SECONDEXPANSION:
$(analysis_files): %: $$(call analysis_to_df,%)
python script.py $(dir $@)
final_analysis_plot.png: $(analysis_files)
python make_plot.py $(analysis_files)
请注意 script.py
创建所有的 analysis_time_XX.txt
文件的目录中。这种设置的缺陷是,make不知道第一个脚本会生成所有的目标,所以在使用并行make时,会不必要地运行。对于我的应用来说,并行make是必要的,因为这些脚本的运行时间很长,并行化可以节省很多时间,因为这种设置是 "尴尬的并行"。
有没有一种优雅的方法来解决这个问题?甚至有优雅的方法来清理我现在的代码吗?我在这里展示了一个简单的例子,这已经需要很好的设置,对几个不同的脚本进行这样的设置很快就会变得笨重。
我想,在你的情况下,没有必要去费心地使用 .txt
文件。如果 script.py
是比较好的,可以按文件工作,那么编写单个文件规则就有价值了。在这种情况下,我们需要引入一个中间的每目录的 .done
文件。
DATA_DIRS := a b c
# A directory/.done.analysis file means that `script.py` was run here.
DONE_FILES := $(DATA_DIRS:%=%/*.done.analysis)
# .done.analysis depends on all the source data files.
# When a .dat file is added or changes, it will be newer than
# a .done.analysis file; and the analysis would be re-run.
$(DONE_FILES): %/.done.analysis: $(wildcard %/*.dat)
python script.py $(@D)
final_analysis_plot.png: $(DONE_FILES)
python make_plot.py $(wildcard $(DATA_DIRS)/analysis_time_*.txt)