我想执行以下命令:

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

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


当前回答

这个答案,就像@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标志进行并行化。

其他回答

也许你可以用:

xxx:
    for i in `seq 1 4`; do ./a.out $$i; done;

尽管GNUmake表工具包有一个真正的while循环(不管这在GNUmake编程中意味着什么,它有两个或三个执行阶段),如果需要的是一个迭代列表,有一个简单的解决方案,即interval。为了好玩,我们把数字也转换成十六进制:

include gmtt/gmtt.mk

# generate a list of 20 numbers, starting at 3 with an increment of 5
NUMBER_LIST := $(call interval,3,20,5)

# convert the numbers in hexadecimal (0x0 as first operand forces arithmetic result to hex) and strip '0x'
NUMBER_LIST_IN_HEX := $(foreach n,$(NUMBER_LIST),$(call lstrip,$(call add,0x0,$(n)),0x))

# finally create the filenames with a simple patsubst
FILE_LIST := $(patsubst %,./a%.out,$(NUMBER_LIST_IN_HEX))

$(info $(FILE_LIST))

输出:

./a3.out ./a8.out ./ad.out ./a12.out ./a17.out ./a1c.out ./a21.out ./a26.out ./a2b.out ./a30.out ./a35.out ./a3a.out ./a3f.out ./a44.out ./a49.out ./a4e.out ./a53.out ./a58.out ./a5d.out ./a62.out

这并不是对这个问题的纯粹回答,而是一种解决这类问题的聪明方法:

而不是写一个复杂的文件,简单地委托控制,例如一个bash脚本: makefile

foo : bar.cpp baz.h
    bash script.sh

script.sh是这样的:

for number in 1 2 3 4
do
    ./a.out $number
done

我意识到这个问题已经是几年前的问题了,但是这篇文章可能仍然对某些人有用,因为它演示了一种不同于上面的方法,它既不依赖于shell操作,也不需要开发人员提取硬编码的数值字符串。

$(eval ....)内置宏是你的朋友。或者至少可以这样。

define ITERATE
$(eval ITERATE_COUNT :=)\
$(if $(filter ${1},0),,\
  $(call ITERATE_DO,${1},${2})\
)
endef

define ITERATE_DO
$(if $(word ${1}, ${ITERATE_COUNT}),,\
  $(eval ITERATE_COUNT+=.)\
  $(info ${2} $(words ${ITERATE_COUNT}))\
  $(call ITERATE_DO,${1},${2})\
)
endef

default:
  $(call ITERATE,5,somecmd)
  $(call ITERATE,0,nocmd)
  $(info $(call ITERATE,8,someothercmd)

That's a simplistic example. It won't scale pretty for large values -- it works, but as the ITERATE_COUNT string will increase by 2 characters (space and dot) for each iteration, as you get up into the thousands, it takes progressively longer to count the words. As written, it doesn't handle nested iteration (you'd need a separate iteration function and counter to do so). This is purely gnu make, no shell requirement (though obviously the OP was looking to run a program each time -- here, I'm merely displaying a message). The if within ITERATE is intended to catch the value 0, because $(word...) will error out otherwise.

注意,使用增长字符串作为计数器是因为内置的$(words…)可以提供阿拉伯计数,但make不支持数学运算(你不能将1+1赋值给某物并得到2,除非你从shell中调用某物来为你完成它,或者使用同样复杂的宏操作)。这适用于INCREMENTAL计数器,但不适用于DECREMENT计数器。

I don't use this myself, but recently, I had need to write a recursive function to evaluate library dependencies across a multi-binary, multi-library build environment where you need to know to bring in OTHER libraries when you include some library which itself has other dependencies (some of which vary depending on build parameters), and I use an $(eval) and counter method similar to the above (in my case, the counter is used to ensure we don't somehow go into an endless loop, and also as a diagnostic to report how much iteration was necessary).

Something else worth nothing, though not significant to the OP's Q: $(eval...) provides a method to circumvent make's internal abhorrence to circular references, which is all good and fine to enforce when a variable is a macro type (intialized with =), versus an immediate assignment (initialized with :=). There are times you want to be able to use a variable within its own assignment, and $(eval...) will enable you to do that. The important thing to consider here is that at the time you run the eval, the variable gets resolved, and that part which is resolved is no longer treated as a macro. If you know what you're doing and you're trying to use a variable on the RHS of an assignment to itself, this is generally what you want to happen anyway.

  SOMESTRING = foo

  # will error.  Comment out and re-run
  SOMESTRING = pre-${SOMESTRING}

  # works
  $(eval SOMESTRING = pre${SOMESTRING}

default:
  @echo ${SOMESTRING}

快乐的制作。

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

例如,如果你在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 =;