我想执行以下命令:

./a.out 1
./a.out 2
./a.out 3
./a.out 4
.
.
. and so on

如何写这个东西作为一个循环在Makefile?


当前回答

这招对我很管用:

NUM=4

a-out:
    for (( i=1; i<=${NUM}; i++ )) \
    do \
        ./a.out $$i ; \
    done

其他回答

使用make的主要原因是-j标志。Make -j5将同时运行5个shell命令。如果你有4个cpu,这是一个很好的makefile测试。

基本上,你想看到的是:

.PHONY: all
all: job1 job2 job3

.PHONY: job1
job1: ; ./a.out 1

.PHONY: job2
job2: ; ./a.out 2

.PHONY: job3
job3: ; ./a.out 3

这是-j友好的(一个好迹象)。你能认出那个锅炉盘吗?我们可以写成:

.PHONY: all job1 job2 job3
all: job1 job2 job3
job1 job2 job3: job%:
    ./a.out $*

为了达到同样的效果(是的,就make而言,这与前面的公式相同,只是更紧凑一点)。

进一步的参数化,这样你就可以在命令行上指定一个限制(因为make没有任何好的算术宏,所以我在这里作弊,使用$(shell…)))

LAST := 1000
NUMBERS := $(shell seq 1 ${LAST})
JOBS := $(addprefix job,${NUMBERS})
.PHONY: all ${JOBS}
all: ${JOBS} ; echo "$@ success"
${JOBS}: job%: ; ./a.out $*

您可以使用make -j5 LAST=550运行该命令,其中LAST默认为1000。

一个简单的、独立于shell/平台的纯宏解决方案是…

# GNU make (`gmake`) compatible; ref: <https://www.gnu.org/software/make/manual>
define EOL

$()
endef
%sequence = $(if $(word ${1},${2}),$(wordlist 1,${1},${2}),$(call %sequence,${1},${2} $(words _ ${2})))

.PHONY: target
target:
    $(foreach i,$(call %sequence,10),./a.out ${i}${EOL})

为了跨平台支持,让命令分隔符(用于在同一行上执行多个命令)成为可配置的。

例如,如果你在Windows平台上使用MinGW,命令分隔符是&:

NUMBERS = 1 2 3 4
CMDSEP = &
doit:
    $(foreach number,$(NUMBERS),./a.out $(number) $(CMDSEP))

这将在一行中执行连接的命令:

./a.out 1 & ./a.out 2 & ./a.out 3 & ./a.out 4 &

正如前面提到的,在*nix平台上使用CMDSEP =;

这招对我很管用:

NUM=4

a-out:
    for (( i=1; i<=${NUM}; i++ )) \
    do \
        ./a.out $$i ; \
    done

这个答案,就像@Vroomfondel的答案一样,旨在以优雅的方式规避循环问题。

我的想法是让make生成循环本身作为一个导入的makefile,就像这样:

include Loop.mk
Loop.mk:Loop.sh
     Loop.sh > $@

shell脚本可以像你喜欢的那样高级,但可以是一个最小的工作示例

#!/bin/bash
LoopTargets=""
NoTargest=5
for Target in `seq $NoTargest` ; do
    File="target_${Target}.dat"
    echo $File:data_script.sh
    echo $'\t'./data_script.ss $Target
    LoopTargets="$LoopTargets $File"
done
echo;echo;echo LoopTargets:=$LoopTargets

生成文件

target_1.dat:data_script.sh
    ./data_script.ss 1
target_2.dat:data_script.sh
    ./data_script.ss 2
target_3.dat:data_script.sh
    ./data_script.ss 3
target_4.dat:data_script.sh
    ./data_script.ss 4
target_5.dat:data_script.sh
    ./data_script.ss 5


LoopTargets:= target_1.dat target_2.dat target_3.dat target_4.dat target_5.dat

这样做的好处是make本身可以跟踪已经生成的文件以及需要(重新)生成的文件。同样,这也允许make使用-j标志进行并行化。