make がシステムをどう再コンパイルすればよいかを知るための情報は、makefile と呼ばれるデータベースを読み込むことで得られます。
MAKEFILESmakeはmakefileをどう読むかmakefile には、次の5種類のものを書きます。明示的なルール、暗黙のルール、変数定義、ディレクティブ(指示子)、そしてコメントです。ルール、変数、ディレクティブについては、後の章で詳しく説明します。
objects を定義していました(変数で makefile を簡潔にするの項を参照してください)。
make に何か特別な処理をさせるための指示です。次のようなものがあります。
# を書きたいときは、バックスラッシュでエスケープしてください(例:\#)。コメントは makefile のどの行にも書けますが、状況によっては特別な扱いを受けます。
変数参照や関数呼び出しの内部ではコメントを使えません。変数参照や関数呼び出しの中に現れた # は、コメントの始まりではなく文字どおりの # として扱われます。
レシピの中のコメントは、ほかのレシピのテキストと同じように、そのままシェルに渡されます。それをコメントとして解釈するかどうかは、シェルが決めます。
define ディレクティブの内部では、変数を定義している間コメントは無視されず、そのまま変数の値の一部として保持されます。その変数が展開されるときに、コメントは make のコメントとして扱われるか、レシピのテキストとして扱われるかのいずれかになります。どちらになるかは、その変数が評価される文脈によって決まります。
makefile は「行ベース」の構文を採用しており、改行文字が特別な意味を持ち、文(ステートメント)の終わりを表します。GNU make では、文を構成する1行の長さに制限はなく、コンピュータのメモリの許すかぎりいくらでも長くできます。
とはいえ、折り返したりスクロールしたりせずには表示できないほど長い行は読みづらいものです。そこで、文の途中に改行を入れて makefile を読みやすく整形できます。これは、文の途中の改行をバックスラッシュ(\)でエスケープすることで行います。区別が必要な場面では、(エスケープされているかどうかにかかわらず)改行で終わる1行を「物理行」と呼び、エスケープされていない最初の改行までの、エスケープされた改行をすべて含む完結した1つの文を「論理行」と呼ぶことにします。
バックスラッシュと改行の組み合わせがどう扱われるかは、その文がレシピ行か、レシピ以外の行かによって異なります。レシピ行でのバックスラッシュ+改行の扱いについては、後で説明します(レシピ行の分割の項を参照してください)。
レシピ行以外では、バックスラッシュ+改行は1個の空白文字に変換されます。これが済むと、そのバックスラッシュ+改行の周囲の空白はすべて1個の空白にまとめられます。これには、バックスラッシュの前にあるすべての空白、バックスラッシュ+改行の後の行頭にあるすべての空白、そして連続するバックスラッシュ+改行の組み合わせがすべて含まれます。
特別なターゲット .POSIX が定義されている場合は、POSIX.2 に準拠するためにバックスラッシュ+改行の扱いがわずかに変わります。第一に、バックスラッシュの前にある空白は除去されません。第二に、連続するバックスラッシュ+改行はまとめられません。
空白を付け加えたくないのに行を分割する必要がある場合は、ちょっとした技が使えます。バックスラッシュ+改行の組を、ドル記号・バックスラッシュ・改行という3文字に置き換えるのです。
var := one$\
word
make がバックスラッシュ+改行を取り除き、次の行を1個の空白にまとめると、これは次と等価になります。
var := one$ word
続いて make は変数展開を行います。変数参照「$ 」は、1文字の名前「 」(空白)を持つ変数を参照していますが、そのような変数は存在しないので空文字列に展開されます。その結果、最終的な代入は次と等価になります。
var := oneword
デフォルトでは、make は makefile を探すとき、次の名前を順に試します:GNUmakefile、makefile、Makefile の順です。
通常、makefile の名前は makefile か Makefile のどちらかにすべきです。(私たちは Makefile をお勧めします。ディレクトリの一覧を表示したとき先頭付近の目立つ位置に現れ、README などほかの重要なファイルのすぐそばに並ぶからです。)最初に試される名前 GNUmakefile は、たいていの makefile には推奨されません。この名前は、GNU make 専用で、ほかのバージョンの make では解釈できないような makefile を持っている場合にだけ使うべきです。ほかの make プログラムは makefile と Makefile は探しますが、GNUmakefile は探しません。
これらの名前がどれも見つからなければ、make は makefile をいっさい使いません。その場合は、コマンドの引数でゴールを指定しなければならず、make は組み込みの暗黙のルールだけを使ってそれを再生成する方法を見つけ出そうとします。暗黙のルールの利用の項を参照してください。
makefile に標準でない名前を使いたい場合は、「-f」または「--file」オプションで makefile の名前を指定できます。引数「-f name」または「--file=name」は、ファイル name を makefile として読み込むよう make に指示します。「-f」や「--file」オプションを複数回使えば、複数の makefile を指定できます。指定したすべての makefile は、その順序どおりに連結されたものとして扱われます。「-f」や「--file」を指定した場合、デフォルトの makefile 名 GNUmakefile、makefile、Makefile は自動的には探されません。
include ディレクティブは、現在の makefile を読むのを一時中断し、続きを読む前に1つ以上のほかの makefile を読み込むよう make に指示します。このディレクティブは、makefile 中の次のような行です。
include filenames…
filenames にはシェルのファイル名パターンを含められます。filenames が空の場合は、何も取り込まれず、エラーも出力されません。
行頭には余分な空白を置くことができ、無視されます。ただし先頭の文字はタブ(または .RECIPEPREFIX の値)であってはなりません。タブで始まる行はレシピ行とみなされてしまうからです。include とファイル名の間、およびファイル名どうしの間には空白が必要です。そこにある余分な空白や、ディレクティブの末尾にある空白は無視されます。行末には「#」で始まるコメントを置けます。ファイル名に変数参照や関数参照が含まれていれば、それらは展開されます。変数の使い方の項を参照してください。
例えば、a.mk、b.mk、c.mk という3つの .mk ファイルがあり、$(bar) が bish bash に展開されるとすると、次の式は
include foo *.mk $(bar)
次と等価になります。
include foo a.mk b.mk c.mk bish bash
make は include ディレクティブを処理するとき、それを含む makefile の読み込みを中断し、列挙された各ファイルを順に読み込みます。それが終わると、make はそのディレクティブが現れた makefile の読み込みを再開します。
include ディレクティブを使う場面の1つは、いくつものプログラムがそれぞれ別ディレクトリの個別の makefile で扱われていて、共通の変数定義(変数の設定の項を参照してください)やパターンルール(パターンルールの定義と再定義の項を参照してください)のまとまりを使う必要があるときです。
もう1つの場面は、前提条件をソースファイルから自動的に生成したいときです。生成した前提条件を1つのファイルに入れておき、それをメインの makefile から取り込むのです。このやり方は、ほかのバージョンの make で伝統的に行われてきた、前提条件をなんとかしてメインの makefile の末尾に追記する方法より、一般にすっきりしています。前提条件を自動生成するの項を参照してください。
指定された名前がスラッシュで始まっておらず(GNU Make が MS-DOS / MS-Windows のパス対応付きでコンパイルされている場合は、ドライブレターとコロンで始まっておらず)、そのファイルがカレントディレクトリで見つからない場合は、ほかのいくつかのディレクトリが探索されます。まず、「-I」または「--include-dir」オプションで指定したディレクトリが探索されます(オプション一覧の項を参照してください)。続いて、次のディレクトリが(存在すれば)この順で探索されます:prefix/include(通常は /usr/local/include 1)、/usr/gnu/include、/usr/local/include、/usr/include。
.INCLUDE_DIRS 変数には、make が取り込みファイルを探索する現在のディレクトリ一覧が入ります。そのほかの特別な変数の項を参照してください。
これらのデフォルトディレクトリでの探索を避けたい場合は、コマンドラインオプション -I に特別な値 - を付けて指定します(例:-I-)。こうすると make は、すでに設定済みの取り込みディレクトリを(デフォルトのディレクトリも含めて)すべて忘れます。
取り込もうとした makefile がこれらのどのディレクトリでも見つからなくても、その時点では致命的なエラーにはなりません。include を含む makefile の処理はそのまま続きます。makefile の読み込みがすべて終わると、make は古くなったファイルや存在しないファイルを再生成しようと試みます。makefile 自身が再生成されるしくみの項を参照してください。その makefile を再生成するルールが見つからなかったとき、あるいはルールは見つかったがそのレシピが失敗したときに、はじめて make はその makefile が見つからないことを致命的なエラーとして報告します。
存在しなかったり再生成できなかったりする makefile を、エラーメッセージを出さずに make に単に無視させたい場合は、include の代わりに -include ディレクティブを次のように使います。
-include filenames…
これはあらゆる点で include と同じように動作しますが、filenames のいずれか(またはそれらのいずれかの前提条件)が存在しなかったり再生成できなかったりしても、エラー(警告すら)が出ない点だけが異なります。
ほかの make 実装との互換性のために、sinclude は -include の別名になっています。
MAKEFILES
環境変数 MAKEFILES が定義されている場合、make はその値を、ほかの makefile よりも先に読み込むべき追加の makefile 名の一覧(空白区切り)とみなします。これは include ディレクティブとよく似た動きをします。それらのファイルを求めてさまざまなディレクトリが探索されます(ほかの makefile を取り込むの項を参照してください)。それに加えて、これらの makefile(またはそれらが取り込む makefile)からはデフォルトゴールが取られることはなく、MAKEFILES に列挙されたファイルが見つからなくてもエラーになりません。
MAKEFILES の主な用途は、make の再帰的な呼び出しどうしの間で情報をやり取りすることです(makeの再帰的な利用の項を参照してください)。トップレベルで make を呼び出す前にこの環境変数を設定するのは、たいてい望ましくありません。外部から makefile に手出しをしないほうがふつうは良いからです。とはいえ、特定の makefile を指定せずに make を実行している場合は、MAKEFILES 内の makefile で、探索パスを定義する(前提条件のためのディレクトリ探索の項を参照してください)など、組み込みの暗黙のルールがうまく働くよう手助けする有用なことができます。
ログイン時に MAKEFILES を環境に自動設定し、それが行われている前提で makefile を書きたくなるユーザもいます。これは非常にまずい考えです。なぜなら、そういった makefile は、ほかの人が実行すると動かなくなるからです。makefile に明示的な include ディレクティブを書くほうがはるかに良いやり方です。ほかの makefile を取り込むの項を参照してください。
makefile が、RCS ファイルや SCCS ファイルなどのほかのファイルから再生成できる場合があります。makefile をほかのファイルから再生成できるなら、おそらく make には、読み込む前にその makefile を最新の状態にしておいてほしいでしょう。
そのために、make はすべての makefile を読み込んだあと、処理した順番でそれぞれをゴールターゲットとみなし、更新を試みます。並列ビルド(並列実行の項を参照してください)が有効になっていれば、makefile も並列で再ビルドされます。
ある makefile に、それ自身を更新する方法を示すルールがある(そのルールはその makefile 自身にあっても、別の makefile にあってもかまいません)か、あるいはそれに暗黙のルールが適用される(暗黙のルールの利用の項を参照してください)場合、必要であればその makefile は更新されます。すべての makefile を確認したあと、実際に変更されたものが1つでもあれば、make は白紙の状態に戻り、すべての makefile を最初から読み直します。(このとき各 makefile の更新も再び試みますが、ふつうはすでに最新なので、もう変更されることはありません。)再スタートのたびに、特別な変数 MAKE_RESTARTS が更新されます(そのほかの特別な変数の項を参照してください)。
1つ以上の makefile が再生成できないことが分かっていて、おそらく効率上の理由から、それらに対して暗黙のルール探索を make に行わせたくない場合は、暗黙のルールの検索を防ぐ通常の方法ならどれを使ってもかまいません。例えば、その makefile をターゲットとし、レシピを空にした明示的なルールを書けます(空のレシピの利用の項を参照してください)。
makefile が、レシピはあるが前提条件のないダブルコロンルールであるファイルを再生成するよう指定している場合、そのファイルは常に再生成されます(ダブルコロンルールの項を参照してください)。makefile の場合、レシピはあるが前提条件のないダブルコロンルールを持つ makefile は、make を実行するたびに再生成され、さらに make がやり直して makefile を読み直したあとにもう一度再生成されます。これは無限ループを引き起こしてしまいます。make は makefile の再生成と再スタートを延々と繰り返し、ほかのことを何もしなくなってしまうのです。そこでこれを避けるため、レシピはあるが前提条件のないダブルコロンルールのターゲットとして指定された makefile を、make は再生成しようとしません。
偽りのターゲット(Phony)(偽りのターゲットの項を参照してください)も同じ効果をもたらします。偽りのターゲットは決して最新とはみなされないため、偽りと印を付けられた取り込みファイルがあると、make は際限なく再スタートしてしまいます。これを避けるため、偽りと印を付けられた makefile を make は再生成しようとしません。
この性質を利用して、起動時間を最適化できます。Makefile を再生成する必要がないと分かっているなら、次のいずれかを加えることで make にその再生成を試みさせないようにできます。
.PHONY: Makefile
あるいは:
Makefile:: ;
「-f」や「--file」オプションで読み込む makefile を1つも指定しなかった場合、make はデフォルトの makefile 名を試します。makefile にどんな名前を付けるかの項を参照してください。「-f」や「--file」オプションで明示的に要求された makefile と違い、make はこれらの makefile が存在すべきかどうかを確信していません。とはいえ、デフォルトの makefile が存在しないが make のルールを実行すれば作成できる場合は、その makefile を使えるようにするためにルールを実行してほしいことでしょう。
そこで、デフォルトの makefile がどれも存在しない場合、make はそれらを順に作ろうとし、どれか1つの作成に成功するか、試す名前が尽きるまで続けます。なお、make が makefile を見つけられず作成もできなくても、それはエラーではありません。makefile は常に必要というわけではないのです。
「-t」または「--touch」オプション(レシピを実行する代わりにの項を参照してください)を使うとき、どのターゲットをタッチするかの判断に古い makefile を使ってほしくはないでしょう。そのため「-t」オプションは makefile の更新には影響を与えません。「-t」が指定されても makefile は実際に更新されます。同様に、「-q」(または「--question」)と「-n」(または「--just-print」)も makefile の更新を妨げません。古い makefile では、ほかのターゲットについて誤った出力になってしまうからです。したがって「make -f mfile -n foo」は、mfile を更新して読み込み、それから foo とその前提条件を更新するレシピを、実行はせずに表示します。foo について表示されるレシピは、更新後の mfile の内容で指定されたものになります。
しかし場合によっては、makefile の更新さえも止めたいことがあるかもしれません。これは、makefile を makefile として指定するのに加えて、コマンドラインでゴールとしても指定することで実現できます。makefile 名がゴールとして明示的に指定されると、「-t」などのオプションがその makefile にも適用されます。
したがって「make -f mfile -n mfile foo」は、makefile mfile を読み込み、それを更新するのに必要なレシピを(実際には実行せずに)表示し、続いて foo を更新するのに必要なレシピを(これも実行せずに)表示します。foo についてのレシピは、既存の mfile の内容で指定されたものになります。
ほかの makefile とほとんど同じ内容の makefile を持ちたいことがあります。多くの場合は「include」ディレクティブで一方を他方に取り込み、ターゲットや変数定義を追加すれば済みます。しかし、2つの makefile が同じターゲットに対して異なるレシピを与えるのは不正です。とはいえ、別のやり方があります。
(取り込む側、つまり相手を取り込みたい側の)makefile では、何にでもマッチするパターンルールを使って、「この makefile の情報からは作れないターゲットを再生成するには、別の makefile を見るように」と指定できます。パターンルールについて詳しくは、パターンルールの定義と再定義の項を参照してください。
例えば、ターゲット「foo」(やほかのターゲット)の作り方を示す Makefile という makefile があるとします。ここで、次の内容を持つ GNUmakefile という makefile を書けます。
foo:
frobnicate > foo
%: force
@$(MAKE) -f Makefile $@
force: ;
「make foo」と打つと、make は GNUmakefile を見つけて読み込み、foo を作るにはレシピ「frobnicate > foo」を実行すればよいと分かります。「make bar」と打つと、make は GNUmakefile の中に bar を作る方法を見つけられないので、パターンルールのレシピ「make -f Makefile bar」を使います。Makefile が bar を更新するルールを提供していれば、make はそのルールを適用します。GNUmakefile が作り方を示していないほかのどのターゲットについても同様です。
このしくみが働くのは、パターンルールのパターンがただの「%」であり、どんなターゲットにもマッチするからです。このルールは前提条件 force を指定しており、これによってターゲットファイルがすでに存在していてもレシピが確実に実行されるようになっています。force ターゲットには空のレシピを与えて、make がそれをビルドするために暗黙のルールを探さないようにしています。さもないと、make は force 自身にも同じ何にでもマッチするルールを適用し、前提条件のループを作ってしまうのです!
makeはmakefileをどう読むか
GNU make は、2つのはっきり分かれた段階(フェーズ)で仕事をします。第1段階では、すべての makefile や取り込んだ makefile などを読み込み、すべての変数とその値、そして暗黙のルールと明示的なルールを内部に取り込み、すべてのターゲットとその前提条件からなる依存関係グラフを構築します。第2段階では、make はこの内部化したデータを使って、どのターゲットを更新する必要があるかを判断し、それらを更新するのに必要なレシピを実行します。
この2段階方式を理解しておくことは重要です。なぜなら、これは変数や関数の展開がいつ起こるかに直接影響し、makefile を書くときの混乱の原因になりがちだからです。以下に、makefile に現れうるさまざまな構文と、それぞれの構文の各部分でどの段階で展開が起こるかをまとめます。
展開が第1段階で起こる場合、それを即時(immediate)と呼びます。make は makefile を解析しながら、その構文部分を展開します。展開が即時でない場合、それを遅延(deferred)と呼びます。遅延される構文部分の展開は、その展開が使われるとき——即時の文脈で参照されるとき、または第2段階で必要になるとき——まで先延ばしされます。
これらの構文の中には、まだなじみのないものもあるかもしれません。後の章で各構文に慣れてきたら、その都度この節を参照すればよいでしょう。
変数定義は次のように解析されます。
immediate = deferred immediate ?= deferred immediate := immediate immediate ::= immediate immediate :::= immediate-with-escape immediate += deferred or immediate immediate != immediate define immediate deferred endef define immediate = deferred endef define immediate ?= deferred endef define immediate := immediate endef define immediate ::= immediate endef define immediate :::= immediate-with-escape endef define immediate += deferred or immediate endef define immediate != immediate endef
追加演算子「+=」では、その変数が以前に単純変数(「:=」または「::=」)として設定されていた場合は右辺が即時とみなされ、そうでなければ遅延とみなされます。
エスケープ付き即時演算子「:::=」では、右辺の値はただちに展開されますが、その後でエスケープされます(つまり、展開結果中のすべての $ が $$ に置き換えられます)。
シェル代入演算子「!=」では、右辺がただちに評価されてシェルに渡されます。その結果は左辺に書かれた変数に格納され、その変数は再帰展開変数とみなされます(したがって、参照されるたびに再評価されます)。
条件ディレクティブは即時に解析されます。これは例えば、条件ディレクティブの中で自動変数を使えないということを意味します。自動変数は、そのルールのレシピが起動されるまで設定されないからです。条件ディレクティブの中で自動変数を使う必要がある場合は、その条件をレシピの中に移し、代わりにシェルの条件構文を使わなければなりません。
ルールは、その形にかかわらず、常に同じ方法で展開されます。
immediate : immediate ; deferred
deferred
つまり、ターゲット部分と前提条件部分は即時に展開され、ターゲットをビルドするためのレシピは常に遅延されます。これは、明示的なルール、パターンルール、サフィックスルール、静的パターンルール、そして単純な前提条件定義のいずれについても当てはまります。
GNU make は makefile を1行ずつ解析します。解析は次の手順で進みます。
make はmakefileをどう読むかの項を参照してください)。
ここから導かれる重要な帰結は、1行に収まるならマクロがルール全体に展開されうる、ということです。次の例は動作します。
myrule = target : ; echo built $(myrule)
しかし、次の例は動作しません。make は展開後の行を再分割しないからです。
define myrule
target:
echo built
endef
$(myrule)
上の makefile は、レシピを持つルールではなく、前提条件「echo」と「built」を持つターゲット「target」の定義になってしまいます。あたかも makefile に target: echo built と書かれていたかのようにです。展開が完了したあとも行中に残っている改行は、通常の空白として無視されます。
複数行にわたるマクロを正しく展開するには、eval 関数を使わなければなりません。これによって、展開されたマクロの結果に対して make のパーサが実行されます(eval 関数の項を参照してください)。
これまでに、GNU make は読み込み段階とターゲット更新段階という2つのはっきり分かれた段階で動作することを学びました(make はmakefileをどう読むかの項を参照してください)。GNU Make には、makefile で定義された一部またはすべてのターゲットについて、前提条件(だけ)を2回目の展開にかける機能もあります。この2回目の展開を起こすには、この機能を使う最初の前提条件リストよりも前に、特別なターゲット .SECONDEXPANSION を定義しておく必要があります。
.SECONDEXPANSION が定義されていると、GNU make があるターゲットの前提条件を確認する必要が生じたとき、その前提条件は2回目に展開されます。たいていの場合、この二次展開は何の効果もありません。すべての変数参照や関数参照は、makefile の最初の解析時にすでに展開されているからです。そこで、パーサの二次展開段階を活用するには、makefile 中で変数参照や関数参照をエスケープしておく必要があります。この場合、1回目の展開は参照のエスケープを解除するだけで展開はせず、展開は二次展開段階に委ねられます。例えば、次の makefile を考えてみましょう。
.SECONDEXPANSION: ONEVAR = onefile TWOVAR = twofile myfile: $(ONEVAR) $$(TWOVAR)
1回目の展開段階のあと、myfile ターゲットの前提条件リストは onefile と $(TWOVAR) になります。最初の(エスケープされていない)ONEVAR への変数参照は展開され、2番目の(エスケープされた)変数参照は変数参照と認識されることなく、単にエスケープが解除されるだけです。そして二次展開の最中に、最初の語は再び展開されますが、変数参照も関数参照も含まないので値 onefile のままです。一方、2番目の語はいまや変数 TWOVAR への通常の参照となり、値 twofile に展開されます。最終結果として、前提条件は onefile と twofile の2つになります。
もちろん、これはあまり面白い例ではありません。同じ結果は、両方の変数をエスケープせずに前提条件リストに書くだけでもっと簡単に得られるからです。両者の違いは、変数が再設定されたときにはっきりします。次の例を考えてみましょう。
.SECONDEXPANSION: AVAR = top onefile: $(AVAR) twofile: $$(AVAR) AVAR = bottom
ここでは、onefile の前提条件は即時に展開されて値 top に解決されますが、twofile の前提条件は二次展開まで完全には展開されず、値 bottom になります。
これはやや面白くなってきましたが、この機能の真価が現れるのは、二次展開が常にそのターゲットの自動変数のスコープ内で行われると知ったときです。つまり、$@ や $* などの変数を二次展開の最中に使え、それらはレシピの中とまったく同じように、期待どおりの値を持つのです。やるべきことは、$ をエスケープして展開を遅延させるだけです。また、二次展開は明示的なルールと暗黙の(パターン)ルールの両方について起こります。これを知ると、この機能の使い道は劇的に広がります。例えば次のとおりです。
.SECONDEXPANSION: main_OBJS := main.o try.o test.o lib_OBJS := lib.o api.o main lib: $$($$@_OBJS)
ここでは、最初の展開のあと、main ターゲットと lib ターゲットの両方の前提条件は $($@_OBJS) になります。二次展開の最中、$@ 変数はターゲットの名前に設定されるので、main ターゲットの展開結果は $(main_OBJS)、すなわち main.o try.o test.o になり、一方 lib ターゲットの二次展開結果は $(lib_OBJS)、すなわち lib.o api.o になります。
関数も、適切にエスケープしてあれば、ここに混ぜて使えます。
main_SRCS := main.c try.c test.c lib_SRCS := lib.c api.c .SECONDEXPANSION: main lib: $$(patsubst %.c,%.o,$$($$@_SRCS))
このバージョンでは、ユーザはオブジェクトファイルではなくソースファイルを指定できますが、前提条件リストの結果は前の例と同じになります。
二次展開段階における自動変数の評価、とりわけターゲット名変数 $$@ の評価は、レシピ内での評価と同じように振る舞います。ただし、make が解釈するルール定義の種類ごとに、いくつか微妙な違いや「特殊なケース」が関わってきます。さまざまな自動変数を使う際の細かな点を、以下に説明します。
明示的なルールの二次展開では、$$@ と $$% はそれぞれ、ターゲットのファイル名と、ターゲットがアーカイブのメンバである場合のターゲットメンバ名に評価されます。$$< 変数は、このターゲットに対する最初のルールの最初の前提条件に評価されます。$$^ と $$+ は、同じターゲットについてすでに現れたルールのすべての前提条件のリストに評価されます($$+ は重複を含み、$$^ は含みません)。次の例がこれらの振る舞いの理解の助けになるでしょう。
.SECONDEXPANSION: foo: foo.1 bar.1 $$< $$^ $$+ # 行 #1 foo: foo.2 bar.2 $$< $$^ $$+ # 行 #2 foo: foo.3 bar.3 $$< $$^ $$+ # 行 #3
最初の前提条件リストでは、3つの変数($$<、$$^、$$+)はすべて空文字列に展開されます。2番目では、それぞれ foo.1、foo.1 bar.1、foo.1 bar.1 という値になります。3番目では、それぞれ foo.1、foo.1 bar.1 foo.2 bar.2、foo.1 bar.1 foo.2 bar.2 foo.1 foo.1 bar.1 foo.1 bar.1 という値になります。
ルールは makefile に書かれた順序で二次展開されますが、レシピを持つルールだけは常に最後に評価されます。
変数 $$? と $$* は利用できず、空文字列に展開されます。
静的パターンルールの二次展開のルールは、1つの例外を除いて、上の明示的なルールの場合と同じです。その例外とは、静的パターンルールでは $$* 変数がパターンの語幹(stem)に設定されることです。明示的なルールと同様に、$$? は利用できず、空文字列に展開されます。
make は暗黙のルールを探すとき、語幹を代入してから、ターゲットパターンにマッチするすべてのルールについて二次展開を行います。自動変数の値は、静的パターンルールの場合と同じやり方で導かれます。例として次を見てください。
.SECONDEXPANSION: foo: bar foo foz: fo%: bo% %oo: $$< $$^ $$+ $$*
ターゲット foo に対してこの暗黙のルールが試されるとき、$$< は bar に展開され、$$^ は bar boo に展開され、$$+ も bar boo に展開され、$$* は f に展開されます。
暗黙のルールの検索アルゴリズムで説明するディレクトリ接頭辞(D)は、(展開後に)前提条件リスト中のすべてのパターンに付け加えられる点に注意してください。例として次を見てください。
.SECONDEXPANSION:
/tmp/foo.o:
%.o: $$(addsuffix /%.c,foo bar) foo.h
@echo $^
二次展開とディレクトリ接頭辞の再構成のあとに表示される前提条件リストは、/tmp/foo/foo.c /tmp/bar/foo.c foo.h になります。この再構成が不要なら、前提条件リスト中で % の代わりに $$* を使えます。