Makefile 如何使GNU make成批运行?

Makefile 如何使GNU make成批运行?,makefile,gnu-make,Makefile,Gnu Make,我想使用make来处理大量的输入到输出,比如说,使用python脚本。问题在于,脚本在每个输入上运行的时间非常短,但是初始化需要一段时间,即python引擎+库初始化。因此,只有输入->输出规则的简单makefile最终会被这个初始化时间所控制。并行性对此没有帮助 python脚本可以接受多个输入和输出,因此: python my_进程-i in1-o out1-i in2-o out2 这是使用脚本的推荐方法 如何通过成批发送过时的输入输出对来创建一个最能使用我的_进程的Makefile规则?

我想使用make来处理大量的输入到输出,比如说,使用python脚本。问题在于,脚本在每个输入上运行的时间非常短,但是初始化需要一段时间,即python引擎+库初始化。因此,只有输入->输出规则的简单makefile最终会被这个初始化时间所控制。并行性对此没有帮助

python脚本可以接受多个输入和输出,因此:

python my_进程-i in1-o out1-i in2-o out2

这是使用脚本的推荐方法

如何通过成批发送过时的输入输出对来创建一个最能使用我的_进程的Makefile规则?类似于并行,但知道哪些输出已过时


如果可能的话,我宁愿避免递归make。

我不完全理解您的问题:您真的希望make成批运行,还是希望一种永久的make进程在运行时检查文件系统,并在必要时向Python进程提供数据?如果是后者,这与批处理模式相反,而与管道相反

对于批处理模式,需要一个虚拟文件来记录最后的运行时间。在这种情况下,我们滥用make for,因为makefile在这一部分中是一个单技巧的小马,它不直观,并且违反了良好的规则:

SOURCES := $(wildcard in*)                                                                                                                                                                                                                                                                                                                                                                                                                                                                
lastrun : $(SOURCES)
        python my_process $(foreach src,$?,-i $(src) -o $(patsubst in%,out%,$(src)))
        touch lastrun                                                                                                                                                                                                                        

PS:请注意,此解决方案有一个重大缺陷,即在运行makefile时,它不会检测到in文件的更新。总而言之,更可取的做法是简单地收集由更新过程本身更新的in文件的文件名,并避免生成替代文件。

我不完全理解您的问题:您真的希望make成批运行,还是希望一种永久的make过程在运行中检查文件系统并将其传送到服务器Python进程,只要它认为有必要?如果是后者,这与批处理模式相反,而与管道相反

对于批处理模式,需要一个虚拟文件来记录最后的运行时间。在这种情况下,我们滥用make for,因为makefile在这一部分中是一个单技巧的小马,它不直观,并且违反了良好的规则:

SOURCES := $(wildcard in*)                                                                                                                                                                                                                                                                                                                                                                                                                                                                
lastrun : $(SOURCES)
        python my_process $(foreach src,$?,-i $(src) -o $(patsubst in%,out%,$(src)))
        touch lastrun                                                                                                                                                                                                                        

PS:请注意,此解决方案有一个重大缺陷,即在运行makefile时,它不会检测到in文件的更新。总之,更可取的做法是简单地收集由更新过程本身更新的in文件的文件名,并避免生成althogether。

这就是我最终使用的,一个带有一层递归的makefile

我试着用$?具有分组和非分组目标,但无法获得所需的确切行为。如果删除了其中一个输出目标,则将重新运行该规则,但$?不一定有一些输入文件,但不是正确的对应输入文件,很奇怪

生成文件:

all:

INDIR=in
OUTDIR=out

