我想执行以下命令:

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

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


如果你使用。/a,下面就可以做到这一点。out,你就在一个unix类型的平台上。

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

测试如下:

target:
    for number in 1 2 3 4 ; do \
        echo $$number ; \
    done

生产:

1
2
3
4

对于更大的范围,使用:

target:
    number=1 ; while [[ $$number -le 10 ]] ; do \
        echo $$number ; \
        ((number = number + 1)) ; \
    done

这将输出1到10(包含10),只需将while终止条件从10更改为1000,以获得更大的范围,如您的注释所示。

嵌套循环可以这样完成:

target:
    num1=1 ; while [[ $$num1 -le 4 ]] ; do \
        num2=1 ; while [[ $$num2 -le 3 ]] ; do \
            echo $$num1 $$num2 ; \
            ((num2 = num2 + 1)) ; \
        done ; \
        ((num1 = num1 + 1)) ; \
    done

生产:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
4 1
4 2
4 3

如果您正在使用GNU make,您可以尝试一下

NUMBERS = 1 2 3 4
doit:
        $(foreach var,$(NUMBERS),./a.out $(var);)

哪个将生成并执行

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

使用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。


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

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


我意识到这个问题已经是几年前的问题了,但是这篇文章可能仍然对某些人有用,因为它演示了一种不同于上面的方法,它既不依赖于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}

快乐的制作。


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

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

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

script.sh是这样的:

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

也许你可以用:

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

#I have a bunch of files that follow the naming convention
#soxfile1  soxfile1.o  soxfile1.sh   soxfile1.ini soxfile1.txt soxfile1.err
#soxfile2  soxfile2.o   soxfile2.sh  soxfile2.ini soxfile2.txt soxfile2.err
#sox...        ....        .....         ....         ....        ....
#in the makefile, only select the soxfile1.. soxfile2... to install dir
#My GNU makefile solution follows:
tgt=/usr/local/bin/          #need to use sudo
tgt2=/backup/myapplication/  #regular backup 

install:
        for var in $$(ls -f sox* | grep -v '\.' ) ; \
        do \
                sudo  cp -f $$var ${TGT} ;     \
                      cp -f  $$var ${TGT2} ;  \
        done


#The ls command selects all the soxfile* including the *.something
#The grep command rejects names with a dot in it, leaving  
#My desired executable files in a list. 

您可以使用set -e作为for循环的前缀。例子:

all:
    set -e; for a in 1 2 3; do /bin/false; echo $$a; done

Make将立即退出,退出代码<> 0。


尽管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

一个简单的、独立于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})

在循环中动态地分配变量

1 2 3 4中for数的问题;做…-solution是,在循环中没有变量可以赋值。$(eval VAR=…)只能在目标执行开始时已知赋值内容的情况下使用。如果赋值依赖于循环变量,VAR将为空。

为了避免这个问题,可以使用目标功能对循环建模。下面的例子从SRC / OBJ获取第n个文件,并将它们一起处理。使用这种结构,您甚至可以使用$(eval…)来处理循环变量,如VAR3所示。

makefile

SRC = f1.c f2.cpp f3.cpp
OBJ = f1.o f2.o f3.o

SRC2 = $(addsuffix _,$(SRC))
JOIN = $(join $(SRC2),$(OBJ))

PHONY: all
all : info loop

loop : $(JOIN)

$(JOIN) :
    @# LOOP - CONTENT
    @echo "TARGET: $@"
    $(eval VAR1=$(word 1,$(subst _, ,$@)))
    @echo "VAR1: "$(VAR1)
    $(eval VAR2=$(word 2,$(subst _, ,$@)))
    @echo "VAR2: "$(VAR2)
    $(eval VAR3=$(subst .o,.x,$(VAR2)))
    @echo "You can even substitute you loop variable VAR3: "$(VAR3)
    #g++ -o $(VAR2) $(VAR1)
    @echo

PHONY: info
info:
    @printf "\n"
    @echo "JOIN: "$(JOIN)
    @printf "\n"

输出

$ make

JOIN: f1.c_f1.o f2.cpp_f2.o f3.cpp_f3.o

TARGET: f1.c_f1.o
VAR1: f1.c
VAR2: f1.o
You can even substitute you loop variable VAR3: f1.x
#g++ -o f1.o f1.c

TARGET: f2.cpp_f2.o
VAR1: f2.cpp
VAR2: f2.o
You can even substitute you loop variable VAR3: f2.x
#g++ -o f2.o f2.cpp

TARGET: f3.cpp_f3.o
VAR1: f3.cpp
VAR2: f3.o
You can even substitute you loop variable VAR3: f3.x
#g++ -o f3.o f3.cpp

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


这招对我很管用:

NUM=4

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