因为GNUmake允许变量与内存允许的一样大,所以构建大量依赖列表没有问题.但是,如果要在配方中实际使用这些文件列表(用于构建目标的shell命令序列),则会遇到问题:命令可能超出shell的命令行长度限制,从而产生错误,例如"参数"列表太长了".
例如,假设我想连接列表中包含的几个文件$(INPUTS)以生成文件combined.txt.通常,我可以使用:
combined.txt: $(INPUTS)
cat $^ > $@
Run Code Online (Sandbox Code Playgroud)
但是,如果$(INPUTS)包含数千个文件,就像在我的情况下那样,调用cat太长并且失败.有没有办法解决这个问题?可以安全地假设存在一些与一个巨大命令具有相同行为的命令序列 - 在这种情况下,一系列cat命令(每个输入文件一个)>>用于追加combined.txt将起作用.但是如何make说服生成这些命令呢?
j_r*_*ker 10
在寻找答案时,关于我能找到的最佳建议是将列表分解为一系列较小的列表并使用shell for循环处理它们.但是你不能总是这样做,即使你可以这样做也是一个混乱的黑客:例如,make一旦命令失败,如何获得通常的停止行为并不明显.幸运的是,经过大量的搜索和实验,结果证明存在一般解决方案.
make食谱为配方中的每一行调用一个单独的子shell.此行为可能令人烦恼且违反直觉:例如,cd一行上的命令不会影响后续命令,因为它们在单独的子shell中运行.然而,它实际上是我们需要make在非常长的文件列表上执行操作.
通常,如果使用常规变量赋值构建"多行"文件列表,该文件使用反斜杠来断开多行上的语句,则make删除所有换行符:
# The following two statements are equivalent
FILES := a b c
FILES := \
a \
b \
c
Run Code Online (Sandbox Code Playgroud)
但是,使用该define指令,可以构建包含换行符的变量值.更重要的是,如果你将这样的变量替换为一个配方,每一行确实会使用一个单独的子shell运行,所以例如make test从/home/jbloggs下面的makefile 运行(并假设没有文件调用test存在)将产生输出/home/jbloggs,因为效果所述的cd ..命令时,其子外壳结束丢失:
define CMDS
cd ..
pwd
endef
test:
$(CMDS)
Run Code Online (Sandbox Code Playgroud)
如果我们创建一个包含换行符的变量define,它可以像往常一样与其他文本连接,并使用所有常用make函数进行处理.结合$(foreach)功能,我们可以得到我们想要的东西:
# Just a single newline! Note 2 blank lines are needed.
define NL
endef
combined.txt: $(INPUTS)
rm $@
$(foreach f,$(INPUTS),cat $(f) >> $@$(NL))
Run Code Online (Sandbox Code Playgroud)
我们要求$(foreach)将每个文件名转换为以换行符结尾的命令,该命令将在其自己的子shell中执行.对于更复杂的需求,您可以使用一系列echo命令将文件名列表写入文件,然后使用xargs.
define指令被描述为可选服用=,:=或+=在第一行的末尾令牌,以确定哪些变量的味道是要创建-但请注意,这仅适用于GNU的版本make3.82和了!您可能正在运行流行的版本3.81,就像我一样,如果您添加其中一个令牌,它会默默地为变量赋值,从而导致很多挫败感.请看这里了解更多.