【发布时间】:2018-11-15 18:16:17
【问题描述】:
我正在尝试重现仅在某些机器上发生的情况。为了重现它,我创建了一个包含 2000 个文件的目录:
mkdir /tmp/test
cd /tmp/test
for f in $(seq 1 2000); do touch $f.txt; done
然后我使用下面的 Makefile(从真实用例中简化):
FILES:=$(shell find . -name '*.txt')
%.done: %.txt
@echo "done $@"
toolong:
@$(foreach file,$(sort $(FILES)), \
if $(MAKE) $(file); \
then echo "did $(file)" >> $@; \
else echo "failed $(file)" >> $@; fi; )
运行 make 不出所料地产生错误:
make: execvp: /bin/sh: Argument list too long
This question 提出了一个可行的解决方案。但是,我需要确切了解为什么此错误不会在我同事的计算机上发生。我尝试了以下方法:
- 增加堆栈限制(
ulimit -s在两台机器上给出相同的结果,8192,增加它不会改变任何东西); - 正在检查
getconf ARG_MAX(两台机器上都是2097152); - 检查
MAX_ARG_STRLEN(两台机器上都是131072); - 使用不同的外壳(
zsh在两台机器上都使用;我还尝试了bash、dash和sh,通过export SHELL=<shell> make,以及用链接替换符号链接/bin/sh -> /bin/bash到dash)。
最后,我尝试从源代码重新编译 Make,并意识到,即使我在我的 Ubuntu 测试机器上编译相同版本的 Make (4.1),我得到的行为与我在 Fedora 中的行为相同,即错误“参数列表太长”。
make --version 只显示它们之间的一个区别:
来自apt 包的版本:
GNU Make 4.1 为 x86_64-pc-linux-gnu
构建
从源代码编译的版本 (./configure && make):
GNU Make 4.1 为 x86_64-unknown-linux-gnu
构建
我什至尝试编译 make-dfsg,结果相同 make --version,但结果与我的其他手动编译的 make 相同。
通过增加 Ubuntu 上的文件数量,我设法确定生成的命令行大小的实际限制是:
- Fedora 或 Arch Linux(均使用 Make 4.2.1)或使用手动编译的 Make 4.1 的 Ubuntu:128 KB(约 1200 个文件);
- Debian Sid 或 Ubuntu,均从
apt包安装了 Make 4.1:2 MB(~19300 个文件)。
我真的很想了解 (1) 为什么存在这种差异,以及 (2) 我如何编译 Make 以获得更高的限制,以便我可以在两台机器上拥有完全相同的行为。
【问题讨论】:
-
你检查环境长度了吗,似乎限制超过了环境+参数长度
-
您的简化 Makefile 片段不包含任何 /bin/sh 调用,仅包含 gmake 内部函数。 arg列表的行到底在哪里太长了?除此之外,使用最小的环境运行您的 make,例如
env -i HOME=$HOME PATH=$PATH gmake ...以排除环境大小是罪魁祸首。 -
@Jens:实际的 makefile 确实会调用其他命令,但这是我得到的最简单的版本,它会产生与原始 makefile 中完全相同的错误消息。如果您有一台非 Debian 机器,复制粘贴我的问题中的第一个代码块,然后将第二个代码块复制到
Makefile中(必要时用制表符替换空格)应该会导致确切的错误消息。用 20000 替换 2000,你应该也可以在基于 Debian 的机器上重现它。