HN 日本語サマリー

← 一覧へ戻る
プログラミング

再帰的な `make` と `-j`

Recursive `make` and `-j` (quuxplusone.github.io)

33 pointsby ibobev14 コメント

要約

この記事では、Makefileのレシピ内で`make`を再帰的に呼び出す際のGNU Makeの挙動について、バージョン3.81から4.3にかけての変化を解説しています。特に、`-j`オプション(並列実行)が子プロセスに引き継がれない問題と、その解決策としてレシピ行の先頭に`+`を付けるか、`make`を`$(MAKE)`に置き換える方法について説明しています。また、これらの方法でも解消されない警告や、バージョンによる挙動の違いについても詳細な表を用いて比較しています。

全文翻訳

再帰的なmakeと-j 今日、私は「再帰的」なmakeの呼び出し、つまりMakefileのレシピ自体がmakeを呼び出すケースについて調べました。公式ドキュメントは非常に優れていますが、私は様々なGNU Makeのバージョンを扱っているため、GNU Make 3.81から4.3にかけて挙動がどのように変化したかを調べました。 基本的に、Makefileに次のようなレシピがある場合の問題です。 ser: make -C subdir そして `make -j4 ser` を実行すると、最近のGNU Makeのバージョンでは警告(jobserver unavailable: using -j1)が表示されます。そして、並列実行したいのに、ジョブサーバー(基本的に-jオプション)が子makeに転送されないため、並列実行が失われます。さらに、呼び出したmakeと同じmakeバイナリが必ずしも使用されるわけではなく、パスにあるmakeが実行されます。 レシピ行の先頭に`+`を付けるか、(それが不快にマジカルに感じるので)`make`を`$(MAKE)`に置き換えることで、これら3つの問題すべてを修正できます。詳細は記事の最後にあります。 ジョブサーバーが正しく転送されるようになったので、コマンドラインで指定した並列度と再帰的なレシピで指定した並列度がどのように相互作用するかを疑問に思いました。テストに使用したMakefileのペアは以下の通りです。 detab -E -4 - >Makefile <<EOF ser: make -C subdir par2: make -j2 -C subdir par4: make -j4 -C subdir par: make -j -C subdir serplus: +make -C subdir par2plus: +make -j2 -C subdir par4plus: +make -j4 -C subdir parplus: +make -j -C subdir sermake: $(MAKE) -C subdir par2make: $(MAKE) -j2 -C subdir par4make: $(MAKE) -j4 -C subdir parmake: $(MAKE) -j -C subdir EOF mkdir subdir detab -E -4 - >subdir/Makefile <<EOF all: one two three four five one: echo 'one' && sleep 1 && echo 'ONE' two: echo 'two' && sleep 1 && echo 'TWO' three: echo 'thr' && sleep 1 && echo 'THR' four: echo 'fou' && sleep 1 && echo 'FOU' five: echo 'fiv' && sleep 1 && echo 'FIV' EOF その後、利用可能な様々なバージョンのMakeで `make ser`, `make -j2 ser` などをテストしました。以下はGNU Make 3.81(OS X上)の結果です。各セルの数字は並列実行されたレシピの数を示します。ダガー†が付いている4行は、テストしたMakeのバージョン間で異なります。これらのイディオムは「移植性がない」ため、実際には避けるべきだと私は思います。 make… -j2 -j4 -j ser† 1 2 4 ∞ serplus 1 2 4 ∞ sermake 1 2 4 ∞ par2 2 2b 2b 2 par2plus 2 2b 2b 2 par2make 2 2b 2b 2 par4 4 4b 4b 4 par4plus 4 4b 4b 4 par4make 4 4b 4b 4 par† ∞ 2 4 ∞ parplus† ∞ 2 4 ∞ parmake† ∞ 2 4 ∞ 上記の表で、「b」は次の警告を示します。 $ make -j2 par2 make[1]: warning: -jN forced in submake: disabling jobserver mode. GNU Make 4.0(Debian 8.11上): make… -j2 -j4 -j ser† 1 1a 1a ∞ serplus 1 2 4 ∞ sermake 1 2 4 ∞ par2 2 2b 2b 2 par2plus 2 2b 2b 2 par2make 2 2b 2b 2 par4 4 4b 4b 4 par4plus 4 4b 4b 4 par4make 4 4b 4b 4 par† ∞ 1a 1a ∞ parplus† ∞ 2 4 ∞ parmake† ∞ 2 4 ∞ 上記の表で、「a」と「b」はそれぞれ次の2つの警告を示します。 $ make -j2 ser make[1]: warning: jobserver unavailable: using -j1. Add '+' to parent make rule. $ make -j2 par2 make[1]: warning: -jN forced in submake: disabling jobserver mode. GNU Make 4.2.1(Rocky 8.10上)およびGNU Make 4.3(Red Hat 9またはUbuntu 22.04上)では、後者のエラーメッセージが調整され、Nの値が含まれるようになります。 $ make -j2 par make[1]: warning: -j0 forced in submake: resetting jobserver mode. $ make -j2 par2 make[1]: warning: -j2 forced in submake: resetting jobserver mode. また、行 `par`, `parplus`, `parmake` の挙動も変更されます。 make… -j2 -j4 -j ser† 1 1a 1a ∞ serplus 1 2 4 ∞ sermake 1 2 4 ∞ par2 2 2b 2b 2 par2plus 2 2b 2b 2 par2make 2 2b 2b 2 par4 4 4b 4b 4 par4plus 4 4b 4b 4 par4make 4 4b 4b 4 par† ∞ ∞b ∞b ∞ parplus† ∞ ∞b ∞b ∞ parmake† ∞ ∞b ∞b ∞ 「親makeルールに'+'を追加してください」と明示的に指示する警告「a」は、レシピ行の先頭に`+`を付けることで、常に消えます。例えば、次のようになります。 serplus: +make -C subdir この解決策は、`@`や`-`のプレフィックスと同様に、私には不快にマジカルに見えます。(個人的には、レシピ行の最後に「|| true」がある方が、先頭にマジカルな「-」があるよりも良いと思います。) よりマジカルに見えない(しかし、内部的には同様にマジカルであるらしい)解決策は、`$(MAKE)`を使用することです。例えば次のようになります。 sermake: $(MAKE) -C subdir どちらの方法も、警告「b」(「ジョブサーバーモードのリセット」について)を沈黙させるわけではありません。 実際には、次のファンタジーのような行が欲しいところです。 make… -j2 -j4 -j fantasy nprocs 2 4 ∞ しかし、現時点では、引用された行のいずれかから好みのトレードオフを選択する必要があります。 sermake 1 2 4 ∞ par4make 4 4b 4b 4 投稿日: 2026-09-30 カテゴリ: concurrency, makefiles, sre