因为GNU make
允许变量与内存允许的一样大,所以构建大量依赖列表没有问题。但是,如果要在配方中实际使用这些文件列表(用于构建目标的shell命令序列),则会遇到问题:命令可能超出 shell的命令行长度限制,产生错误,例如“参数列表太长”。
例如,假设我想连接列表$(INPUTS)
中包含的几个文件以生成文件combined.txt
。通常,我可以使用:
combined.txt: $(INPUTS)
cat $^ > $@
但如果$(INPUTS)
包含数千个文件,就像我的情况一样,对cat
的调用太长而且失败。有没有办法解决这个问题?可以安全地假设存在一些与一个庞大命令具有相同行为的命令序列 - 在这种情况下,一系列cat
命令,每个输入文件一个,使用>>
追加到combined.txt
会有效。但如何说服make
生成这些命令?
答案 0 :(得分:10)
在寻找答案时,我能找到的最佳建议是to break up the list into a series of smaller lists and process them using shell for
loops。但是你不能总是那样做,即使你可以这样做也是一个混乱的黑客:例如,一旦命令失败,如何获得通常的make
停止行为并不明显。幸运的是,经过大量的搜索和实验,结果证明存在一般解决方案。
make
食谱为食谱中的每一行调用一个单独的子shell。此行为可能令人烦恼且违反直觉:例如,一行上的cd
命令不会影响后续命令,因为它们在单独的子shell中运行。然而,实际上我们需要让make
对很长的文件列表执行操作。
通常,如果使用常规变量赋值构建“多行”文件列表,使用反斜杠将语句分成多行,make
将删除所有换行符:
# The following two statements are equivalent
FILES := a b c
FILES := \
a \
b \
c
但是,使用define
指令,可以构建包含换行符的变量值。更重要的是,如果将这样的变量替换为配方,每一行确实将使用单独的子shell运行,以便例如从make test
使用下面的makefile运行/home/jbloggs
(并假设没有调用文件) test
存在)将产生输出/home/jbloggs
,因为cd ..
命令的效果在其子shell结束时会丢失:
define CMDS
cd ..
pwd
endef
test:
$(CMDS)
如果我们使用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))
我们要求$(foreach)
将每个文件名转换为换行符终止命令,该命令将在其自己的子shell中执行。对于更复杂的需求,您可以使用一系列echo
命令将文件名列表写入文件,然后使用xargs
。
define
指令被描述为可选择在第一行的末尾加上=
,:=
或+=
令牌,以确定variable flavour是哪个要创建 - 但请注意,这仅适用于GNU make
3.82及更高版本!您可能正在运行流行的版本3.81,就像我一样,如果您添加其中一个令牌,它会默默地为变量赋值,从而导致很多挫败感。有关详情,请参阅here。