makefile 的参数列表太长错误

makefile 的参数列表太长错误

在 makefile 中,我有

@echo "$(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES)" | tr ' ' '\n' >> $@

问题是它$(CLEAN_FILES)很大,所以当我运行 make 时,我得到

make: execvp: /bin/sh: Argument list too long

我使用的是 Xubuntu 18.10。

编辑:我应该提供更多背景信息。我正在研究的是一个 make 规则(我正在使用 GNU make)来自动生成文件.hgignore。这是完整的 make 规则:

.hgignore : .hgignore_extra
    @echo "Making $@"
    @rm -f $@
    @echo "# Automatically generated by Make. Edit .hgignore_extra instead." > $@
    @tail -n +2 $< >> $@
    @echo "" >> $@
    @echo "# The following files come from the Makefile." >> $@
    @echo "syntax: glob" >> $@
    @echo "$(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES)" | tr ' ' '\n' >> $@
    @chmod a-w $@
.PHONY : .hgignore

编辑2:根据@mosvy的建议,我也尝试过

.hgignore : .hgignore_extra
    @echo "Making $@"
    @rm -f $@
    @echo "# Automatically generated by Make. Edit .hgignore_extra instead." > $@
    @tail -n +2 $< >> $@
    @echo "" >> $@
    @echo "# The following files come from the Makefile." >> $@
    @echo "syntax: glob" >> $@
    $(file >$@) $(foreach V,$(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES),$(file >>$@,$V))
    @true
    @chmod a-w $@
.PHONY : .hgignore

运行make .hgignore此命令后,我不再收到“参数列表太长”错误,但生成的 .hgignore 文件仅包含该syntax: glob行之前的输出,之后不包含任何内容。

答案1

正如 @schily 已经解释的那样,这不是 shell 问题,不能使用xargs、引用、分割成更多 echo;等来解决。 make 操作中的所有文本都作为参数传递给单个execve(2),并且它不能长于操作系统允许的最大大小。

如果您使用 GNU make(Linux 上的默认设置),您可以使用它fileforeach功能:

TEST = $(shell yes foobar | sed 200000q)

/tmp/junk:
        $(file >$@) $(foreach V,$(TEST),$(file >>$@,$V))
        @true

.PHONY: /tmp/junk

这会将所有由$(TEST)换行符分隔的单词打印到名为 的文件中$@。它基于 make 的类似示例手动的

你的 Makefile 可能会被重新设计成更易于管理的东西,不需要花哨的 GNU 功能,但很难从你发布的代码片段中看出如何做。

更新:

对于问题的确切片段,可以这样做:

.hgignore : .hgignore_extra
    $(info Making $@)
    $(file >[email protected])
    $(file >>[email protected],# Automatically generated by Make. Edit .hgignore_extra instead.)
    $(shell tail -n 2 $< >>[email protected])
    $(file >>[email protected],)
    $(file >>[email protected],# The following files come from the Makefile.)
    $(file >>[email protected],syntax: glob)
    $(foreach L, $(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES), $(file >>[email protected],$L))
    @mv -f [email protected] $@
    @chmod a-w $@
.PHONY : .hgignore

我对其进行了一些更改,因此它首先写入.hgignore.new,如果一切顺利,然后才转移.hgignore.new.hgignore。您必须将缩进空格改回制表符,因为这哑的界面正在破坏空白。

答案2

UNIX系统适用以下规则:

以下数据构成进程的初始堆栈:

  • 所有环境字符串的 strlen() 总和 + 每个字符串的最后一个 nul 字符
  • 所有参数字符串的 strlen() 总和 + 每个字符串的最后一个 nul 字符
  • 环境数组:n+1 环境字符串 * sizeof char *
  • argv 数组:n+1 个参数字符串 * sizeof char *
  • 一些额外的数字

所有这些数据不得超过ARG_MAX

在历史上的 UNIX 上, 的ARG_MAX值为10240或者20480 字节

SunOS-4.0(1987 年 12 月发布)将该限制提高到1MB

Solaris-7.0(于 1997 年发布)引入了 64 位支持,并且为了避免 64 位系统的实际较小限制(由于较大的envargv数组而导致char *),ARG_MAX被提升为2MB对于 64 位程序。

顺便说一句:现代 POSIX 兼容操作系统包括对getconf程序的支持并getconf ARG_MAX打印实际值。在 64 位 Linux 上,返回2MB所以 Linux 上的第一个视图似乎采用 SunOS 增强功能......

现在让我们看看make

make程序通过以下方式调用命令Makefiles

sh -ce command

哪里command是一个单一参数那就是扩大您在 的操作行中看到的字符串Makefile

SunPro Make在 20 世纪 90 年代初引入了一项优化:

  • 如果命令行不包含 shell 元字符,make它本身会标记命令行并通过以下方式调用命令:execv()以避免 shell 调用的开销。

后来,gmakesmake采用了这种优化。

smake2012年引入了另一项优化:

  • 如果 shell 命令是由echo一个以 a 结尾的简单命令引入的; ,并且如果以下命令行不包含 shell 元字符,则 会echo内联到并执行 viasmake之后的命令,以减少现代构建系统的开销通常用于抑制命令回显,而是使用简化的调用来使输出更易于理解(例如,参见 1993 年 2 月引入的 Schily Makefile 系统)。;execv()@makeechomake

这些规则都不make适用于您的make命令行,因为您的命令行包含 shell 元字符。所以你的整个命令是通过以下方式调用的:

sh -ce command

其中command是一个字符串,其中包含由 makefile 引起的总大小。

现在看来,Linux 内核既不兼容 UNIX,也不兼容 POSIX,并且强制实施了 UNIX 上从未存在的附加限制。这个额外的限制似乎是基于单个字符串的最大长度。

如果这是真的,这将取消 Linux 的资格,因为它将阻止 Linux 与make.

您是否考虑过向 Linux 内核人员提交错误报告?

答案3

在这种情况下,你的问题是你为什么要这样做?看起来您正在尝试创建某种 .ignore 文件,其中包含可能是程序输出或 glob 的所有文件。在前一种情况下,我建议将程序输出直接传递到tr(如果绝对必要的话),然后传递到文件,而不通过中间变量。如果你使用 glob 那么你可以使用一个非常简单的for环形:

for file in *
do
    echo "$file" >> out.txt
done

(如有必要,请在 Makefile 中转义。)

笼统地说:

  • 对于任何大数据块,使用管道和重定向而不是变量。它们确实非常快(因为它们的处理速度与管道中的每个程序处理输入的速度一样快)。
  • 使用xargs当你真的需要的时候。
  • 避免无用的echos 和cats

答案4

这个答案,特别是这句话:

一个参数的长度不得超过 MAX_ARG_STRLEN (131072)。如果您生成像“sh -c '使用长参数生成'”这样的长调用,这可能会变得相关。

另一方面,要传递的参数总数的限制相当高,因此也许只是将它们作为单独的参数传递,因为最终结果echo是相同的?

这只是"从原始命令中删除 s:

@echo $(IGNORE_DIRS) $(CLEAN_FILES) $(CLEAN_DIRS) $(REALCLEAN_FILES) | tr ' ' '\n' >> $@

希望这足以将参数保持在参数数量(相当高)和每个参数长度(这将不再是问题,因为每个参数现在都很短)的限制内。


更新:删除引号并不能真正完全解决这个问题,因为make在 shell 中运行每个命令时,使用相当于sh -c '...'整个命令成为单个参数的位置,因此它仍然会绑定到单个参数的限制长度,即 131,072 x86 平台上的 Linux 上的字节。

相关内容