ルールのレシピ(recipe、ルールの実行内容)は、実行すべき1行以上のシェルコマンドからなります。これらのコマンドは、書かれた順に1つずつ実行されます。通常、これらのコマンドを実行した結果として、そのルールのターゲットが最新の状態になります。
ユーザーが使うシェルプログラムはさまざまですが、makefile 内のレシピは、makefile 側で別途指定しない限り、常に /bin/sh によって解釈されます。レシピの実行の項を参照してください。
makefile には少し変わった性質があります。それは、1つのファイルの中に実は2つの異なる構文が混在している、ということです。makefile の大部分は make の構文(Makefileの書き方を参照)を使います。しかしレシピのほうはシェルに解釈してもらうことを前提としているため、シェルの構文で書かれます。make プログラムはシェルの構文を理解しようとはしません。レシピの中身をシェルに渡す前に、ごく限られた特定の変換だけを行います。
レシピの各行は、必ずタブ(あるいは .RECIPEPREFIX 変数に設定した文字。その他の特別な変数を参照)で始めなければなりません。ただし例外として、レシピの最初の1行だけは、ターゲットと前提条件を書いた行にセミコロンで区切って続けて書くことができます。タブで始まる行は、それが「ルールの文脈」(つまり、あるルールが始まってから、別のルールや変数定義が現れるまでの間)に現れる限り、そのルールのレシピの一部とみなされます。レシピ行の合間には空行やコメントだけの行が現れても構いません。それらは無視されます。
以上のルールから、次のような帰結が生まれます:
make のコメントではありません。それはそのままシェルに渡されます。シェルがそれをコメントとして扱うかどうかは、お使いのシェル次第です。
make の変数定義ではなくレシピの一部とみなされ、シェルへ渡されます。
ifdef、ifeq など。条件式の構文を参照)で、行頭の最初の文字がタブでインデントされているものは、レシピの一部とみなされてシェルへ渡されます。
make がレシピを解釈する数少ない場面の1つが、改行の直前にバックスラッシュがないかをチェックすることです。makefile の通常の構文と同じく、論理的には1つのレシピ行を、各改行の前にバックスラッシュを置くことで、makefile 上では複数の物理行に分割して書けます。このように連なった行は1つのレシピ行とみなされ、それを実行するためにシェルが1回だけ起動されます。
ただし、makefile の他の場所での扱い(長い行を分割するを参照)とは対照的に、レシピ内ではバックスラッシュと改行の組は取り除かれません。バックスラッシュも改行も両方ともそのまま保持されてシェルに渡されます。このバックスラッシュ/改行がどう解釈されるかは、お使いのシェル次第です。バックスラッシュ/改行の次の行の最初の文字がレシピ接頭文字(デフォルトではタブ。その他の特別な変数を参照)であれば、その文字(そしてその1文字だけ)が取り除かれます。レシピに空白が加えられることは決してありません。
例えば、次の makefile における all ターゲットのレシピを見てみましょう:
all :
@echo no\
space
@echo no\
space
@echo one \
space
@echo one\
space
これは4つの別々のシェルコマンドからなり、その出力は次のようになります:
nospace nospace one space one space
もう少し複雑な例として、この makefile を見てみましょう:
all : ; @echo 'hello \
world' ; echo "hello \
world"
これは、次のコマンドでシェルを1回起動します:
echo 'hello \
world' ; echo "hello \
world"
そして、シェルのクォートのルールに従って、次の出力が得られます:
hello \ world hello world
二重引用符("…")でくくった文字列の中ではバックスラッシュ/改行の組が取り除かれているのに対し、単一引用符('…')でくくった文字列の中では取り除かれていない点に注目してください。これがデフォルトのシェル(/bin/sh)によるバックスラッシュ/改行の扱い方です。makefile で別のシェルを指定した場合は、扱いが異なるかもしれません。
単一引用符の中で長い行を分割したいけれど、バックスラッシュ/改行はクォートした内容の中に現れてほしくない、という場面もあります。これは、Perl のような言語にスクリプトを渡すときによく起こります。そうした言語では、スクリプトの中に余計なバックスラッシュが入ると意味が変わったり、構文エラーになったりしかねないからです。これに対処する簡単な方法の1つは、クォートした文字列やコマンド全体をいったん make 変数に入れておき、レシピの中ではその変数を使うことです。こうすると makefile の改行クォートのルールが適用され、バックスラッシュ/改行が取り除かれます。先ほどの例をこの方法で書き直すと:
HELLO = 'hello \ world' all : ; @echo $(HELLO)
次のような出力が得られます:
hello world
お好みであれば、ターゲット固有変数(ターゲット固有変数の値を参照)を使うこともできます。そうすれば、変数とそれを使うレシピとをより密接に対応づけられます。
make がレシピに対して行うもう1つの処理は、レシピ中の変数参照を展開することです(変数参照の基本を参照)。これは、make がすべての makefile を読み終え、ターゲットが古い(更新が必要な)状態だと判断したあとに行われます。したがって、再ビルドされないターゲットのレシピは決して展開されません。
レシピ中の変数参照や関数参照は、makefile の他の場所での参照とまったく同じ構文・意味を持ちます。クォートのルールも同じです。レシピ中にドル記号そのものを出したいときは、ドル記号を二重(「$$」)にしなければなりません。デフォルトのシェルのように、ドル記号で変数を導入するシェルでは、参照したい変数が make の変数なのか(ドル記号1個を使う)、シェルの変数なのか(ドル記号2個を使う)を、頭の中ではっきり区別しておくことが大切です。例えば:
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
これは次のコマンドがシェルに渡される結果となり:
for i in one two three; do \
echo $i; \
done
期待どおりの結果を生みます:
one two three
通常、make はレシピの各行を実行する前に画面に表示します。これをエコー(echo)と呼びます。あたかも自分でその行をタイプしているかのように見えるからです。
行が「@」で始まっている場合、その行のエコー表示は抑制されます。「@」はその行がシェルに渡される前に取り除かれます。これは典型的には、makefile の進行状況を知らせる echo コマンドのように、何かを表示するだけが目的のコマンドに使います:
@echo About to make distribution files
make に「-n」または「--just-print」フラグを与えると、ほとんどのレシピを実行せずにエコー表示だけ行います(オプション一覧を参照)。この場合は「@」で始まるレシピ行も表示されます。このフラグは、実際には何もせずに、make がどのレシピを必要だと考えているかを知りたいときに便利です。
make への「-s」または「--silent」フラグは、すべてのレシピが「@」で始まっているかのように、エコー表示を一切行わないようにします。makefile に、前提条件を持たない特別なターゲット .SILENT のルールを書いても同じ効果が得られます(特別な組み込みターゲット名を参照)。
ターゲットを更新するためにレシピを実行する段になると、.ONESHELL という特別なターゲットが有効になっていない限り(1つのシェルで実行するを参照)、レシピの各行ごとに新しいサブシェルを起動して実行します。(実際には、make は結果に影響しない範囲で近道をすることがあります。)
注意してください: このことは、シェル変数を設定したり、cd のように各プロセス内に閉じた文脈を設定するシェルコマンドを起動したりしても、それはレシピの後続の行には影響しない、ということを意味します。(訳注: cd などはサブシェルごとに独立しているため、次の行には引き継がれません。)cd によって次の文に影響を与えたいときは、両方の文を1つのレシピ行に入れてください。そうすれば make はその行全体を1つのシェルで実行し、シェルが各文を順番に実行します。例えば:
foo : bar/lose
cd $(<D) && gobble $(<F) > ../$@
ここではシェルの AND 演算子(&&)を使っています。こうしておけば、cd コマンドが失敗した場合に、間違ったディレクトリで gobble コマンドを起動しようとせずにスクリプトが失敗してくれます。間違ったディレクトリで実行すると問題を起こしかねません(この場合、少なくとも ../foo が中身を失って空になってしまうのは確実でしょう)。
ときには、レシピのすべての行を1回のシェル起動にまとめて渡したい場合があります。これが役立つ状況はおおむね2つあります。1つは、レシピが多数のコマンド行からなる makefile で、余計なプロセスを避けることで性能を改善できる場合です。もう1つは、レシピのコマンドに改行を含めたい場合です(例えば SHELL としてまったく別のインタプリタを使っているようなとき)。makefile のどこかに .ONESHELL という特別なターゲットが現れると、すべてのターゲットについて、レシピの全行が1回のシェル起動にまとめて渡されるようになります。レシピ行と行の間の改行は保持されます。例えば:
.ONESHELL:
foo : bar/lose
cd $(<D)
gobble $(<F) > ../$@
これでコマンドが別々のレシピ行に書かれていても、期待どおりに動くようになります。
.ONESHELL が指定されている場合、特別な接頭文字(「@」「-」「+」)がチェックされるのはレシピの最初の1行だけです。それ以降の行は、SHELL が起動されるとき、特別な文字もレシピ行に含まれたままになります。もしレシピをこれらの特別な文字で始めたい場合は、最初の行の先頭文字にならないよう工夫する必要があります(コメントなどを足すとよいでしょう)。例えば、次の例は Perl では構文エラーになります。最初の「@」が make に取り除かれてしまうからです:
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
@f = qw(a b c);
print "@f\n";
しかし、次のどちらの書き方でも正しく動きます:
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
# 最初の行の先頭文字が "@" にならないようにする
@f = qw(a b c);
print "@f\n";
あるいは
.ONESHELL:
SHELL = /usr/bin/perl
.SHELLFLAGS = -e
show :
my @f = qw(a b c);
print "@f\n";
特別な機能として、SHELL が POSIX 形式のシェルだと判断された場合、「内部の」レシピ行(2行目以降)にある特別な接頭文字は、レシピが処理される前に取り除かれます。この機能は、既存の makefile に .ONESHELL という特別なターゲットを追加しても、大幅な修正なしに正しく動かせるようにすることを意図したものです。これらの特別な接頭文字は POSIX シェルスクリプトでは行頭に置けないので、機能が失われることもありません。例えば、次は期待どおりに動きます:
.ONESHELL:
foo : bar/lose
@cd $(@D)
@gobble $(@F) > ../$@
とはいえ、この特別な機能があっても、.ONESHELL を使った makefile は、気づくほどの違いが生じる形で挙動が変わることがあります。例えば、通常はレシピのどの行が失敗してもルールが失敗し、それ以降のレシピ行は処理されません。ところが .ONESHELL のもとでは、最後のレシピ行以外のどこかで失敗が起きても make はそれに気づきません。.SHELLFLAGS を変更してシェルに -e オプションを足せば、コマンドラインのどこで失敗してもシェルが失敗するようになりますが、それ自体がレシピの挙動を変えてしまうこともあります。最終的には、.ONESHELL で動くようにレシピ行を堅牢に書き直す必要が出てくるかもしれません。
シェルとして使われるプログラムは、変数 SHELL から取得されます。この変数が makefile で設定されていない場合は、プログラム /bin/sh がシェルとして使われます。シェルに渡される引数は、変数 .SHELLFLAGS から取得されます。.SHELLFLAGS のデフォルト値は、通常は -c、POSIX 準拠モードでは -ec です。
ほとんどの変数とは違い、変数 SHELL は環境から設定されることが決してありません。これは、SHELL 環境変数が、対話的に使うシェルプログラムの個人的な好みを指定するために使われるからです。そうした個人的な選択が makefile の動作に影響してしまっては非常にまずいことになります。環境からの変数を参照してください。
さらに、makefile の中で SHELL を設定しても、その値は make が起動するレシピ行に対して環境変数としてエクスポートされません。代わりに、ユーザーの環境から受け継いだ値(もしあれば)がエクスポートされます。この挙動は、SHELL を明示的にエクスポートすれば変えられます(サブmakeへ変数を伝えるを参照)。そうすれば、レシピ行に環境変数として渡されるよう強制できます。
ただし、MS-DOS や MS-Windows では、環境にある SHELL の値が使われます。これらのシステムではほとんどのユーザーがこの変数を設定しないため、設定されている場合は make のために特に設定された可能性が高いからです。MS-DOS では、SHELL の設定が make に適していない場合、make に使わせたいシェルを変数 MAKESHELL に設定できます。設定されていれば、SHELL の値の代わりにそれがシェルとして使われます。
MS-DOS や MS-Windows でシェルを選ぶ仕組みは、他のシステムよりずっと複雑です。
MS-DOS では、SHELL が設定されていない場合、(常に設定されている)変数 COMSPEC の値が代わりに使われます。
makefile 内で変数 SHELL を設定する行の処理は、MS-DOS では異なります。標準のシェルである command.com は機能が滑稽なほど貧弱で、make のユーザーの多くは別のシェルをインストールしがちです。そのため MS-DOS では、make は SHELL の値を調べ、それが Unix 形式のシェルを指しているか DOS 形式のシェルを指しているかによって挙動を変えます。これにより、SHELL が command.com を指していてもそれなりの機能が使えるようになっています。
SHELL が Unix 形式のシェルを指している場合、MS-DOS の make はさらに、そのシェルが実際に見つかるかどうかを確認します。見つからなければ、SHELL を設定するその行を無視します。MS-DOS では、GNU make は次の場所からシェルを探します:
SHELL の値が指すまさにその場所。例えば makefile が「SHELL = /bin/sh」と指定していれば、make は現在のドライブの /bin ディレクトリを調べます。
PATH 変数に含まれる各ディレクトリを、順に。
調べる各ディレクトリで、make はまずその名前のファイルそのもの(上の例では sh)を探します。それが見つからなければ、実行ファイルを示す既知の拡張子をつけたファイルも、そのディレクトリ内で探します。例えば .exe、.com、.bat、.btm、.sh などです。
これらの試みのいずれかが成功すれば、SHELL の値は見つかったシェルのフルパス名に設定されます。しかし、どれも見つからなければ SHELL の値は変更されず、結果としてそれを設定する行は実質的に無視されます。これは、Unix 形式のシェルが実際にインストールされているシステムでのみ、make がそのシェル特有の機能をサポートするようにするためです。
なお、このシェルの拡張探索は、SHELL が makefile から設定された場合に限られます。環境やコマンドラインで設定された場合は、Unix と同じように、シェルのフルパス名を自分で設定することが期待されます。
上記の DOS 固有の処理の結果、(多くの Unix の makefile がそうしているように)「SHELL = /bin/sh」を含む makefile は、例えば PATH 上のどこかのディレクトリに sh.exe をインストールしてあれば、MS-DOS でもそのまま動きます。
GNU make は複数のレシピを同時に実行する方法を心得ています。通常、make は一度に1つのレシピしか実行せず、それが終わるのを待ってから次を実行します。しかし「-j」または「--jobs」オプションを使うと、make に多数のレシピを同時に実行するよう指示できます。makefile の中から、一部またはすべてのターゲットについて並列実行を抑止することもできます(並列実行を無効にするを参照)。
MS-DOS では、そのシステムがマルチプロセシングをサポートしていないため、「-j」オプションは効果がありません。
「-j」オプションのあとに整数を続けると、それが一度に実行するレシピの数になります。これをジョブスロットの数と呼びます。「-j」オプションのあとに整数らしきものが続かなければ、ジョブスロットの数に制限はなくなります。ジョブスロットのデフォルト数は1で、これは逐次実行(一度に1つずつ)を意味します。
再帰的な make の起動を扱う際には、並列実行に関する問題が生じます。これについて詳しくは、サブmakeへオプションを伝えるを参照してください。
あるレシピが失敗し(シグナルで kill されるか、ゼロ以外のステータスで終了し)、そのレシピでエラーが無視されない場合(レシピ中のエラーを参照)、同じターゲットを再生成するための残りのレシピ行は実行されません。レシピが失敗したときに「-k」または「--keep-going」オプションが指定されていなければ(オプション一覧を参照)、make は実行を中止します。make が(シグナルを含む)何らかの理由で終了する際に、子プロセスがまだ実行中であれば、実際に終了する前にそれらが終わるのを待ちます。
システムの負荷が高いときは、おそらく負荷が低いときより少ないジョブ数で動かしたいでしょう。「-l」オプションを使うと、make に対して、ロードアベレージに基づいて一度に実行するジョブ数を制限するよう指示できます。「-l」または「--max-load」オプションのあとには浮動小数点数を続けます。例えば、
-l 2.5
とすると、ロードアベレージが 2.5 を超えている間は make は2つ目以降のジョブを開始しません。数値を続けずに「-l」オプションを指定すると、それ以前の「-l」オプションで設定された負荷制限が解除されます。
より正確には、make がジョブを開始しようとするとき、すでに少なくとも1つのジョブが実行中であれば、現在のロードアベレージを確認します。それが「-l」で与えた制限値を下回っていなければ、make はロードアベレージがその制限を下回るか、他のジョブがすべて終わるまで待ちます。
デフォルトでは、負荷制限はありません。
makefile が、すべてのターゲット間の依存関係を完全かつ正確に定義していれば、make は並列実行が有効かどうかに関係なく、正しくゴールをビルドします。これが makefile の理想的な書き方です。
しかし、makefile 内の一部またはすべてのターゲットが並列実行できず、しかも make に伝えるための前提条件を追加するのが現実的でない、という場合もあります。そうしたときは、makefile はさまざまな方法で並列実行を無効にできます。
前提条件を持たない .NOTPARALLEL という特別なターゲットがどこかに指定されていると、並列設定に関係なく、その make インスタンス全体が逐次的に実行されます。例えば:
all: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL:
この場合、make をどう起動しても、ターゲット one、two、three は逐次的に実行されます。
.NOTPARALLEL という特別なターゲットが前提条件を持つ場合は、それらの前提条件のそれぞれが1つのターゲットとみなされ、それらのターゲットのすべての前提条件が逐次的に実行されます。なお、前提条件が逐次的に実行されるのは、このターゲットをビルドするときに限ります。もし別のターゲットが同じ前提条件を挙げていて、それが .NOTPARALLEL の対象でなければ、それらの前提条件は並列に実行されるかもしれません。例えば:
all: base notparallel base: one two three notparallel: one two three one two three: ; @sleep 1; echo $@ .NOTPARALLEL: notparallel
ここで「make -j base」はターゲット one、two、three を並列に実行しますが、「make -j notparallel」はそれらを逐次的に実行します。「make -j all」を実行すると、それらは並列に実行されます。base がそれらを前提条件として挙げていて、しかも逐次化されていないからです。
.NOTPARALLEL ターゲットにはコマンドを付けるべきではありません。
最後に、.WAIT という特別なターゲットを使えば、特定の前提条件の逐次化をきめ細かく制御できます。このターゲットが前提条件のリストに現れ、かつ並列実行が有効な場合、make は .WAIT の左側のすべての前提条件が完了するまで、.WAIT の右側の前提条件をいっさいビルドしません。例えば:
all: one two .WAIT three one two three: ; @sleep 1; echo $@
並列実行が有効な場合、make は one と two を並列にビルドしようとしますが、その両方が完了するまで three のビルドには取りかかりません。
.NOTPARALLEL に与えるターゲットと同じく、.WAIT が効果を持つのは、それが前提条件リストに現れるターゲットをビルドするときに限ります。同じ前提条件が .WAIT なしで他のターゲットにも現れていれば、それらは依然として並列に実行されるかもしれません。このため、ターゲット付きの .NOTPARALLEL も .WAIT も、並列実行を制御する手段としては前提条件の関係を定義するほど確実ではありません。とはいえ、これらは使いやすく、それほど複雑でない状況では十分でしょう。
.WAIT 前提条件は、そのルールのどの自動変数にも現れません。
移植性のために、makefile に実体としての .WAIT ターゲットを作っても構いませんが、この機能を使うのに必須ではありません。.WAIT ターゲットを作る場合は、前提条件やコマンドを持たせるべきではありません。
.WAIT 機能は他のバージョンの make でも実装されており、make の POSIX 標準でも規定されています。
複数のレシピを並列に実行すると、各レシピの出力は生成されたそばから現れます。その結果、異なるレシピからのメッセージが入り混じり、ときには同じ行に重なって現れることさえあります。これでは出力が非常に読みにくくなります。
これを避けるには「--output-sync」(「-O」)オプションを使えます。このオプションは、make が起動するコマンドの出力をいったん保存しておき、コマンドが完了したらまとめて表示するよう指示します。さらに、並列に実行されている再帰的な make の起動が複数ある場合、それらは互いに連携し、一度に1つだけが出力を生成するようにします。
作業ディレクトリの表示が有効になっている場合(「--print-directory」オプションを参照)、各出力グループの前後に「ディレクトリに入る/出る」のメッセージが表示されます。これらのメッセージを見たくない場合は、「--no-print-directory」オプションを MAKEFLAGS に追加してください。
出力を同期する粒度には4つのレベルがあり、オプションに引数を与えて指定します(例:「-Oline」や「--output-sync=recurse」)。
noneこれがデフォルトです。すべての出力は生成されたそのまま直接送られ、同期は行われません。
lineレシピの個々の行からの出力がまとめられ、その行が完了したらすぐに表示されます。1つのレシピが複数行からなる場合、それらは他のレシピの行と入り混じることがあります。
target各ターゲットのレシピ全体からの出力がまとめられ、そのターゲットが完了したら表示されます。--output-sync または -O オプションを引数なしで与えた場合は、これがデフォルトになります。
recursemake の各再帰起動からの出力がまとめられ、その再帰起動が完了したら表示されます。
どのモードを選んでも、ビルド全体の所要時間は変わりません。違うのは出力の見え方だけです。
「target」モードと「recurse」モードはどちらも、ターゲットのレシピ全体の出力を集めて、レシピが完了したときに途切れなく表示します。両者の違いは、make の再帰起動を含むレシピの扱い方にあります(makeの再帰的な利用を参照)。再帰行を持たないレシピについては、「target」モードと「recurse」モードはまったく同じ挙動になります。
「recurse」モードを選ぶと、再帰的な make の起動を含むレシピは他のターゲットと同じように扱われます。すなわち、再帰的な make からの出力も含め、レシピからの出力は保存され、レシピ全体が完了したあとに表示されます。これにより、ある再帰的な make インスタンスがビルドしたすべてのターゲットの出力がひとまとまりになり、出力が理解しやすくなることがあります。しかし一方で、ビルド中に出力がまったく見えない長い時間が続き、そのあとに大量の出力がどっと現れる、ということにもつながります。ビルドの進行をリアルタイムで眺めるのではなく、後からビルドのログを見るような場合には、これが最適な選択肢かもしれません。
出力をリアルタイムで眺めている場合、ビルド中の長い沈黙はもどかしいものです。「target」出力同期モードは、標準的な方法で make が再帰的に起動されようとしていることを検出し、その行の出力は同期しません。再帰的な make が自分のターゲットについて同期を行い、それぞれの出力は完了したときにすぐに表示されます。なお、レシピの再帰行からの出力は同期されない点に注意してください(例えば、再帰行が make を実行する前にメッセージを表示する場合、そのメッセージは同期されません)。
「line」モードは、make の出力を監視してレシピの開始・完了を追跡するフロントエンドにとって便利です。
make が起動するプログラムの中には、出力先が端末なのかファイルなのかを判断して挙動を変えるものがあります(しばしば「対話的(interactive)」モードと「非対話的(non-interactive)」モードと呼ばれます)。例えば、出力に色を付けられる多くのプログラムは、端末に書き込んでいないと判断すると色付けを行いません。makefile がこのようなプログラムを起動する場合、出力同期オプションを使うと、最終的には出力が端末に届くにもかかわらず、そのプログラムは自分が「非対話的」モードで動いていると思い込んでしまいます。
2つのプロセスが同じデバイスから同時に入力を受け取ることはできません。一度に1つのレシピだけが端末から入力を受け取るようにするため、make は実行中のレシピのうち1つを除いて、すべての標準入力ストリームを無効化します。別のレシピが標準入力から読み取ろうとすると、たいていは致命的なエラー(「Broken pipe」シグナル)になります。
どのレシピが有効な標準入力ストリーム(端末、あるいは make の標準入力をリダイレクトした先から来るもの)を得るかは予測できません。最初に実行されたレシピが常に最初にそれを得て、そのレシピが終わったあとに最初に開始されたレシピが次にそれを得る、という具合になります。
より良い代替手段が見つかれば、make のこの部分の動作は変更するつもりです。それまでの間は、並列実行機能を使っているなら、どのレシピも標準入力を使わないようにすべきです。逆に、この機能を使っていなければ、標準入力はすべてのレシピで普通に機能します。
各シェル起動が戻るたびに、make はその終了ステータスを調べます。シェルが正常に完了した(終了ステータスがゼロの)場合、レシピの次の行が新しいシェルで実行されます。最後の行が終われば、そのルールは完了です。
エラーがあった(終了ステータスがゼロ以外だった)場合、make は現在のルールを、場合によってはすべてのルールを、あきらめます。
あるレシピ行が失敗しても、それが問題を意味しないこともあります。例えば、あるディレクトリが存在することを確実にするために mkdir コマンドを使うことがあります。そのディレクトリがすでに存在していると mkdir はエラーを報告しますが、おそらくそれでも make には処理を続けてほしいでしょう。
レシピ行のエラーを無視するには、その行の文の先頭(最初のタブのあと)に「-」を書きます。「-」はその行が実行のためにシェルに渡される前に取り除かれます。
例えば、
clean:
-rm -f *.o
これにより、rm がファイルを削除できなくても make は処理を続けます。
make を「-i」または「--ignore-errors」フラグ付きで実行すると、すべてのルールのすべてのレシピでエラーが無視されます。makefile に特別なターゲット .IGNORE のルールを書いても、前提条件がなければ同じ効果があります。こちらのほうが柔軟性は劣りますが、ときに便利です。
「-」や「-i」フラグによってエラーが無視される場合、make はエラーによる戻りを成功と同じように扱います。ただし、シェルが終了したステータスコードを知らせるメッセージを表示し、エラーが無視されたことを伝えます。
無視するよう指示されていないエラーが発生した場合、それは現在のターゲットが正しく再生成できないこと、そして直接的であれ間接的であれそのターゲットに依存する他のターゲットも再生成できないことを意味します。これらのターゲットの前提条件が達成されていないため、これ以上レシピは実行されません。
通常、この状況では make はただちにあきらめ、ゼロ以外のステータスを返します。しかし「-k」または「--keep-going」フラグが指定されていると、make はあきらめてゼロ以外のステータスを返す前に、保留中のターゲットの他の前提条件についても検討を続け、必要なら再生成します。例えば、あるオブジェクトファイルのコンパイルでエラーが起きたあとでも、「make -k」は——それらをリンクすることがもう不可能だと分かっていても——他のオブジェクトファイルのコンパイルを続けます。オプション一覧を参照してください。
通常の挙動は、あなたの目的が指定したターゲットを最新の状態にすることだ、と想定しています。それが不可能だと make が知った時点で、ただちに失敗を報告したほうがよい、というわけです。「-k」オプションは、本当の目的はプログラムに加えた変更をできるだけ多くテストすることだ——おそらく、いくつもの独立した問題を見つけ出し、次にコンパイルを試みる前にそれらをまとめて直したい——と告げるものです。Emacs の compile コマンドがデフォルトで「-k」フラグを渡すのはこのためです。
通常、レシピ行が失敗したとき、もしそれがターゲットファイルを少しでも変更していたら、そのファイルは壊れていて使えません——少なくとも完全には更新されていません。それでもファイルのタイムスタンプは「今が最新だ」と示しているので、次に make を実行したとき、そのファイルを更新しようとはしません。これは、シェルがシグナルで kill されたときとまったく同じ状況です。makeの中断と強制終了を参照してください。そこで一般的に正しい対処は、ファイルを変更し始めたあとでレシピが失敗した場合、そのターゲットファイルを削除することです。make は、.DELETE_ON_ERROR がターゲットとして現れていれば、これを行います。これはほとんどの場合あなたが make に望むことですが、歴史的な慣習ではありません。そのため互換性のために、明示的に要求しなければなりません。
シェルの実行中に make が致命的なシグナルを受け取った場合、そのレシピが更新するはずだったターゲットファイルを削除することがあります。これは、ターゲットファイルの最終更新時刻が、make が最初に確認したときから変化している場合に行われます。
ターゲットを削除する目的は、次に make が実行されたときに、それが最初から作り直されるようにすることです。なぜでしょうか? コンパイラの実行中に Ctrl-c を押し、ちょうどオブジェクトファイル foo.o を書き始めたところだったとしましょう。Ctrl-c はコンパイラを kill し、その結果、ソースファイル foo.c より新しい最終更新時刻を持つ、不完全なファイルが残ります。しかし make も Ctrl-c シグナルを受け取り、この不完全なファイルを削除します。もし make がこれをしなかったら、次に make を起動したとき、foo.o は更新不要だと判断してしまいます——その結果、リンカが半分欠けたオブジェクトファイルをリンクしようとして、奇妙なエラーメッセージを出すことになるでしょう。
このようなターゲットファイルの削除を防ぐには、特別なターゲット .PRECIOUS がそのファイルに依存するようにします。make はターゲットを再生成する前に、それが .PRECIOUS の前提条件に現れているかどうかを確認し、それによってシグナルが発生したときにそのターゲットを削除すべきかどうかを決めます。こうしたい理由としては、そのターゲットが何らかのアトミックな(分割不可能な)方法で更新される、あるいは更新時刻を記録するためだけに存在する(中身はどうでもよい)、あるいは他の種類の問題を防ぐために常に存在していなければならない、などが考えられます。
make は後始末に最善を尽くしますが、後始末が不可能な状況もあります。例えば、make が捕捉できないシグナルで kill されることがあります。あるいは、make が起動したプログラムの1つが kill されたりクラッシュしたりして、タイムスタンプ上は最新だが壊れたターゲットファイルを残すことがあります。この場合 make は、その失敗がターゲットの後始末を必要とすることに気づきません。あるいは、make 自身がバグに遭遇してクラッシュすることもあります。
こうした理由から、防御的なレシピ(defensive recipe)を書くのが最善です。防御的なレシピは、たとえ失敗しても壊れたターゲットを残しません。最もよくあるのは、ターゲットを直接更新するのではなく一時ファイルを作り、それから一時ファイルを最終的なターゲット名にリネームするやり方です。一部のコンパイラはすでにこのように動くので、その場合は防御的なレシピを書く必要はありません。
make の再帰的な利用とは、makefile の中で make をコマンドとして使うことを指します。この手法は、より大きなシステムを構成するさまざまなサブシステムごとに、別々の makefile を用意したい場合に役立ちます。例えば、独自の makefile を持つサブディレクトリ subdir があり、それを含むディレクトリの makefile からそのサブディレクトリに対して make を実行したいとしましょう。これは次のように書けば実現できます:
subsystem:
cd subdir && $(MAKE)
あるいは、同等の次の書き方でもよいでしょう(オプション一覧を参照):
subsystem:
$(MAKE) -C subdir
この例をそのままコピーすれば再帰的な make コマンドを書けますが、それらがどのように、そしてなぜそう動くのか、またサブmakeがトップレベルの make とどう関係するのかについて、知っておくべきことがたくさんあります。再帰的な make コマンドを起動するターゲットを「.PHONY」と宣言しておくと便利なこともあります(これが役立つ場面についてさらに詳しくは、偽りのターゲットを参照)。
便宜上、GNU make は起動時に(-C オプションの処理を終えたあと)、変数 CURDIR にカレントワーキングディレクトリのパス名を設定します。この値はその後 make によって触れられることはありません。特に、他のディレクトリのファイルを include しても CURDIR の値は変わらない点に注意してください。この値は、makefile で設定された場合と同じ優先順位を持ちます(デフォルトでは、環境変数 CURDIR がこの値をオーバーライドすることはありません)。なお、この変数を設定しても make の動作には影響しません(例えば、make がワーキングディレクトリを変更したりはしません)。
再帰的な make コマンドでは、明示的なコマンド名「make」ではなく、常に変数 MAKE を使うべきです。次のように書きます:
subsystem:
cd subdir && $(MAKE)
この変数の値は、make が起動されたときのファイル名です。このファイル名が /bin/make だったなら、実行されるレシピは「cd subdir && /bin/make」になります。トップレベルの makefile を実行するのに特別なバージョンの make を使っていれば、再帰的な起動でも同じ特別なバージョンが実行されます。
特別な機能として、ルールのレシピの中で変数 MAKE を使うと、「-t」(「--touch」)、「-n」(「--just-print」)、「-q」(「--question」)オプションの効果が変わります。変数 MAKE を使うことは、レシピ行の先頭に「+」文字を置くのと同じ効果を持ちます。レシピを実行する代わりにを参照してください。この特別な機能は、MAKE 変数がレシピに直接現れる場合にのみ有効になります。MAKE 変数が別の変数の展開を通じて参照される場合には適用されません。その場合は、これらの特別な効果を得るために「+」トークンを使わなければなりません。
上の例で「make -t」コマンドを考えてみましょう。(「-t」オプションは、実際にはレシピを何も実行せずにターゲットを最新の状態としてマークします。レシピを実行する代わりにを参照。)「-t」の通常の定義に従えば、この例で「make -t」コマンドは subsystem という名前のファイルを作るだけで、他には何もしないはずです。本当にやってほしいのは「cd subdir && make -t」を実行することですが、それにはレシピを実行する必要があり、「-t」はレシピを実行するなと言っています。
この特別な機能が、望みどおりの動作をさせてくれます。ルールのレシピ行に変数 MAKE が含まれている限り、フラグ「-t」「-n」「-q」はその行には適用されません。MAKE を含むレシピ行は、ほとんどのレシピを実行させないフラグが存在しても、通常どおり実行されます。そして通常の MAKEFLAGS の仕組みがフラグをサブmakeへ渡すので(サブmakeへオプションを伝えるを参照)、ファイルをタッチするように、あるいはレシピを表示するように、というあなたの要求がサブシステムへ伝播します。
トップレベルの make の変数値は、明示的に要求すれば、環境を通じてサブmakeへ渡せます。これらの変数はサブmakeではデフォルト値として定義されますが、「-e」スイッチ(オプション一覧を参照)を使わない限り、サブmakeが使う makefile で定義された変数をオーバーライドすることはありません。
変数を下位へ渡す、つまりエクスポート(export)するために、make はレシピの各行を実行する環境にその変数とその値を追加します。サブmakeは、今度はその環境を使って自分の変数値の表を初期化します。環境からの変数を参照してください。
明示的な要求がある場合を除き、make が変数をエクスポートするのは、その変数が最初から環境で定義されている場合、あるいはコマンドラインで設定されていてその名前が英字・数字・アンダースコアだけからなる場合に限られます。
make 変数 SHELL の値はエクスポートされません。代わりに、起動時の環境にある SHELL 変数の値がサブmakeへ渡されます。後述する export ディレクティブを使えば、make に SHELL の値をエクスポートさせることができます。シェルを選ぶを参照してください。
特別な変数 MAKEFLAGS は常にエクスポートされます(unexport しない限り)。MAKEFILES は、何か値を設定すればエクスポートされます。
make は、コマンドラインで定義された変数値を自動的に下位へ渡します。これらを MAKEFLAGS 変数に入れることで行います。サブmakeへオプションを伝えるを参照してください。
変数が make によってデフォルトで作られたものである場合(暗黙のルールで使われる変数を参照)、通常はそれらは下位へ渡されません。サブmakeはそれらを自分で定義します。
特定の変数をサブmakeへエクスポートしたい場合は、次のように export ディレクティブを使います:
export variable …
逆に、変数がエクスポートされるのを防ぎたい場合は、次のように unexport ディレクティブを使います:
unexport variable …
どちらの形式でも、export と unexport への引数は展開されます。したがって、(un)export すべき変数名(のリスト)に展開される変数や関数を指定することもできます。
便利なことに、変数の定義とエクスポートを同時に行うこともできます:
export variable = value
これは次と同じ結果になります:
variable = value export variable
また、
export variable := value
は次と同じ結果になります:
variable := value export variable
同様に、
export variable += value
は次とまったく同じです:
variable += value export variable
変数にテキストを追加するを参照してください。
お気づきかもしれませんが、export と unexport ディレクティブは、make においてシェル sh での働き方とまったく同じように働きます。
すべての変数をデフォルトでエクスポートしたい場合は、export を単独で使えます:
export
これは make に対して、export や unexport ディレクティブで明示的に言及されていない変数もエクスポートすべきだ、と告げます。unexport ディレクティブで指定された変数は、それでもエクスポートされません。
export を単独で使ったときに引き起こされる挙動は、古いバージョンの GNU make ではデフォルトでした。もし makefile がこの挙動に依存していて、古いバージョンの make と互換性を保ちたい場合は、export ディレクティブを使う代わりに、特別なターゲット .EXPORT_ALL_VARIABLES を makefile に追加できます。これは古い make では無視されますが、export ディレクティブのほうは古い make では構文エラーを引き起こします。
export 単独や .EXPORT_ALL_VARIABLES を使ってデフォルトで変数をエクスポートする場合、名前が英数字とアンダースコアだけからなる変数だけがエクスポートされます。それ以外の変数をエクスポートするには、export ディレクティブで個別に名前を挙げなければなりません。
変数の値を環境に追加するには、その値を展開する必要があります。変数の展開に副作用がある場合(info や eval などの関数のように)、コマンドが起動されるたびにその副作用が現れます。これは、そうした変数の名前を、デフォルトではエクスポートされない名前にすることで避けられます。とはいえ、より良い解決策は、この「デフォルトでエクスポート」機能をまったく使わず、代わりに該当する変数を名前を挙げて明示的に export することです。
unexport を単独で使うと、make に対してデフォルトでは変数をエクスポートしないように告げられます。これはデフォルトの挙動なので、これが必要になるのは(おそらくインクルードした makefile などで)export が以前に単独で使われていた場合だけです。一部のレシピでは変数をエクスポートし、他のレシピではしない、というように export と unexport を単独で使い分けることはできません。単独で現れた最後の export または unexport ディレクティブが、make の実行全体の挙動を決めます。
特別な機能として、変数 MAKELEVEL はレベルからレベルへ下位に渡されるときに変化します。この変数の値は、レベルの深さを十進数で表した文字列です。値はトップレベルの make で「0」、サブmakeで「1」、サブサブmakeで「2」、という具合です。この増加は、make がレシピのための環境をセットアップするときに起こります。
MAKELEVEL の主な用途は、条件付きディレクティブの中でそれをテストすることです(Makefileの条件付き部分を参照)。こうすることで、再帰的に実行された場合とあなたが直接実行した場合とで挙動を変える makefile を書けます。
変数 MAKEFILES を使うと、すべてのサブmakeコマンドに追加の makefile を使わせることができます。MAKEFILES の値は、空白で区切られたファイル名のリストです。この変数が外側のレベルの makefile で定義されていると、環境を通じて下位へ渡されます。そしてそれは、サブmakeが通常のまたは指定された makefile を読む前に読み込む、追加の makefile のリストとして機能します。変数MAKEFILESを参照してください。
「-s」や「-k」のようなフラグは、変数 MAKEFLAGS を通じて自動的にサブmakeへ渡されます。この変数は、make が受け取ったフラグの文字を含むように make によって自動的にセットアップされます。したがって「make -ks」を実行すると、MAKEFLAGS は「ks」という値になります。
その結果、すべてのサブmakeは環境に MAKEFLAGS の値を受け取ります。それに応じて、サブmakeはその値からフラグを取り出し、あたかも引数として与えられたかのように処理します。オプション一覧を参照してください。これは、他の環境変数とは異なり、環境で指定された MAKEFLAGS が makefile で指定された MAKEFLAGS より優先される、ということを意味します。
MAKEFLAGS の値は、引数を取らない1文字オプションを表す文字の(空でもよい)集まりに続けて、スペースと、引数を取るオプションや長い名前のオプションを並べたものです。あるオプションに1文字版と長い名前版の両方がある場合は、常に1文字版が優先されます。コマンドラインに1文字オプションが1つもない場合、MAKEFLAGS の値はスペースで始まります。
同様に、コマンドラインで定義された変数も MAKEFLAGS を通じてサブmakeへ渡されます。MAKEFLAGS の値の中で「=」を含む単語を、make はあたかもコマンドラインに現れた変数定義であるかのように扱います。変数のオーバーライドを参照してください。
オプション「-C」「-f」「-o」「-W」は MAKEFLAGS に入れられません。これらのオプションは下位へ渡されません。
「-j」オプションは特別なケースです(並列実行を参照)。これに何らかの数値「N」を設定し、お使いの OS がそれをサポートしている場合(ほとんどの UNIX システムはサポートしますが、それ以外はたいていしません)、親の make とすべてのサブmakeが連携して、それら全体で同時に実行されるジョブが「N」個だけになるようにします。なお、再帰的とマークされたジョブ(レシピを実行する代わりにを参照)は、合計ジョブ数に数えられません(さもなければ「N」個のサブmakeが動いてしまい、実際の作業のためのスロットが残らなくなってしまうからです!)。
お使いの OS が上記の連携をサポートしていない場合、「-j」は MAKEFLAGS に追加されません。そのため、サブmakeは非並列モードで実行されます。もし「-j」オプションがサブmakeへ渡されてしまうと、要求した数よりずっと多くのジョブが並列に実行されることになるでしょう。「-j」を数値引数なしで与えた場合は、できるだけ多くのジョブを並列に実行するという意味であり、これは下位へ渡されます。無限がいくつあっても1つの無限と変わらないからです。
他のフラグを下位へ渡したくない場合は、次のように MAKEFLAGS の値を変更しなければなりません:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
コマンドラインの変数定義は、実際には変数 MAKEOVERRIDES に現れ、MAKEFLAGS はこの変数への参照を含んでいます。フラグは通常どおり下位へ渡したいが、コマンドラインの変数定義は渡したくない、という場合は、次のように MAKEOVERRIDES を空にリセットできます:
MAKEOVERRIDES =
これは普段するようなことではありません。しかし、一部のシステムは環境のサイズに小さな固定の上限を持っており、MAKEFLAGS の値にこれほど多くの情報を入れるとそれを超えてしまうことがあります。「Arg list too long」というエラーメッセージが出たら、これが原因かもしれません。(POSIX.2 に厳密に準拠する場合、makefile に特別なターゲット「.POSIX」が現れていると、MAKEOVERRIDES を変更しても MAKEFLAGS には影響しません。おそらく、これを気にする必要はないでしょう。)
歴史的な互換性のために、よく似た変数 MFLAGS も存在します。これは MAKEFLAGS と同じ値を持ちますが、コマンドラインの変数定義を含まない点と、空でない限り常にハイフンで始まる点が異なります(MAKEFLAGS がハイフンで始まるのは、「--warn-undefined-variables」のように1文字版のないオプションで始まる場合だけです)。MFLAGS は伝統的に、次のように再帰的な make コマンドの中で明示的に使われていました:
subsystem:
cd subdir && $(MAKE) $(MFLAGS)
しかし今では MAKEFLAGS があるので、この使い方は不要です。makefile を古い make プログラムと互換性を保ちたい場合はこの手法を使ってください。より新しいバージョンの make でも問題なく動きます。
MAKEFLAGS 変数は、「-k」(オプション一覧を参照)のような特定のオプションを make を実行するたびに設定しておきたい場合にも便利です。環境に MAKEFLAGS の値を入れておくだけです。makefile の中で MAKEFLAGS を設定して、その makefile で有効にすべき追加のフラグを指定することもできます。(なお、MFLAGS はこの用途には使えません。あの変数は互換性のためだけに設定されるもので、あなたが設定した値を make はいかなる形でも解釈しません。)
make が(環境からであれ makefile からであれ)MAKEFLAGS の値を解釈するとき、まず値がハイフンで始まっていなければハイフンを先頭に付けます。次に値を空白で区切られた単語に切り分け、それらの単語をあたかもコマンドラインで与えられたオプションであるかのように解析します(ただし「-C」「-f」「-h」「-o」「-W」とそれらの長い名前版は無視され、不正なオプションでもエラーになりません)。
MAKEFLAGS を環境に入れる場合は、make の動作を激変させ、makefile や make 自体の目的を台無しにするようなオプションを決して含めないように気をつけてください。例えば「-t」「-n」「-q」オプションをこれらの変数のいずれかに入れると、悲惨な結果を招きかねず、少なくとも驚くような、そしておそらくはうっとうしい効果を確実にもたらします。
GNU make 以外の実装の make も使いたいので、GNU make 固有のフラグを MAKEFLAGS 変数に追加したくない、という場合は、代わりに GNUMAKEFLAGS 変数にそれらを追加できます。この変数は MAKEFLAGS の直前に、MAKEFLAGS と同じやり方で解析されます。再帰的な make へ渡す MAKEFLAGS を組み立てるとき、make は GNUMAKEFLAGS から取ったものも含め、すべてのフラグを含めます。その結果、GNUMAKEFLAGS を解析したあと、GNU make は再帰中にフラグが重複するのを避けるために、この変数を空文字列に設定します。
GNUMAKEFLAGS には、makefile の挙動を実質的に変えないフラグだけを使うのが最善です。makefile がどのみち GNU Make を必要とするなら、単に MAKEFLAGS を使ってください。「--no-print-directory」や「--output-sync」のようなフラグは GNUMAKEFLAGS に適しているかもしれません。
何段階もの再帰的な make 起動を使う場合、「-w」または「--print-directory」オプションを使うと、make がディレクトリの処理を始めるときと終えるときに、それぞれのディレクトリを表示してくれるので、出力がずっと理解しやすくなります。例えば、ディレクトリ /u/gnu/make で「make -w」を実行すると、make は他のことを行う前に次の形式の行を表示し:
make: Entering directory `/u/gnu/make'.
処理が完了したときに次の形式の行を表示します:
make: Leaving directory `/u/gnu/make'.
通常、このオプションを指定する必要はありません。「make」が自動でやってくれるからです。「-w」は、「-C」オプションを使ったときと、サブmakeで自動的にオンになります。ただし、サイレントにすると指示する「-s」も同時に使っている場合や、明示的に無効化する「--no-print-directory」を使っている場合は、make は「-w」を自動的にオンにしません。
同じコマンドの並びがさまざまなターゲットの生成に役立つ場合、それを define ディレクティブで定型シーケンス(canned sequence)として定義し、それらのターゲットのレシピからその定型シーケンスを参照できます。定型シーケンスは実際には変数なので、その名前は他の変数名と衝突してはいけません。
定型レシピを定義する例を示します:
define run-yacc = yacc $(firstword $^) mv y.tab.c $@ endef
ここで run-yacc は定義される変数の名前です。endef が定義の終わりを示し、その間の行がコマンドです。define ディレクティブは定型シーケンス内の変数参照や関数呼び出しを展開しません。「$」文字、括弧、変数名などはすべて、定義しようとしている変数の値の一部になります。define の完全な説明については、複数行変数の定義を参照してください。
この例の最初のコマンドは、この定型シーケンスを使うルールの最初の前提条件に対して Yacc を実行します。Yacc の出力ファイルは常に y.tab.c という名前になります。2番目のコマンドは、その出力をルールのターゲットファイル名へ移動します。
定型シーケンスを使うには、その変数をルールのレシピに代入します。他の変数と同じように代入できます(変数参照の基本を参照)。define で定義された変数は再帰展開変数なので、define の中に書いたすべての変数参照は、今ここで展開されます。例えば:
foo.c : foo.y
$(run-yacc)
run-yacc の値の中に「$^」が現れると、そこには「foo.y」が代入され、「$@」には「foo.c」が代入されます。
これは現実的な例ですが、この特定のものは実際には不要です。というのも、make には、関係するファイル名に基づいてこれらのコマンドを割り出す暗黙のルールがあるからです(暗黙のルールを使うを参照)。
レシピの実行において、定型シーケンスの各行は、あたかもその行がルールの中に単独で、タブを前に付けて現れたかのように扱われます。特に、make は各行ごとに別々のサブシェルを起動します。コマンド行に作用する特別な接頭文字(「@」「-」「+」)を、定型シーケンスの各行で使えます。ルールのレシピの書き方を参照してください。例えば、次の定型シーケンスを使うと:
define frobnicate = @echo "frobnicating target $@" frob-step-1 $< -o $@-step-1 frob-step-2 $@-step-1 -o $@ endef
make は最初の行、すなわち echo コマンドをエコー表示しません。しかし続く2つのレシピ行はエコー表示します。
一方、定型シーケンスを参照するレシピ行に付けた接頭文字は、そのシーケンスのすべての行に適用されます。したがって、次のルール:
frob.out: frob.in
@$(frobnicate)
はどのレシピ行もエコー表示しません。(「@」の完全な説明については、レシピのエコー表示を参照してください。)
何もしないレシピを定義すると便利なことがあります。これは、空白だけからなるレシピを与えるだけで実現できます。例えば:
target: ;
これは target に対して空のレシピを定義します。レシピ接頭文字で始まる行で空のレシピを定義することもできますが、そういう行は空に見えてしまうので、紛らわしくなります。
何もしないレシピをなぜ定義したいのか、不思議に思うかもしれません。これが役立つ理由の1つは、ターゲットが暗黙のレシピ(暗黙のルールや .DEFAULT 特別ターゲットから来るもの。暗黙のルールを使うと最後の手段としてのデフォルトルールを定義するを参照)を受け取らないようにするためです。
空のレシピは、別のレシピの副作用として作られるターゲットについて、エラーを避けるためにも使えます。ターゲットが存在しない場合、空のレシピがあれば、make はそのターゲットの作り方が分からないと文句を言わず、そのターゲットは古い状態だとみなします。
実体のあるファイルではなく、前提条件を再生成させるためだけに存在するターゲットに対して、空のレシピを定義したくなるかもしれません。しかし、これは最善のやり方ではありません。ターゲットファイルが実際に存在する場合、前提条件が正しく再生成されないことがあるからです。これを行うより良い方法については、偽りのターゲットを参照してください。