【问题标题】:Makefile - argument list too long, but only in some configurationsMakefile - 参数列表太长,但仅在某些配置中
【发布时间】: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 在两台机器上都使用;我还尝试了bashdashsh,通过export SHELL=<shell> make,以及用链接替换符号链接/bin/sh -> /bin/bashdash)。

最后,我尝试从源代码重新编译 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 的机器上重现它。

标签: shell makefile


【解决方案1】:

您的配方扩展为单行 shell 复合命令(因为行结尾的反斜杠)。而且这条线可能太长了。你检查过它在所有机器上的样子(shell 复合命令)吗?我建议你把它包裹在$(info...):

toolong:
    @$(info $(foreach file,$(sort $(FILES)), \
        if $(MAKE) $(file); \
        then echo "did $(file)" >> $@; \
        else echo "failed $(file)" >> $@; fi; ))

看看你的机器和其他机器上的输出是否相同。如果出于任何原因,您的更长,则可能是解释。否则它一定是操作系统差异......

注意:您可以为每个目标制定一条规则,而不是一条规则适用于所有人。

注意:你的食谱(独立于其奇妙的长度)没有任何用处。由于文件已经存在并且没有先决条件,因此您所有的 sub-make 调用只会告诉您文件是最新的。

【讨论】:

  • 这是一个非常好的评论。然而,奇怪的是,当我使用info 时,在两台机器上我得到完全相同的输出,并且没有人抱怨行太大。我diffed 输出,它是相同的。关于配方本身的实用性,我知道它不是很有用,但实际配方确实有用,我只是简化了它,因为错误与配方无关,我希望它易于重试,而不必删除每次测试后的文件。
  • @anol 没有人抱怨行长并不奇怪:它仅由 make 处理,而不是由 shell 处理。如果输出相同,则一定是操作系统不同,但猜测哪个无法访问机器似乎很困难。
  • 我刚刚发布了一个编辑:在 Fedora 上的限制似乎是 128 KB,在 Ubuntu 上是 2 MB。如果这有助于跟踪它的来源......
  • 您考虑环境大小了吗? env | wc -c 在不同的机器上返回什么?
  • env | wc -c 在我的 Fedora 上报告 4470,在我的 Ubuntu 上报告 2654。但是我的一位同事在他的 Arch Linux 上报告了不到 1200,但它仍然失败,就像在我的 Fedora 中一样。对我来说,差异似乎太小而无关紧要。
猜你喜欢
  • 1970-01-01
  • 2019-06-03
  • 2017-11-30
  • 2020-05-09
  • 2014-05-21
  • 2015-04-04
  • 2021-10-26
  • 2014-04-18
  • 2021-10-28
相关资源
最近更新 更多