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がすでに説明したように、これはシェルの問題ではなくxargs、引用符、より多くのエコー分割;などを使用して解決することはできません。 make 操作のすべてのテキストは単一の引数として渡され、オペレーティングシステムがexecve(2)許可する最大サイズより長くすることはできません。

GNU make(Linuxではデフォルト)を使用している場合は、次のものを使用できます。fileそしてforeach機能:

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() 合計 + 各文字列の最後の null 文字
  • strlen() すべての引数文字列の合計 + 各文字列の最後の null 文字
  • 環境配列: n+1 環境文字列 * 文字サイズ *
  • argv配列:n + 1パラメータ文字列*文字サイズ*
  • 追加番号

これらすべてのデータを超えてはいけませんARG_MAX

ARG_MAX過去のUNIXでは10240または20480バイト

SunOS-4.0(1987年12月リリース)は、この制限を次に高めました。1MB

Solaris-7.0(1997年リリース)は64ビットサポートを導入し、64ビットシステムの実際のマイナーな制限(より大きなenv合計argv配列によるchar *)を避けるためにARG_MAX次に昇格しました。2MB64ビットプログラムの場合。

注:最新のPOSIX準拠のオペレーティングシステムには、プログラムのサポートgetconfgetconf ARG_MAX実際の値の印刷が含まれています。 64ビットLinuxでは、以下を返します。2MBしたがって、Linuxでの最初のビューは〜らしいSunOS拡張機能を提供...

今見てみましょうmake

プログラムmakeは以下を介してコマンドを呼び出しますMakefiles

sh -ce command

commandはどこにありますか?単一パラメータそれは拡大するタスクラインに表示される文字列ですMakefile

SunPro Make1990年代初頭に最適化が導入されました。

  • コマンドラインにシェルメタ文字が含まれていない場合は、コマンドラインmake自体にタグを付けて、次のようにコマンドを呼び出します。execv()シェル呼び出しのオーバーヘッドを防ぎます。

後でこの最適化が採用されgmakeましたsmake

smake2012年には別の最適化が導入されました。

  • シェルコマンドがechoaで終わる単純なコマンドとして導入され、次の; コマンドラインにシェルメタ文字が含まれていない場合は、echoviaの後のコマンドがインラインで実行され、smake最新のビルドシステムでコマンドを抑制するためによく使用されるオーバーヘッドが削減されます。 、代わりに出力を理解しやすくするために、単純化された呼び出しを使用します(たとえば、1993年2月に導入されたSchily Makefileシステムを参照)。;execv()@makeechomake

コマンドラインにシェルメタ文字が含まれているため、これらのルールはコマンドラインmakeには適用されません。makeしたがって、コマンド全体は次のように呼び出されます。

sh -ce command

ここで、commandmakefile によって発生した合計サイズを含む文字列です。

これで、LinuxカーネルはUNIXやPOSIXと互換性がなく、UNIXには存在しなかった追加の制限を適用しているようです。この追加の制限は、単一の文字列の最大長に基づいているようです。

これが本当なら、Linuxはmake

Linuxカーネル担当者にバグレポートを送信することを検討しましたか?

答え3

この場合、あなたの質問はなぜこれをやっていますか?プログラム出力やglobである可能性のあるすべてのファイルを含む一種の.ignoreファイルを生成したいようです。前者の場合は、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これは、シェルで各コマンドを実行すると、コマンド全体sh -c '...'に対応するエントリが単一の引数になるため、単一の引数の長さ制限である131,072にまだ縛られているためです。 Linuxのx86プラットフォームバイト。

関連情報