INFILES=$(wildcard in/*)
OUTFILES=$(patsubst in/%, out/%, $(INFILES))

ifdef FIRST_PASS
#Discover which input-output pairs are out of date
$(shell mkdir -p $(OUTDIR); echo -n > $(OUTDIR)/.needs_rebuild)
$(OUTFILES) : out/% : in/%
    @echo $@ $^ >> $(OUTDIR)/.needs_rebuild

all: $(OUTFILES)
    @echo -n
else
#Recurse to run FIRST_PASS, builds .needs_rebuild:
$(shell $(MAKE) -f $(CURDIR)/$(firstword $(MAKEFILE_LIST)) FIRST_PASS=1)
#Convert .needs_rebuild into batches, creates all_batches phony target for convenience
$(shell cat $(OUTDIR)/.needs_rebuild | ./make_batches.sh 32 > $(OUTDIR)/.batches)
-include $(OUTDIR)/.batches

batch%:
    #In this rule, $^ is all inputs needing rebuild.
    #The corresponding utputs can be computed using a patsubst:
    targets="$(patsubst in/%, out/%, $^)"; touch $$targets

clean:
    rm -rf $(OUTDIR)

all: all_batches

endif
make_batches.sh:

#!/bin/bash
set -beEu -o pipefail

batch_size=$1

function _make_batches {
    batch_num=$1
    shift 1
    #echo ".PHONY: batch$batch_num"
    echo "all_batches: batch$batch_num"
    while (( $# >= 1 )); do
        read out in <<< $1
        shift 1
        echo "batch$batch_num: $in"
        echo "$out: batch$batch_num"
    done
}
export -f _make_batches

echo ".PHONY: all_batches"

parallel -N$batch_size -- _make_batches {#} {} \;

不幸的是,makefile是一个单技巧的小马,并且有相当多的样板文件来完成这个配方。

这就是我最终得到的,一个带有一层递归的makefile

我试着用$?具有分组和非分组目标,但无法获得所需的确切行为。如果删除了其中一个输出目标,则将重新运行该规则,但$?不一定有一些输入文件,但不是正确的对应输入文件,很奇怪

生成文件:

all:

INDIR=in
OUTDIR=out

INFILES=$(wildcard in/*)
OUTFILES=$(patsubst in/%, out/%, $(INFILES))

ifdef FIRST_PASS
#Discover which input-output pairs are out of date
$(shell mkdir -p $(OUTDIR); echo -n > $(OUTDIR)/.needs_rebuild)
$(OUTFILES) : out/% : in/%
    @echo $@ $^ >> $(OUTDIR)/.needs_rebuild

all: $(OUTFILES)
    @echo -n
else
#Recurse to run FIRST_PASS, builds .needs_rebuild:
$(shell $(MAKE) -f $(CURDIR)/$(firstword $(MAKEFILE_LIST)) FIRST_PASS=1)
#Convert .needs_rebuild into batches, creates all_batches phony target for convenience
$(shell cat $(OUTDIR)/.needs_rebuild | ./make_batches.sh 32 > $(OUTDIR)/.batches)
-include $(OUTDIR)/.batches

batch%:
    #In this rule, $^ is all inputs needing rebuild.
    #The corresponding utputs can be computed using a patsubst:
    targets="$(patsubst in/%, out/%, $^)"; touch $$targets

clean:
    rm -rf $(OUTDIR)

all: all_batches

endif
make_batches.sh:

#!/bin/bash
set -beEu -o pipefail

batch_size=$1

function _make_batches {
    batch_num=$1
    shift 1
    #echo ".PHONY: batch$batch_num"
    echo "all_batches: batch$batch_num"
    while (( $# >= 1 )); do
        read out in <<< $1
        shift 1
        echo "batch$batch_num: $in"
        echo "$out: batch$batch_num"
    done
}
export -f _make_batches

echo ".PHONY: all_batches"

parallel -N$batch_size -- _make_batches {#} {} \;

不幸的是,makefile是一个单技巧的小马,而且有相当多的样板文件来完成这个配方。

听起来像是针对分组目标的工作。我自己无法尝试,因为发行版被旧版本的GNU Make卡住了@安德烈亚斯:不,分组目标在这里没有帮助。首先,分组目标目前无法使用模式规则,我认为这是bcs。这将是一种非常不寻常的模式,其中需要N:N:1三重关系,其次,您不会从分组目标中得到需要更新的目标列表,至少不会使用通常的特殊变量。@Vromfondel是的,模式不起作用。我的想法与你的答案非常相似,在源代码上使用patsubst来获取文件,而不是占位符目标lastrun。听起来像是针对分组目标的作业。我自己无法尝试,因为发行版被旧版本的GNU Make卡住了@安德烈亚斯:不,分组目标在这里没有帮助。首先,分组目标目前无法使用模式规则,我认为这是bcs。这将是一种非常不寻常的模式,其中需要N:N:1三重关系,其次,您不会从分组目标中得到需要更新的目标列表,至少不会使用通常的特殊变量。@Vromfondel是的,模式不起作用。我的想法和你的答案很相似,在资料来源上加了一个patsubst来把文件拿出来
她比占位符目标lastrun更高。这似乎会对所有输入运行一次my_进程,这不会利用make的并行性,而且如果任何输入发生更改,这将对所有输入重新运行my_进程。抱歉,忘记将$SOURCES替换为$?。现在它只重建更改的文件。为了实现平衡的并行性,例如,由于有那么多进程作为核心,有必要动态创建lastrunX目标,并将其先决条件列表分开,希望make在并行化方面做得足够好。尽管我完全承认一个更好的构建系统应该不会对您的需求产生任何问题,但总体而言,make看起来似乎是一个错误的工具。我会将您的示例添加到我正在秘密构建的make继任者的功能列表中,谢谢;包括$?:比目标更新的所有先决条件的名称,它们之间有空格。如果目标不存在,将包括所有先决条件。对于作为存档成员的先决条件,仅使用命名成员。请参阅存档这很有效,谢谢!这似乎会对所有输入运行一次my_进程,这不会利用make的并行性,而且如果任何输入发生更改,这将对所有输入重新运行my_进程。抱歉,忘记将$SOURCES替换为$?。现在它只重建更改的文件。为了实现平衡的并行性,例如,由于有那么多进程作为核心,有必要动态创建lastrunX目标,并将其先决条件列表分开,希望make在并行化方面做得足够好。尽管我完全承认一个更好的构建系统应该不会对您的需求产生任何问题,但总体而言,make看起来似乎是一个错误的工具。我会将您的示例添加到我正在秘密构建的make继任者的功能列表中,谢谢;包括$?:比目标更新的所有先决条件的名称,它们之间有空格。如果目标不存在,将包括所有先决条件。对于作为存档成员的先决条件,仅使用命名成员。请参阅存档这很有效,谢谢!离题:对于您的场景,这真的比让my_进程并行处理简单吗?例如,使用fork,每个输入/输出对有一个fork。Off-topic:对于您的场景,这真的比让my_进程并行化处理简单吗?例如,使用fork,每个输入/输出对有一个fork。