如何在Makefile中编写循环?
我想执行以下命令:
./a.out 1
./a.out 2
./a.out 3
./a.out 4
.
.
. and so on
如何在 Makefile
中将这个东西写为循环?
I want to execute the following commands:
./a.out 1
./a.out 2
./a.out 3
./a.out 4
.
.
. and so on
How to write this thing as a loop in a Makefile
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
如果您使用的是 UNIX 类型的平台(我假设您使用
./a.out
),则以下内容将执行此操作。测试如下:
产生:
对于更大的范围,使用:
这输出 1 到 10(包含在内),只需将
while
终止条件从 10 更改为 1000,以获得更大的范围,如评论中所示。嵌套循环可以这样完成:
产生:
The following will do it if, as I assume by your use of
./a.out
, you're on a UNIX-type platform.Test as follows:
produces:
For bigger ranges, use:
This outputs 1 through 10 inclusive, just change the
while
terminating condition from 10 to 1000 for a much larger range as indicated in your comment.Nested loops can be done thus:
producing:
如果您使用 GNU make,您可以尝试
生成并执行
If you're using GNU make, you could try
which will generate and execute
使用 make IMHO 的主要原因是
-j
标志。make -j5
将同时运行 5 个 shell 命令。如果您有 4 个 CPU,那么这很好,并且可以很好地测试任何 makefile。基本上,您希望 make 看到类似以下内容:
这是
-j
友好的(一个好兆头)。你能找到样板吗?我们可以这样写:达到相同的效果(是的,就 make 而言,这与之前的公式相同,只是更紧凑一点)。
进一步的参数化,以便您可以在命令行上指定限制(乏味,因为
make
没有任何好的算术宏,所以我会在这里作弊并使用$(shell ...)
)您可以使用
make -j5 LAST=550
运行此命令,LAST
默认为 1000。THE major reason to use make IMHO is the
-j
flag.make -j5
will run 5 shell commands at once. This is good if you have 4 CPUs say, and a good test of any makefile.Basically, you want make to see something like:
This is
-j
friendly (a good sign). Can you spot the boiler-plate? We could write:for the same effect (yes, this is the same as the previous formulation as far as make is concerned, just a bit more compact).
A further bit of parameterisation so that you can specify a limit on the command-line (tedious as
make
does not have any good arithmetic macros, so I'll cheat here and use$(shell ...)
)You run this with
make -j5 LAST=550
, withLAST
defaulting to 1000.我意识到这个问题已经有好几年了,但这篇文章可能仍然对某人有用,因为它演示了一种与上述不同的方法,并且不依赖于 shell 操作,也不需要开发人员删除硬编码数值字符串。
$(eval ....) 内置宏是你的朋友。或者至少可以是。
这是一个简单的例子。对于较大的值,它不会很好地缩放 - 它可以工作,但由于 ITERATE_COUNT 字符串每次迭代都会增加 2 个字符(空格和点),当您达到数千个时,计算单词所需的时间会逐渐更长。正如所写,它不处理嵌套迭代(您需要一个单独的迭代函数和计数器来执行此操作)。这纯粹是 gnu make,没有 shell 要求(尽管显然 OP 每次都希望运行一个程序——在这里,我只是显示一条消息)。 ITERATE 中的 if 旨在捕获值 0,因为否则 $(word...) 将出错。
请注意,使用不断增长的字符串作为计数器,因为 $(words...) 内置函数可以提供阿拉伯计数,但 make 不支持数学运算(您不能将 1+1 分配给某物并得到 2,除非您从 shell 调用某些内容来为您完成它,或者使用同样复杂的宏操作)。这对于增量计数器非常有效,但对于减量计数器则不太有效。
我自己不使用这个,但最近,我需要编写一个递归函数来评估多二进制、多库构建环境中的库依赖关系,在该环境中,当您包含某些库时,您需要知道引入其他库本身还有其他依赖项(其中一些依赖项取决于构建参数),我使用类似于上面的 $(eval) 和计数器方法(在我的例子中,计数器用于确保我们不会以某种方式陷入无休止的状态)循环,也可以作为诊断来报告需要多少迭代)。
其他东西毫无价值,尽管对OP的问题并不重要: $(eval...) 提供了一种方法来规避 make 内部对循环引用的厌恶,当变量是宏类型时(初始化为=),与立即赋值(用 := 初始化)。有时您希望能够在自己的赋值中使用变量,而 $(eval...) 将使您能够做到这一点。这里要考虑的重要一点是,在运行 eval 时,变量会被解析,并且解析的部分不再被视为宏。如果您知道自己在做什么,并且尝试在对其自身进行赋值的 RHS 上使用变量,那么这通常是您希望发生的情况。
快乐制作。
I realize the question is several years old, but this post may still be of use to someone as it demonstrates an approach which differs from the above, and isn't reliant upon either shell operations nor a need for the developer to schpeel out a hardcoded string of numeric values.
the $(eval ....) builtin macro is your friend. Or can be at least.
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.
Note that the growing string to serve as a counter is employed because the $(words...) builtin can provide an arabic count, but that make does not otherwise support math operations (You cannot assign 1+1 to something and get 2, unless you're invoking something from the shell to accomplish it for you, or using an equally convoluted macro operation). This works great for an INCREMENTAL counter, not so well for a DECREMENT one however.
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.
Happy make'ing.
为了跨平台支持,请使命令分隔符(用于在同一行上执行多个命令)可配置。
例如,如果您在 Windows 平台上使用 MinGW,则命令分隔符为
&
:这会在一行中执行串联的命令:
如其他地方所述,在 *nix 平台上使用
CMDSEP =;
。For cross-platform support, make the command separator (for executing multiple commands on the same line) configurable.
If you're using MinGW on a Windows platform for example, the command separator is
&
:This executes the concatenated commands in one line:
As mentioned elsewhere, on a *nix platform use
CMDSEP = ;
.也许你可以使用:
Maybe you can use:
这并不是问题的纯粹答案,而是解决此类问题的一种智能方法:
无需编写复杂的文件,只需将控制权委托给 bash 脚本,例如:
makefile
和 script.sh 如下所示:
This is not really a pure answer to the question, but an intelligent way to work around such problems:
instead of writing a complex file, simply delegate control to for instance a bash script like:
makefile
and script.sh looks like:
您可以使用
set -e
作为 for 循环的前缀。示例:make
将立即退出,退出代码为<> 0 。
You can use
set -e
as a prefix for the for-loop. Example:make
will exit immediately with an exit code<> 0
.尽管 GNUmake 表工具包有一个真正的
while
循环(无论这意味着什么) GNUmake 编程具有两个或三个执行阶段),如果需要的是迭代列表,则有一个使用interval
的简单解决方案。为了好玩,我们也将数字转换为十六进制:输出:
Although the GNUmake table toolkit has a true
while
loop (whatever that means in GNUmake programming with its two or three phases of execution), if the thing which is needed is an iterative list, there is a simple solution withinterval
. For the fun of it, we convert the numbers to hex too:Output:
在循环中动态分配变量
for number in 1 2 3 4 的问题; do \ ...
- 解决方案是,循环内不能分配变量。仅当在目标执行开始时已知赋值内容时,才能使用$(eval VAR=...)
。如果赋值取决于循环变量,VAR
将为空。为了避免这一问题,可以使用目标功能来模拟循环。以下示例从 SRC / OBJ 中获取第 n 个文件并将它们一起处理。使用此结构,您甚至可以使用
$(eval ...)
来处理循环变量,如VAR3
所示。生成文件
输出
Dynamically assign variables in the loop
The promblem with the
for number in 1 2 3 4 ; do \ ...
-solution is, that no variables can be assigned within the loop.$(eval VAR=...)
can only be used, if the content of the assignment is known at the beginning of the target execution. If the assignment depends on the loop-variable,VAR
will be empty.To circumvent this issue, one can use the target functionality to model a loop. The following example takes the n-th file from SRC / OBJ and processes them together. Using this construction you can even use
$(eval ...)
to process the loop variable, as demonstrated withVAR3
.makefile
output
一个简单的、独立于 shell/平台的纯宏解决方案是......
A simple, shell/platform-independent, pure macro solution is ...
这是动态范围的示例
使用
|| exit 1
在按下ctrl + c
时退出。它还将确保单个循环执行失败将退出。 (在这个简单的示例中,这没有用。但如果您运行的命令可能会失败,这会很有帮助)。
使用
RUN_COUNT=10 make
调用示例输出:
Here is an example with dynamic range
Use
|| exit 1
to exit whenctrl + c
is pressed. It will also make sure that a single loop execution failurewill exit. (In this trivial example, this is not useful. But if you run a command that can fail, this is helpful).
Invoke using
RUN_COUNT=10 make
Sample output:
这个答案,就像 @Vroomfondel 的答案一样,旨在以一种优雅的方式规避循环问题。
我的做法是让
make
生成循环本身作为导入的 makefile,如下所示:shell 脚本可以像您喜欢的那样先进,但可以是一个
生成文件的
最小工作示例,优点是make 本身可以跟踪哪些文件已生成以及哪些文件需要(重新)生成。因此,这还使
make
能够使用-j
标志进行并行化。This answer, just as that of @Vroomfondel aims to circumvent the loop problem in an elegant way.
My take is to let
make
generate the loop itself as an imported makefile like this:The shell script can the be as advanced as you like but a minimal working example could be
which generates the file
And advantage there is that
make
can itself keep track of which files have been generated and which ones need to be (re)generated. As such, this also enablesmake
to use the-j
flag for parallelization.这对我有用:
This worked for me:
版本
代码
测试目录
Version
Code
Test Directory
Test CMD & output
Reference