前へ | 次へ | 目次 | 英語原版(gnu.org)

4 ルールの書き方

 ルール(rule)とは makefile の中に書かれるもので、特定のファイル——そのルールのターゲット(target)と呼ばれます(たいていは1つのルールにつき1つです)——を、いつ、どのように作り直すかを指示するものです。ルールには、ターゲットの前提条件(prerequisite、旧称:依存関係)となる別のファイルの一覧と、ターゲットを生成または更新するために使うレシピ(recipe、ルールの実行内容)を書きます。

 ルールを書く順序は、原則として意味を持ちません。ただしデフォルトゴール——あなたが特に何も指定しなかったときに make が処理対象とするターゲット——を決める場合だけは例外です。デフォルトゴールは、最初の makefile の最初のルールにある最初のターゲットになります。ここにはさらに2つの例外があります。1つは、ピリオドで始まるターゲットは、スラッシュ「/」を1つ以上含んでいない限りデフォルトにはならないこと。もう1つは、パターンルールを定義するターゲットはデフォルトゴールに一切影響しないことです。(パターンルールの定義と再定義の項を参照してください。)

 そのため通常は、最初のルールが「プログラム全体(あるいは makefile が記述するすべてのプログラム)をコンパイルするためのルール」になるように makefile を書きます(このターゲットには「all」という名前を付けることがよくあります)。ゴールを指定する引数の項をご覧ください。


4.1 ルールの例

 まずはルールの例を1つ見てみましょう:

foo.o : foo.c defs.h       # frob をいじるためのモジュール
        cc -c -g foo.c

 このルールのターゲットは foo.o で、前提条件は foo.cdefs.h です。レシピには「cc -c -g foo.c」という1つのコマンドが入っています。レシピの行はタブ文字で始めることで、それがレシピであると識別されます。

 このルールは、次の2つのことを述べています:


4.2 ルールの構文

 一般に、ルールは次のような形をしています:

targets : prerequisites
        recipe

 あるいは次の形です:

targets : prerequisites ; recipe
        recipe

 targets(ターゲット群)はファイル名で、スペースで区切ります。ここではワイルドカード文字を使えます(ファイル名にワイルドカードを使うの項を参照)。また「a(m)」という形の名前は、アーカイブファイル a の中のメンバ m を表します(ターゲットとしてのアーカイブメンバの項を参照)。ふつう1つのルールにターゲットは1つですが、複数にする理由がある場合もあります(1つのルールに複数のターゲットの項を参照)。

 recipe(レシピ)の行はタブ文字で始めます(あるいは .RECIPEPREFIX 変数の値の先頭文字で始めます。その他の特別な変数の項を参照)。レシピの最初の行は、前提条件の次の行にタブ文字を付けて置くこともできますし、同じ行にセミコロンを付けて置くこともできます。どちらの書き方でも効果は同じです。レシピの構文には、このほかにもいくつか違いがあります。ルール内でのレシピの書き方の項を参照してください。

 ドル記号は make の変数参照を始めるために使われるので、ターゲットや前提条件の中で本当にドル記号そのものを書きたいときは、「$$」のように2つ重ねて書かなければなりません(変数の使い方の項を参照)。さらに、二次展開(二次展開の項を参照)を有効にしていて、前提条件の一覧の中にドル記号そのものを書きたい場合は、実際には4つのドル記号(「$$$$」)を書く必要があります。

 長い行は、バックスラッシュに続けて改行を入れることで分割できます。ただし make は makefile の1行の長さに制限を設けていないので、これは必須ではありません。

 ルールは make に2つのことを伝えます。すなわち、ターゲットがいつ古くなるか(更新が必要になるか)と、必要なときにどう更新するか、です。

 古くなっているかどうかの判断基準は、prerequisites(前提条件)を使って指定します。前提条件は、スペースで区切ったファイル名からなります。(ここでもワイルドカードや、アーカイブメンバ(makeによるアーカイブファイルの更新の項を参照)を使えます。)ターゲットが存在しないか、あるいはいずれかの前提条件よりも古い(最終更新時刻を比べて)場合に、そのターゲットは古いと見なされます。考え方としては、ターゲットファイルの内容は前提条件の情報をもとに計算されるものなので、前提条件のどれかが変われば、既存のターゲットファイルの内容はもう正しいとは限らなくなる、ということです。

 どう更新するかは recipe(レシピ)で指定します。これはシェル(通常は「sh」)によって実行される1行以上のコマンドですが、いくつかの追加機能が備わっています(ルール内でのレシピの書き方の項を参照)。


4.3 前提条件の種類

 GNU make が理解する前提条件には2種類あります。1つは前節で説明した通常の前提条件、もう1つは順序のみ(order-only)の前提条件です。通常の前提条件は2つのことを述べます。第1に、レシピが呼び出される順序を定めます。ターゲットのすべての前提条件のレシピが完了してから、そのターゲットのレシピが始まります。第2に、依存関係を定めます。いずれかの前提条件がターゲットより新しければ、ターゲットは古いと見なされ、作り直さなければなりません。

 通常は、これがまさに望みどおりの動作でしょう。ターゲットの前提条件が更新されたら、ターゲットも更新されるべきだからです。

 しかしときには、「前提条件がターゲットより先にビルドされることは保証したいが、前提条件が更新されてもターゲットの更新は強制したくない」という場合があります。順序のみの前提条件は、こうした関係を作るために使います。順序のみの前提条件は、前提条件の一覧にパイプ記号(|)を置くことで指定します。パイプ記号の左側の前提条件は通常のもの、右側の前提条件は順序のみのものになります:

targets : normal-prerequisites | order-only-prerequisites

 もちろん、通常の前提条件の部分は空でも構いません。また、同じターゲットに対して複数行にわたって前提条件を宣言することもでき、それらは適切に連結されます(通常の前提条件は通常の前提条件の一覧へ、順序のみの前提条件は順序のみの前提条件の一覧へ追加されます)。なお、同じファイルを通常の前提条件と順序のみの前提条件の両方として宣言した場合は、通常の前提条件が優先される、という点に注意してください(通常の前提条件は、順序のみの前提条件の振る舞いを完全に包含しているからです)。

 順序のみの前提条件は、ターゲットが古いかどうかを判断する際には決してチェックされません。順序のみの前提条件が偽りのターゲット(偽りのターゲット(Phony)の項を参照)としてマークされていても、それによってターゲットが作り直されることはありません。

 例として、ターゲットを別のディレクトリに置きたい場合を考えましょう。そのディレクトリは make を実行する前には存在しないかもしれません。この状況では、ターゲットをそこに置く前にディレクトリを作っておきたいわけですが、ディレクトリのタイムスタンプはファイルの追加・削除・改名のたびに変わるので、ディレクトリのタイムスタンプが変わるたびにすべてのターゲットを作り直す、というのは絶対に避けたいところです。これを管理する1つの方法が順序のみの前提条件です。ディレクトリを、すべてのターゲットの順序のみの前提条件にしてしまうのです:

OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)

$(OBJDIR)/%.o : %.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

all: $(OBJS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
        mkdir $(OBJDIR)

 こうすると、objdir ディレクトリを作るルールは、必要であれば、どの「.o」がビルドされるよりも前に実行されます。しかし objdir ディレクトリのタイムスタンプが変わったというだけでは、どの「.o」もビルドされることはありません。


4.4 ファイル名にワイルドカードを使う

 1つのファイル名で、ワイルドカード文字を使って多数のファイルを指定できます。make のワイルドカード文字は「*」「?」「[…]」で、Bourne シェルのものと同じです。例えば *.c は、(作業ディレクトリ内の)名前が「.c」で終わるすべてのファイルの一覧を表します。

 ある式が複数のファイルにマッチした場合、その結果はソートされます。2 ただし、複数の式が全体としてソートされるわけではありません。例えば *.c *.h は、名前が「.c」で終わるすべてのファイル(ソート済み)を並べたあとに、名前が「.h」で終わるすべてのファイル(ソート済み)を並べます。

 ファイル名の先頭にある「~」という文字にも特別な意味があります。単独で使われた場合、またはスラッシュが続く場合は、あなたのホームディレクトリを表します。例えば ~/bin/home/you/bin に展開されます。「~」のあとに語が続く場合、その文字列は、その語が指すユーザのホームディレクトリを表します。例えば ~john/bin/home/john/bin に展開されます。ユーザごとのホームディレクトリを持たないシステム(MS-DOS や MS-Windows など)では、この機能は環境変数 HOME を設定することで模擬できます。

 ワイルドカードの展開は、ターゲットと前提条件の中では make によって自動的に行われます。レシピの中では、ワイルドカードの展開はシェルが担当します。それ以外の場面では、wildcard 関数で明示的に要求した場合にのみワイルドカードの展開が行われます。

 ワイルドカード文字の特別な意味は、その前にバックスラッシュを付けることで打ち消せます。したがって foo\*bar は、「foo」とアスタリスクと「bar」からなる名前を持つ特定のファイルを指すことになります。


4.4.1 ワイルドカードの使用例

 ワイルドカードはルールのレシピの中でも使えます。そこではシェルによって展開されます。例えば、次はすべてのオブジェクトファイルを削除するルールです:

clean:
        rm -f *.o

 ワイルドカードはルールの前提条件でも便利です。makefile に次のルールを書いておけば、「make print」を実行したときに、前回印刷したとき以降に変更された「.c」ファイルだけが印刷されます:

print: *.c
        lpr -p $?
        touch print

 このルールは print を空ターゲットファイルとして使っています。イベントを記録するための空ターゲットファイルの項を参照してください。(変更されたファイルだけを印刷するために、自動変数「$?」を使っています。自動変数の項を参照。)

 変数を定義するときには、ワイルドカードの展開は行われません。したがって、次のように書くと:

objects = *.o

 変数 objects の値は、文字どおりの文字列「*.o」になります。しかし、objects の値をターゲットや前提条件の中で使えば、そこでワイルドカードの展開が行われます。objects の値をレシピの中で使えば、レシピの実行時にシェルがワイルドカードの展開を行うかもしれません。objects に展開後の結果を設定したいのなら、代わりに次のようにします:

objects := $(wildcard *.o)

 wildcard 関数の項を参照してください。


4.4.2 ワイルドカードを使うときの落とし穴

 ここでは、ワイルドカード展開を素朴に使ったために、意図したとおりに動かない例を見てみましょう。実行可能ファイル foo をディレクトリ内のすべてのオブジェクトファイルから作りたいと考えて、次のように書いたとします:

objects = *.o

foo : $(objects)
        cc -o foo $(CFLAGS) $(objects)

 objects の値は、文字どおりの文字列「*.o」です。ワイルドカードの展開は foo のルールの中で起こるので、現に存在する.o」ファイルがそれぞれ foo の前提条件になり、必要に応じて再コンパイルされます。

 ところが、すべての「.o」ファイルを削除してしまったらどうなるでしょうか? ワイルドカードが1つもファイルにマッチしないと、それはそのまま残されます。すると foo は、奇妙な名前のファイル *.o に依存することになります。そんなファイルはまず存在しないので、make は「*.o をどう作ればよいか分からない」というエラーを出します。これは望んだ動作ではありません!

 実は、ワイルドカード展開で望みどおりの結果を得ることも可能ですが、それには wildcard 関数や文字列置換といった、もう少し高度な手法が必要です。wildcard 関数の項を参照してください。

 Microsoft のオペレーティングシステム(MS-DOS と MS-Windows)では、パス名のディレクトリ区切りにバックスラッシュを使います。次のような具合です:

  c:\foo\bar\baz.c

 これは Unix 形式の c:/foo/bar/baz.c と等価です(c: の部分はいわゆるドライブレターです)。make をこれらのシステムで実行すると、パス名の中で Unix 形式のスラッシュだけでなくバックスラッシュもサポートします。しかし、このサポートはワイルドカード展開には及びません。ワイルドカード展開では、バックスラッシュはクォート文字だからです。したがって、こうした場面では Unix 形式のスラッシュを必ず使わなければなりません。


4.4.3 wildcard 関数

 ワイルドカードの展開はルールの中では自動的に行われます。しかし、変数を設定するときや関数の引数の中では、ワイルドカードの展開は通常は行われません。そうした場所でワイルドカードの展開をしたいときは、次のように wildcard 関数を使う必要があります:

$(wildcard pattern…)

 この文字列を makefile のどこで使っても、与えられたファイル名パターンのいずれかにマッチする、実在するファイルの名前を、スペース区切りで並べた一覧に置き換えられます。あるパターンにマッチする実在ファイルが1つもなければ、そのパターンは wildcard 関数の出力から取り除かれます。これは、マッチしなかったワイルドカードがルールの中でどう振る舞うかとは異なる点に注意してください。ルールの中では、マッチしなかったワイルドカードは無視されず、そのまま使われます(ワイルドカードを使うときの落とし穴の項を参照)。

 ルールでのワイルドカード展開と同様に、wildcard 関数の結果もソートされます。ただしこれも、個々の式が別々にソートされます。したがって「$(wildcard *.c *.h)」は、「.c」にマッチするすべてのファイル(ソート済み)に続いて、「.h」にマッチするすべてのファイル(ソート済み)へと展開されます。

 wildcard 関数の使い道の1つは、次のようにディレクトリ内のすべての C ソースファイルの一覧を得ることです:

$(wildcard *.c)

 次のように、結果の中の「.c」という接尾辞を「.o」に置き換えれば、C ソースファイルの一覧をオブジェクトファイルの一覧に変えられます:

$(patsubst %.c,%.o,$(wildcard *.c))

 (ここではもう1つの関数 patsubst を使いました。文字列の置換と解析のための関数の項を参照してください。)

 したがって、ディレクトリ内のすべての C ソースファイルをコンパイルしてリンクする makefile は、次のように書けます:

objects := $(patsubst %.c,%.o,$(wildcard *.c))

foo : $(objects)
        cc -o foo $(objects)

 (これは C プログラムをコンパイルする暗黙のルールを利用しているので、各ファイルをコンパイルする明示的なルールを書く必要はありません。「:=」は「=」の変種で、その説明については変数の2つの種別の項を参照してください。)


4.5 前提条件をディレクトリから探す

 大規模なシステムでは、ソースをバイナリとは別のディレクトリに置きたいことがよくあります。makeディレクトリ探索(directory search)機能は、前提条件を見つけるために複数のディレクトリを自動的に探してくれることで、これを容易にします。ファイルをディレクトリ間で再配置しても、個々のルールを書き換える必要はなく、探索パスだけを変えれば済みます。


4.5.1 VPATH: すべての前提条件のための探索パス

 make 変数 VPATH の値は、make が探索すべきディレクトリの一覧を指定します。たいていは、カレントディレクトリにない前提条件ファイルがそのディレクトリに入っていることを想定しますが、makeVPATH を、ルールの前提条件とターゲットの両方の探索リストとして使います。

 したがって、ターゲットまたは前提条件として挙げられたファイルがカレントディレクトリに存在しない場合、makeVPATH に挙げられたディレクトリの中から、その名前のファイルを探します。そのいずれかでファイルが見つかれば、そのファイルが前提条件になることがあります(後述)。そうすると、前提条件の一覧にあるファイル名を、あたかもすべてカレントディレクトリに存在するかのように、ルールに書けるようになります。ディレクトリ探索を踏まえたレシピの書き方の項を参照してください。

 VPATH 変数の中では、ディレクトリ名をコロンまたは空白で区切ります。ディレクトリを並べた順序が、make が探索する順序になります。(MS-DOS と MS-Windows では、コロンはドライブレターのあとでパス名そのものに使えるため、VPATH のディレクトリ名の区切りにはセミコロンを使います。)

 例えば、

VPATH = src:../headers

 とすると、src../headers という2つのディレクトリを含むパスを指定したことになり、make はこの順に探索します。

 この VPATH の値のもとでは、次のルール、

foo.o : foo.c

 は、次のように書いたかのように解釈されます:

foo.o : src/foo.c

 ただしこれは、foo.c がカレントディレクトリには存在せず、src ディレクトリで見つかった場合の話です。


4.5.2 vpath ディレクティブ

 VPATH 変数に似ていますが、より選択的なのが vpath ディレクティブ(小文字である点に注意)です。これを使うと、特定のクラスのファイル名——あるパターンにマッチするもの——に対して探索パスを指定できます。これにより、あるクラスのファイル名には特定の探索ディレクトリを与え、別のファイル名には別のディレクトリ(あるいは何も与えない)とすることができます。

 vpath ディレクティブには3つの形式があります:

vpath pattern directories

pattern にマッチするファイル名のための探索パスとして directories を指定します。

探索パス directories は、探索すべきディレクトリの一覧で、VPATH 変数で使う探索パスと同じく、コロン(MS-DOS と MS-Windows ではセミコロン)または空白で区切ります。

vpath pattern

pattern に結びつけられた探索パスを消去します。

vpath

これまでに vpath ディレクティブで指定したすべての探索パスを消去します。

 vpath のパターンは、「%」文字を含む文字列です。この文字列は、探索対象になっている前提条件のファイル名にマッチしなければなりません。「%」文字は0文字以上の任意の文字列にマッチします(パターンルールと同様です。パターンルールの定義と再定義の項を参照)。例えば %.h は、.h で終わるファイルにマッチします。(「%」がない場合、パターンは前提条件に完全一致しなければなりませんが、これが役立つことはあまりありません。)

 vpath ディレクティブのパターン中の「%」文字は、前にバックスラッシュ(「\」)を付けてクォートできます。「%」文字をクォートしてしまうバックスラッシュ自体は、さらにバックスラッシュを重ねることでクォートできます。「%」文字や別のバックスラッシュをクォートするバックスラッシュは、パターンがファイル名と比較される前に取り除かれます。「%」文字をクォートするおそれのないバックスラッシュは、そのまま残されます。

 前提条件がカレントディレクトリに存在しない場合、ある vpath ディレクティブの pattern がその前提条件ファイルの名前にマッチすれば、そのディレクティブの directories が、VPATH 変数のディレクトリと同じように(そしてそれより先に)探索されます。

 例えば、

vpath %.h ../headers

 は、名前が .h で終わる前提条件がカレントディレクトリで見つからない場合、../headers ディレクトリの中を探すよう make に指示します。

 複数の vpath パターンが前提条件ファイルの名前にマッチする場合、make はマッチした vpath ディレクティブを1つずつ処理し、それぞれのディレクティブで挙げられたディレクトリをすべて探索します。make は複数の vpath ディレクティブを、makefile に現れる順序で処理します。同じパターンを持つ複数のディレクティブは、互いに独立しています。

 したがって、

vpath %.c foo
vpath %   blish
vpath %.c bar

 は、「.c」で終わるファイルを foo、次に blish、次に bar の順に探しますが、一方で、

vpath %.c foo:bar
vpath %   blish

 は、「.c」で終わるファイルを foo、次に bar、次に blish の順に探します。


4.5.3 ディレクトリ探索はどのように行われるか

 ディレクトリ探索(一般的なものか選択的なものかを問わず)によって前提条件が見つかったとき、見つかったパス名が、make が実際に前提条件の一覧であなたに提供するものになるとは限りません。ディレクトリ探索で発見したパスが捨てられる場合もあるのです。

 ディレクトリ探索で見つけたパスを残すか捨てるかを決めるために make が使うアルゴリズムは、次のとおりです:

  1. ターゲットファイルが makefile で指定されたパスに存在しない場合、ディレクトリ探索が行われます。
  2. ディレクトリ探索に成功すると、そのパスは残され、このファイルはひとまずターゲットとして記憶されます。
  3. このターゲットのすべての前提条件が、これと同じ方法で調べられます。
  4. 前提条件を処理したあと、ターゲットは作り直しが必要な場合もあれば、不要な場合もあります:
    1. ターゲットの作り直しが不要な場合、ディレクトリ探索で見つかったファイルへのパスが、このターゲットを含むどの前提条件の一覧でも使われます。要するに、make がターゲットを作り直す必要がなければ、ディレクトリ探索で見つかったパスを使う、ということです。
    2. ターゲットの作り直しが必要な(古い)場合、ディレクトリ探索で見つかったパス名は捨てられ、ターゲットは makefile で指定されたファイル名を使って作り直されます。要するに、make が作り直さなければならないときは、ターゲットはディレクトリ探索で見つかったディレクトリではなく、ローカルに作り直される、ということです。

 このアルゴリズムは複雑に見えるかもしれませんが、実際には、これがまさに望みどおりであることが非常に多いのです。

 他のバージョンの make は、もっと単純なアルゴリズムを使います。すなわち、ファイルが存在せず、ディレクトリ探索で見つかった場合は、ターゲットを作り直す必要があるかどうかに関係なく、常にそのパス名が使われます。したがって、ターゲットが作り直される場合は、ディレクトリ探索で発見したパス名の場所に作られます。

 実は、一部または全部のディレクトリでこの振る舞いを望むのであれば、GPATH 変数を使ってそれを make に伝えられます。

 GPATHVPATH と同じ構文・形式を持ちます(すなわち、空白またはコロンで区切ったパス名の一覧です)。古いターゲットが、GPATH にも現れるディレクトリでディレクトリ探索によって見つかった場合、そのパス名は捨てられません。ターゲットは、展開後のパスを使って作り直されます。


4.5.4 ディレクトリ探索を踏まえたレシピの書き方

 ディレクトリ探索によって前提条件が別のディレクトリで見つかっても、それでルールのレシピが変わることはありません。レシピは書かれたとおりに実行されます。したがって、make が前提条件を見つけたディレクトリでその前提条件を探すように、レシピは注意して書かなければなりません。

 これは「$^」などの自動変数を使って行います(自動変数の項を参照)。例えば「$^」の値は、ルールのすべての前提条件の一覧で、それらが見つかったディレクトリの名前も含みます。そして「$@」の値はターゲットです。したがって:

foo.o : foo.c
        cc -c $(CFLAGS) $^ -o $@

 (変数 CFLAGS は、暗黙のルールによる C コンパイルに渡すフラグを指定できるようにするために存在します。ここではすべての C コンパイルに一律で効くように、統一の意味でこれを使っています。暗黙のルールが使う変数の項を参照してください。)

 前提条件にはヘッダファイルも含まれることがよくありますが、それらはレシピの中では触れたくないものです。自動変数「$<」は、最初の前提条件だけを表します:

VPATH = src:../headers
foo.o : foo.c defs.h hack.h
        cc -c $(CFLAGS) $< -o $@

4.5.5 ディレクトリ探索と暗黙のルール

 VPATHvpath で指定したディレクトリの探索は、暗黙のルールが検討される際にも行われます(暗黙のルールの使い方の項を参照)。

 例えば、ファイル foo.o に明示的なルールがない場合、make は暗黙のルールを検討します。例えば、foo.c が存在すればそれをコンパイルする組み込みルールなどです。このファイルがカレントディレクトリに見当たらなければ、適切なディレクトリが探索されます。foo.c がそれらのディレクトリのいずれかに存在する(あるいは makefile で言及されている)場合、C コンパイルの暗黙のルールが適用されます。

 暗黙のルールのレシピは、必要上ふつう自動変数を使います。その結果、特別な手間をかけずとも、ディレクトリ探索で見つかったファイル名がそのまま使われます。


4.5.6 リンクライブラリのためのディレクトリ探索

 ディレクトリ探索は、リンカで使うライブラリに対して特別な形で適用されます。この特別な機能は、名前が「-lname」という形の前提条件を書いたときに働きます。(ここで何か変わったことが起きているのは、前提条件はふつうファイルの名前であるのに対し、ライブラリのファイル名は一般に「-lname」ではなく libname.a のような形をしている、という点から分かります。)

 前提条件の名前が「-lname」という形のとき、make はそれを特別に扱い、まず libname.so というファイルを探し、見つからなければ libname.a というファイルを、カレントディレクトリ、マッチする vpath の探索パスと VPATH の探索パスで指定されたディレクトリ、そして次に /lib/usr/libprefix/lib(通常は /usr/local/lib ですが、MS-DOS/MS-Windows 版の makeprefix が DJGPP インストールツリーのルートと定義されているかのように振る舞います)の各ディレクトリの順に探します。

 例えば、システムに /usr/lib/libcurses.a ライブラリがあって(かつ /usr/lib/libcurses.so ファイルがない場合)、

foo : foo.c -lcurses
        cc $^ -o $@

 とすると、foofoo.c または /usr/lib/libcurses.a より古いときに、「cc foo.c /usr/lib/libcurses.a -o foo」というコマンドが実行されます。

 探索対象になるファイルのデフォルトの集合は libname.solibname.a ですが、これは .LIBPATTERNS 変数でカスタマイズできます。この変数の値の中の各語はパターン文字列です。「-lname」のような前提条件が現れると、make は一覧の各パターン内のパーセント記号を name に置き換え、できあがった各ライブラリファイル名を使って上記のディレクトリ探索を行います。

 .LIBPATTERNS のデフォルト値は「lib%.so lib%.a」で、これが上で説明したデフォルトの振る舞いを与えます。

 この変数を空の値に設定すれば、リンクライブラリの展開を完全に無効にできます。


4.6 偽りのターゲット(Phony)

 偽りのターゲット(phony target)とは、実際にはファイルの名前ではないターゲットのことです。むしろそれは、あなたが明示的に要求したときに実行されるレシピに付けた、ただの名前です。偽りのターゲットを使う理由は2つあります。同名のファイルとの衝突を避けるためと、性能を向上させるためです。

 ターゲットファイルを作らないレシピを持つルールを書くと、そのターゲットが作り直しの検討対象になるたびにレシピが実行されます。次はその例です:

clean:
        rm *.o temp

 rm コマンドは clean という名前のファイルを作らないので、そんなファイルはおそらく決して存在しません。したがって、「make clean」と打つたびに rm コマンドが実行されます。

 この例では、もし clean という名前のファイルがこのディレクトリに作られてしまうと、clean ターゲットは正しく機能しなくなります。前提条件を持たないので、clean は常に最新と見なされ、そのレシピは実行されなくなってしまうのです。この問題を避けるには、そのターゲットを特別なターゲット .PHONY の前提条件にすることで、偽りのターゲットであると明示的に宣言できます(特別な組み込みターゲット名の項を参照)。次のようにします:

.PHONY: clean
clean:
        rm *.o temp

 こうしておけば、clean という名前のファイルが存在するかどうかに関係なく、「make clean」はレシピを実行します。

 .PHONY の前提条件は、(たとえ「%」文字を含んでいても)常に文字どおりのターゲット名として解釈され、パターンとしては扱われません。パターンルールを常に作り直したい場合は、「force ターゲット(強制ターゲット)」の利用を検討してください(レシピも前提条件もないルールの項を参照)。

 偽りのターゲットは、make の再帰的な呼び出し(make の再帰的な使い方の項を参照)と組み合わせても便利です。この状況では、makefile はビルドすべきサブディレクトリの数を並べた変数を含むことがよくあります。これを扱う安直な方法は、サブディレクトリをループで回すレシピを持つルールを1つ定義することです。次のようにします:

SUBDIRS = foo bar baz

subdirs:
        for dir in $(SUBDIRS); do \
          $(MAKE) -C $$dir; \
        done

 しかし、この方法にはいくつか問題があります。第1に、サブmake で検出されたエラーがこのルールでは無視されるので、1つが失敗しても残りのディレクトリのビルドを続けてしまいます。これは、エラーを記録して終了するシェルコマンドを追加すれば克服できますが、そうすると、make-k オプション付きで起動された場合でも終了してしまい、それは具合が悪いのです。第2に、そしておそらくもっと重要なことに、ルールが1つしかないため、ターゲットを並列にビルドする make の能力(並列実行の項を参照)を十分に活かせません。個々の makefile のターゲットは並列にビルドされますが、サブディレクトリは一度に1つずつしかビルドされません。

 サブディレクトリを .PHONY ターゲットとして宣言すれば(サブディレクトリは明らかに常に存在するので、これは必ずやらなければなりません。さもないとビルドされません)、これらの問題を取り除けます:

SUBDIRS = foo bar baz

.PHONY: subdirs $(SUBDIRS)

subdirs: $(SUBDIRS)

$(SUBDIRS):
        $(MAKE) -C $@

foo: baz

 ここではさらに、foo サブディレクトリは baz サブディレクトリが完了するまでビルドできない、とも宣言しています。この種の関係の宣言は、並列ビルドを試みるときに特に重要です。

 暗黙のルールの探索(暗黙のルールの使い方の項を参照)は、.PHONY ターゲットに対しては省略されます。これが、実在ファイルの有無を気にしていない場合でも、ターゲットを .PHONY と宣言すると性能の点で良い理由です。

 偽りのターゲットは、実在するターゲットファイルの前提条件にすべきではありません。もしそうしてしまうと、make がそのファイルを検討するたびに偽りのターゲットのレシピが実行されてしまいます。偽りのターゲットが実在ターゲットの前提条件になっていない限り、偽りのターゲットのレシピは、その偽りのターゲットが指定されたゴールであるときにのみ実行されます(ゴールを指定する引数の項を参照)。

 include される makefile を偽りのターゲットとして宣言してはいけません。偽りのターゲットは実在ファイルを表すことを意図していませんし、ターゲットが常に古いと見なされるため、make は常にそれを作り直したうえで自分自身を再実行してしまいます(makefile はどのように作り直されるかの項を参照)。これを避けるため、偽りとマークされた include ファイルが作り直されても、make は自分自身を再実行しません。

 偽りのターゲットは前提条件を持てます。1つのディレクトリに複数のプログラムが含まれている場合は、それらすべてのプログラムを1つの makefile ./Makefile に記述するのがもっとも便利です。デフォルトで作り直されるターゲットは makefile の最初のものになるので、これを「all」という名前の偽りのターゲットにして、その前提条件に個々のプログラムをすべて挙げるのが一般的です。例えば:

all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
        cc -o prog1 prog1.o utils.o

prog2 : prog2.o
        cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
        cc -o prog3 prog3.o sort.o utils.o

 こうすれば、単に「make」と打つだけで3つのプログラムをすべて作り直せますし、作り直したいものを引数として指定することもできます(「make prog1 prog3」のように)。偽りであることは継承されません。偽りのターゲットの前提条件は、明示的にそう宣言されない限り、それ自体が偽りになるわけではありません。

 ある偽りのターゲットが別の偽りのターゲットの前提条件になっている場合、それは後者のサブルーチンの役割を果たします。例えば、次の例では「make cleanall」がオブジェクトファイル、差分ファイル、そして program ファイルを削除します:

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
        rm program

cleanobj :
        rm *.o

cleandiff :
        rm *.diff

4.7 レシピも前提条件もないルール

 あるルールに前提条件もレシピもなく、しかもそのルールのターゲットが存在しないファイルである場合、make は、そのルールが実行されるたびに、このターゲットが更新されたものと見なします。これは、このターゲットに依存するすべてのターゲットが、常にそのレシピを実行することを意味します。

 例を見れば分かりやすいでしょう:

clean: FORCE
        rm $(objects)
FORCE:

 ここでターゲット「FORCE」は上記の特別な条件を満たすので、それに依存するターゲット clean は強制的にレシピを実行させられます。「FORCE」という名前そのものに特別な意味はありませんが、この用途でよく使われる名前の1つです。

 ご覧のとおり、このように「FORCE」を使うのは「.PHONY: clean」を使うのと同じ結果になります。

 「.PHONY」を使うほうがより明示的で効率的です。しかし、他のバージョンの make は「.PHONY」をサポートしていません。そのため「FORCE」は多くの makefile に登場します。偽りのターゲット(Phony)の項を参照してください。


4.8 イベントを記録するための空ターゲットファイル

 空ターゲット(empty target)は偽りのターゲットの一種で、ときおり明示的に要求する処理のためのレシピを保持するのに使います。偽りのターゲットと違い、このターゲットファイルは実際に存在しても構いません。ただしファイルの内容は問題ではなく、たいていは空です。

 空ターゲットファイルの目的は、ルールのレシピが最後に実行された時点を、その最終更新時刻によって記録することです。これが可能なのは、レシピの中のコマンドの1つが、ターゲットファイルを更新する touch コマンドだからです。

 空ターゲットファイルには何らかの前提条件があるべきです(さもないと意味がありません)。空ターゲットを作り直すよう要求すると、いずれかの前提条件がターゲットより新しい場合に——言い換えれば、最後にターゲットを作り直したとき以降にいずれかの前提条件が変更されている場合に——レシピが実行されます。次はその例です:

print: foo.c bar.c
        lpr -p $?
        touch print

 このルールがあれば、「make print」は、前回の「make print」以降にどちらかのソースファイルが変更されている場合に lpr コマンドを実行します。変更されたファイルだけを印刷するために、自動変数「$?」を使っています(自動変数の項を参照)。


4.9 特別な組み込みターゲット名

 特定の名前は、ターゲットとして現れると特別な意味を持ちます。

.PHONY

特別なターゲット .PHONY の前提条件は、偽りのターゲットと見なされます。そのようなターゲットが検討の対象になると、make は、その名前のファイルが存在するかどうかや、その最終更新時刻に関係なく、無条件にそのレシピを実行します。偽りのターゲット(Phony)の項を参照してください。

.SUFFIXES

特別なターゲット .SUFFIXES の前提条件は、サフィックスルールを調べる際に使う接尾辞の一覧です。旧式のサフィックスルールの項を参照してください。

.DEFAULT

.DEFAULT に指定されたレシピは、ルール(明示的なルールでも暗黙のルールでも)が見つからなかったすべてのターゲットに対して使われます。最後の手段としてのデフォルトルールの定義の項を参照してください。.DEFAULT のレシピが指定されている場合、ルールの中で前提条件としては挙げられているがターゲットとしては挙げられていないすべてのファイルに対して、そのレシピが代わりに実行されます。暗黙のルールの探索アルゴリズムの項を参照してください。

.PRECIOUS

.PRECIOUS の前提条件として挙げたターゲットには、次の特別な扱いが与えられます。すなわち、レシピの実行中に make が強制終了されたり中断されたりしても、そのターゲットは削除されません。make の中断と強制終了の項を参照してください。また、ターゲットが中間ファイルの場合、通常は不要になったあとに削除されますが、その削除も行われません。暗黙のルールの連鎖の項を参照してください。この後者の点では、.SECONDARY 特別ターゲットと働きが重なります。

暗黙のルールのターゲットパターン(「%.o」など)を .PRECIOUS 特別ターゲットの前提条件ファイルとして挙げることもできます。そうすると、そのパターンにマッチする名前のターゲットを持つルールが作る中間ファイルが保存されます。

.INTERMEDIATE

.INTERMEDIATE が依存するターゲットは、中間ファイルとして扱われます。暗黙のルールの連鎖の項を参照してください。前提条件のない .INTERMEDIATE は効果を持ちません。

.NOTINTERMEDIATE

特別なターゲット .NOTINTERMEDIATE の前提条件は、決して中間ファイルとは見なされません。暗黙のルールの連鎖の項を参照してください。前提条件のない .NOTINTERMEDIATE は、すべてのターゲットを中間ファイルではないものとして扱わせます。

前提条件がターゲットパターンの場合、そのパターンルールを使ってビルドされるターゲットは中間ファイルと見なされなくなります。

.SECONDARY

.SECONDARY が依存するターゲットは、中間ファイルとして扱われます。ただし、自動的に削除されることは決してありません。暗黙のルールの連鎖の項を参照してください。

.SECONDARY は、いくつかの珍しい状況で冗長な再ビルドを避けるのに使えます。例えば:

hello.bin: hello.o bye.o
        $(CC) -o $@ $^

%.o: %.c
        $(CC) -c -o $@ $<

.SECONDARY: hello.o bye.o

hello.bin はソースファイルとの関係では最新だが、しかしオブジェクトファイル hello.o が見当たらない、という状況を考えてみましょう。.SECONDARY がなければ、make はソースファイルが変わっていないにもかかわらず hello.o を作り直し、続いて hello.bin も作り直してしまいます。hello.o.SECONDARY と宣言しておけば、make はそれを作り直す必要がなくなり、hello.bin も作り直す必要がなくなります。もちろん、ソースファイルのいずれかが更新された場合は、hello.bin の生成が成功するように、すべてのオブジェクトファイルが作り直されます。

前提条件のない .SECONDARY は、すべてのターゲットをセカンダリとして扱わせます(すなわち、中間ファイルと見なされたという理由で削除されるターゲットがなくなります)。

.SECONDEXPANSION

.SECONDEXPANSION が makefile のどこかでターゲットとして言及されると、それが現れたあとに定義されたすべての前提条件の一覧が、すべての makefile を読み込み終えたあとにもう一度展開されます。二次展開の項を参照してください。

.DELETE_ON_ERROR

.DELETE_ON_ERROR が makefile のどこかでターゲットとして言及されると、make は、あるルールのターゲットが変更されていて、かつそのレシピが0以外の終了ステータスで終了した場合に、シグナルを受け取ったときと同じように、そのターゲットを削除します。レシピ内のエラーの項を参照してください。

.IGNORE

.IGNORE に前提条件を指定すると、make はそれら特定のファイルのレシピの実行時のエラーを無視します。.IGNORE のレシピ(もしあれば)は無視されます。

前提条件のないターゲットとして言及した場合、.IGNORE はすべてのファイルのレシピの実行時のエラーを無視するよう指示します。この「.IGNORE」の使い方は、歴史的な互換性のためにのみサポートされています。これは makefile 内のすべてのレシピに影響するので、あまり便利ではありません。特定のレシピのエラーを無視するための、もっと選択的な方法を使うことをお勧めします。レシピ内のエラーの項を参照してください。

.LOW_RESOLUTION_TIME

.LOW_RESOLUTION_TIME に前提条件を指定すると、make は、それらのファイルが低分解能のタイムスタンプを生成するコマンドによって作られたものと見なします。.LOW_RESOLUTION_TIME ターゲットのレシピは無視されます。

現代の多くのファイルシステムが持つ高分解能のファイルタイムスタンプは、make がファイルを最新だと誤って判断する可能性を減らします。しかし残念ながら、ホストによっては高分解能のファイルタイムスタンプを設定する手段を提供していないため、「cp -p」のようにファイルのタイムスタンプを明示的に設定するコマンドは、その秒未満の部分を捨てざるを得ません。そのようなコマンドで作られるファイルは、make がそのファイルを古いと誤って判断しないように、.LOW_RESOLUTION_TIME の前提条件として挙げておくべきです。例えば:

.LOW_RESOLUTION_TIME: dst
dst: src
        cp -p src dst

cp -p」は src のタイムスタンプの秒未満の部分を捨てるので、dst は最新であっても、たいてい src よりわずかに古くなります。.LOW_RESOLUTION_TIME の行があると、make は、dst のタイムスタンプが src のタイムスタンプと同じ秒の先頭にある場合に、dst を最新と見なすようになります。

アーカイブ形式の制約により、アーカイブメンバのタイムスタンプは常に低分解能です。アーカイブメンバを .LOW_RESOLUTION_TIME の前提条件として挙げる必要はありません。make がこれを自動的に行うからです。

.SILENT

.SILENT に前提条件を指定すると、make は、それら特定のファイルを作り直すために使うレシピを、実行前に表示しなくなります。.SILENT のレシピは無視されます。

前提条件のないターゲットとして言及した場合、.SILENT は、レシピを実行する前に一切表示しないよう指示します。特定のレシピのコマンド行だけを静かにする、もっと選択的な方法を使うこともできます。レシピのエコー表示の項を参照してください。make の特定の実行で全レシピを静かにしたい場合は、「-s」または「--silent」オプションを使ってください(オプション一覧の項を参照)。

.EXPORT_ALL_VARIABLES

ターゲットとして言及されるだけで、これは make に対し、デフォルトですべての変数を子プロセスにエクスポートするよう指示します。これは、引数なしの export を使う方法に代わるものです。サブmake への変数の伝達の項を参照してください。

.NOTPARALLEL

.NOTPARALLEL が前提条件のないターゲットとして言及されると、この make の起動におけるすべてのターゲットは、「-j」オプションが与えられていても直列に実行されます。再帰的に呼び出された make コマンドは、依然としてレシピを並列に実行します(その makefile にもこのターゲットが含まれている場合を除く)。

.NOTPARALLEL が前提条件としてターゲットを持つ場合、それらのターゲットのすべての前提条件が直列に実行されます。これは、挙げられたターゲットの各前提条件の間に .WAIT を暗黙的に追加します。並列実行の無効化の項を参照してください。

.ONESHELL

.ONESHELL がターゲットとして言及されると、ターゲットがビルドされる際に、レシピの各行が別々に呼び出されるのではなく、レシピのすべての行が1回のシェルの呼び出しに渡されます。レシピの実行の項を参照してください。

.POSIX

.POSIX がターゲットとして言及されると、makefile は POSIX 準拠モードで解析・実行されます。これは、POSIX 準拠の makefile しか受け付けなくなる、という意味ではありません。GNU make の高度な機能はすべて依然として利用できます。このターゲットは、make のデフォルトの振る舞いが POSIX と異なる部分について、make を POSIX が要求するとおりに振る舞わせるものです。

特に、このターゲットが言及されると、レシピはシェルに -e フラグが渡されたかのように呼び出されます。すなわち、レシピ内で最初に失敗したコマンドによって、レシピは直ちに失敗します。

 定義済みの暗黙のルールの接尾辞も、ターゲットとして現れると特別なターゲットと見なされます。「.c.o」のような2つの接尾辞を連結したものも同様です。これらのターゲットはサフィックスルールであり、暗黙のルールを定義する旧式の方法です(とはいえ今なお広く使われています)。原理的には、どんなターゲット名でも、それを2つに分けて両方の断片を接尾辞リストに追加すれば、このように特別な意味を持たせられます。実際には、接尾辞はふつう「.」で始まるので、これらの特別なターゲット名も「.」で始まります。旧式のサフィックスルールの項を参照してください。


4.10 1つのルールに複数のターゲット

 明示的なルールに複数のターゲットがある場合、それらは2通りの方法のいずれかで扱われます。すなわち、独立したターゲットとして扱われるか、グループ化されたターゲットとして扱われるか、です。どちらの扱いになるかは、ターゲットの一覧のあとに現れる区切り記号によって決まります。

独立したターゲットを持つルール

 標準のターゲット区切り記号「:」を使うルールは、独立したターゲットを定義します。これは、前提条件とレシピを重複させて、各ターゲットごとに同じルールを1回ずつ書くのと同等です。ふつうレシピでは、「$@」などの自動変数を使って、どのターゲットがビルドされているかを指定します。

 独立したターゲットを持つルールは、次の2つの場合に便利です:

 ところで、変数「$@」がレシピを変えられるのと同じように、ターゲットに応じて前提条件を変えたい、ということがあるかもしれません。これは通常のルールで複数のターゲットを使ってはできませんが、静的パターンルールを使えばできます。静的パターンルールの項を参照してください。

グループ化されたターゲットを持つルール

 独立したターゲットではなく、1回の呼び出しで複数のファイルを生成するレシピがある場合、その関係は、ルールがグループ化されたターゲット(grouped targets)を使うと宣言することで表現できます。グループ化されたターゲットのルールは、区切り記号「&:」を使います(ここで「&」は「すべて」を暗示するために使われています)。

 make がグループ化されたターゲットのどれか1つをビルドするとき、レシピの呼び出しの結果として、グループ内の他のすべてのターゲットも更新されることを make は理解します。さらに、グループ化されたターゲットの一部だけが古いか欠けている場合でも、make はレシピを実行すればすべてのターゲットが更新されると認識します。最後に、グループ化されたターゲットのいずれかが古ければ、グループ化されたすべてのターゲットが古いと見なされます。

 例として、次のルールはグループ化されたターゲットを定義します:

foo bar biz &: baz boz
        echo $^ > foo
        echo $^ > bar
        echo $^ > biz

 グループ化されたターゲットのレシピの実行中、自動変数「$@」は、ルールを発動させたグループ内の特定のターゲットの名前に設定されます。グループ化されたターゲットのルールのレシピでこの変数に頼る場合は注意が必要です。

 独立したターゲットと違い、グループ化されたターゲットのルールにはレシピを必ず含めなければなりません。ただし、グループ化されたターゲットのメンバであるターゲットは、レシピを持たない独立したターゲットのルール定義にも現れて構いません。

 各ターゲットに結びつけられるレシピは1つだけです。グループ化されたターゲットが、独立したターゲットのルールや、レシピを持つ別のグループ化されたターゲットのルールに現れると、警告が出て、後者のレシピが前者のレシピを置き換えます。さらに、そのターゲットは前のグループから取り除かれ、新しいグループにのみ現れるようになります。

 あるターゲットを複数のグループに登場させたい場合は、そのターゲットを含むすべてのグループを宣言する際に、ダブルコロンのグループ化されたターゲット区切り記号「&::」を使わなければなりません。グループ化されたダブルコロンのターゲットは、それぞれが独立して扱われ、各グループ化されたダブルコロンのルールのレシピは、その複数のターゲットの少なくとも1つが更新を必要とする場合に、最大1回だけ実行されます。


4.11 1つのターゲットに複数のルール

 1つのファイルを、複数のルールのターゲットにできます。すべてのルールで言及されたすべての前提条件は、そのターゲットの1つの前提条件の一覧にまとめられます。ターゲットが、どれかのルールのいずれかの前提条件より古ければ、レシピが実行されます。

 1つのファイルに対して実行されるレシピは、1つしか持てません。同じファイルに2つ以上のルールがレシピを与えた場合、make は最後に与えられたものを使い、エラーメッセージを表示します。(特別な場合として、ファイルの名前がドットで始まる場合は、エラーメッセージは表示されません。この奇妙な振る舞いは他の make の実装との互換性のためだけのものなので…使うのは避けてください。)ときには、同じターゲットに、makefile の別々の箇所で定義された複数のレシピを発動させたいことがあります。これにはダブルコロンルール(ダブルコロンルールの項を参照)を使えます。

 前提条件だけを持つ追加のルールを使えば、多数のファイルに一度にいくつかの追加の前提条件を与えられます。例えば、makefile には、ビルド対象のシステム内のすべてのコンパイラ出力ファイルの一覧を持つ objects のような変数があることがよくあります。それらすべてが、config.h が変わったら再コンパイルされなければならない、と手軽に言うには、次のように書きます:

objects = foo.o bar.o
foo.o : defs.h
bar.o : defs.h test.h
$(objects) : config.h

 これは、オブジェクトファイルの作り方を実際に指定しているルールを変えることなく、挿入したり取り除いたりできます。追加の前提条件を断続的に加えたい場合に便利な形式です。

 もう1つの工夫として、追加の前提条件を、make へのコマンドライン引数で設定する変数で指定することもできます(変数のオーバーライドの項を参照)。例えば、

extradeps=
$(objects) : $(extradeps)

 とすると、「make extradeps=foo.h」というコマンドは foo.h を各オブジェクトファイルの前提条件と見なしますが、単なる「make」はそうしません。

 あるターゲットの明示的なルールのどれにもレシピがない場合、make は適用できる暗黙のルールを探して1つ見つけようとします(暗黙のルールの使い方の項を参照)。


4.12 静的パターンルール

 静的パターンルール(static pattern rules)は、複数のターゲットを指定し、各ターゲットの前提条件名をターゲット名にもとづいて構成するルールです。これは複数のターゲットを持つ通常のルールよりも汎用的です。というのも、ターゲットどうしが同一の前提条件を持つ必要がないからです。それらの前提条件は類似している必要はありますが、必ずしも同一である必要はありません。


4.12.1 静的パターンルールの構文

 静的パターンルールの構文は次のとおりです:

targets …: target-pattern: prereq-patternsrecipe

 targets の一覧は、このルールが適用されるターゲットを指定します。ターゲットには、通常のルールのターゲットと同様にワイルドカード文字を含められます(ファイル名にワイルドカードを使うの項を参照)。

 target-patternprereq-patterns は、各ターゲットの前提条件をどう計算するかを述べます。各ターゲットは target-pattern と照合され、ターゲット名の一部——語幹(stem)と呼ばれます——が抽出されます。この語幹が各 prereq-pattern に代入されて、前提条件名が作られます(各 prereq-pattern から1つずつ)。

 各パターンは通常、「%」文字をちょうど1つ含みます。target-pattern がターゲットにマッチするとき、「%」はターゲット名のどの部分にもマッチでき、この部分が語幹と呼ばれます。パターンの残りの部分は完全に一致しなければなりません。例えば、ターゲット foo.o はパターン「%.o」にマッチし、「foo」が語幹になります。ターゲット foo.cfoo.out はこのパターンにマッチしません。

 各ターゲットの前提条件名は、各前提条件パターンの「%」を語幹で置き換えて作られます。例えば、ある前提条件パターンが %.c なら、語幹「foo」を代入すると前提条件名 foo.c になります。「%」を含まない前提条件パターンを書くことも認められています。その場合、この前提条件はすべてのターゲットで同じになります。

 パターンルールの「%」文字は、前にバックスラッシュ(「\」)を付けてクォートできます。「%」文字をクォートしてしまうバックスラッシュ自体は、さらにバックスラッシュを重ねることでクォートできます。「%」文字や別のバックスラッシュをクォートするバックスラッシュは、パターンがファイル名と比較されたり語幹が代入されたりする前に取り除かれます。「%」文字をクォートするおそれのないバックスラッシュは、そのまま残されます。例えば、パターン the\%weird\\%pattern\\ では、効力を持つ「%」文字の前に「the%weird\」があり、そのあとに「pattern\\」が続きます。末尾の2つのバックスラッシュは、どの「%」文字にも影響しないため、そのまま残されます。

 次は、foo.obar.o のそれぞれを、対応する .c ファイルからコンパイルする例です:

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

 ここで「$<」は前提条件の名前を保持する自動変数、「$@」はターゲットの名前を保持する自動変数です。自動変数の項を参照してください。

 指定された各ターゲットは、ターゲットパターンにマッチしなければなりません。マッチしないターゲットごとに警告が出されます。一部しかパターンにマッチしないファイルの一覧がある場合は、filter 関数を使ってマッチしないファイル名を取り除けます(文字列の置換と解析のための関数の項を参照):

files = foo.elc bar.o lose.o

$(filter %.o,$(files)): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
        emacs -f batch-byte-compile $<

 この例では、「$(filter %.o,$(files))」の結果は bar.o lose.o で、最初の静的パターンルールによって、これらのオブジェクトファイルがそれぞれ対応する C ソースファイルのコンパイルで更新されます。「$(filter %.elc,$(files))」の結果は foo.elc なので、そのファイルは foo.el から作られます。

 もう1つの例は、静的パターンルールで $* をどう使うかを示します:

bigoutput littleoutput : %output : text.g
        generate text.g -$* > $@

 generate コマンドが実行されるとき、$* は語幹——「big」または「little」——に展開されます。


4.12.2 静的パターンルールと暗黙のルール

 静的パターンルールは、パターンルールとして定義された暗黙のルール(パターンルールの定義と再定義の項を参照)と多くの共通点を持ちます。どちらも、ターゲットのパターンと、前提条件の名前を構成するパターンを持ちます。違いは、ルールがいつ適用されるかを make がどう決めるかにあります。

 暗黙のルールは、そのパターンにマッチするどのターゲットにも適用できますが、適用されるのは、そのターゲットに他でレシピが指定されておらず、かつ前提条件が見つけられる場合に限られます。複数の暗黙のルールが適用可能に見える場合は、そのうち1つだけが適用されます。どれが選ばれるかはルールの順序に依存します。

 対照的に、静的パターンルールは、そのルールで指定した正確なターゲットの一覧に適用されます。それ以外のターゲットには適用できず、指定された各ターゲットには必ず適用されます。両方ともレシピを持つ、衝突する2つのルールが適用される場合は、それはエラーです。

 静的パターンルールが暗黙のルールより優れていると言えるのは、次の理由からです:


4.13 ダブルコロンルール

 ダブルコロン(double-colon)ルールは、ターゲット名のあとに「:」ではなく「::」を付けて書く明示的なルールです。同じターゲットが複数のルールに現れるとき、通常のルールとは違う扱いを受けます。ダブルコロンを持つパターンルールは、まったく別の意味を持ちます(何にでもマッチするパターンルールの項を参照)。

 あるターゲットが複数のルールに現れる場合、それらのルールはすべて同じ種類——すべて通常のルールか、すべてダブルコロンか——でなければなりません。ダブルコロンの場合、それぞれが互いに独立しています。各ダブルコロンルールのレシピは、ターゲットがそのルールのいずれかの前提条件より古い場合に実行されます。そのルールに前提条件がなければ、そのレシピは常に実行されます(ターゲットがすでに存在していても)。その結果として、ダブルコロンルールのうち、1つも実行されないこともあれば、一部だけ、あるいは全部が実行されることもあります。

 同じターゲットを持つダブルコロンルールは、実は互いに完全に分離しています。各ダブルコロンルールは、ちょうど異なるターゲットを持つルールが処理されるのと同じように、個別に処理されます。

 あるターゲットのダブルコロンルールは、makefile に現れる順序で実行されます。とはいえ、ダブルコロンルールが本当に意味を持つのは、レシピを実行する順序が問題にならない場合です。

 ダブルコロンルールはやや分かりにくく、あまり役立つことはありません。これは、ターゲットを更新する方法が、どの前提条件ファイルが更新の原因になったかによって異なる、という場合のための仕組みを提供しますが、そうした場合はまれです。

 各ダブルコロンルールはレシピを指定すべきです。指定しない場合、適用できる暗黙のルールがあればそれが使われます。暗黙のルールの使い方の項を参照してください。


4.14 前提条件を自動的に生成する

 プログラムの makefile では、書く必要のあるルールの多くが、単に「あるオブジェクトファイルがあるヘッダファイルに依存している」と述べるだけ、ということがよくあります。例えば、main.c#include 経由で defs.h を使うなら、次のように書くでしょう:

main.o: defs.h

 このルールは、defs.h が変わるたびに main.o を作り直さなければならない、と make に知らせるために必要です。大きなプログラムなら、こうしたルールを makefile に何十も書かなければならなくなるのが分かるでしょう。しかも、#include を追加・削除するたびに、忘れずに makefile を更新するよう、常に細心の注意を払わなければなりません。

 この面倒を避けるため、現代の C コンパイラのほとんどは、ソースファイル内の #include 行を見て、これらのルールをあなたの代わりに書いてくれます。通常はコンパイラへの「-M」オプションでこれを行います。例えば、次のコマンド:

cc -M main.c

 は、次の出力を生成します:

main.o : main.c defs.h

 こうして、もうそれらのルールをすべて自分で書く必要はなくなります。コンパイラが代わりにやってくれるのです。

 なお、このようなルールは main.o を makefile で言及することになるので、暗黙のルールの探索によって中間ファイルと見なされることは決してありません。これは、make がそのファイルを使ったあとに削除してしまうことが決してない、という意味です。暗黙のルールの連鎖の項を参照してください。

 古い make プログラムでは、このコンパイラの機能を使って、「make depend」のようなコマンドで前提条件をその都度生成するのが伝統的なやり方でした。そのコマンドは、自動生成されたすべての前提条件を含む depend というファイルを作り、makefile はそれを include で読み込めばよかったのです(他の makefile を取り込むの項を参照)。

 GNU make では、makefile を作り直す機能があるため、このやり方は時代遅れになりました——make は古くなった makefile を常に再生成するので、前提条件を再生成せよと make に明示的に伝える必要はもうありません。makefile はどのように作り直されるかの項を参照してください。

 自動的な前提条件生成について私たちがお勧めするやり方は、各ソースファイルに対応する makefile を1つずつ持つことです。各ソースファイル name.c に対し、オブジェクトファイル name.o がどのファイルに依存するかを列挙した makefile name.d を用意します。こうすれば、変更されたソースファイルだけを再スキャンして、新しい前提条件を作り直せます。

 次は、C ソースファイル name.c から、name.d という名前の前提条件ファイル(すなわち makefile)を生成するパターンルールです:

%.d: %.c
        @set -e; rm -f $@; \
         $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
         sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
         rm -f $@.$$$$

 パターンルールの定義については、パターンルールの定義と再定義の項を参照してください。シェルへの「-e」フラグは、$(CC) コマンド(あるいは他のどのコマンドでも)が失敗(0以外のステータスで終了)した場合に、シェルを直ちに終了させます。

 GNU C コンパイラでは、「-M」の代わりに「-MM」フラグを使いたいかもしれません。これはシステムヘッダファイルに関する前提条件を省きます。詳しくは Using GNU CCプリプロセッサを制御するオプションを参照してください。

 sed コマンドの目的は、(例えば)次を:

main.o : main.c defs.h

 次のように変換することです:

main.o main.d : main.c defs.h

 これにより、各「.d」ファイルが、対応する「.o」ファイルが依存するすべてのソースファイルとヘッダファイルに依存するようになります。すると make は、どのソースファイルやヘッダファイルが変わっても前提条件を再生成しなければならないと分かります。

 「.d」ファイルを作り直すルールを定義したら、次は include ディレクティブを使ってそれらをすべて読み込みます。他の makefile を取り込むの項を参照してください。例えば:

sources = foo.c bar.c

include $(sources:.c=.d)

 (この例は置換参照を使って、ソースファイルの一覧「foo.c bar.c」を前提条件 makefile の一覧「foo.d bar.d」に変換しています。置換参照の詳しい情報については、置換参照の項を参照してください。)「.d」ファイルも他と同じく makefile なので、make はあなたが何もしなくても必要に応じてそれらを作り直します。makefile はどのように作り直されるかの項を参照してください。

 なお、「.d」ファイルにはターゲット定義が含まれているので、include ディレクティブは、必ず makefile の最初のデフォルトゴールよりもあとに置いてください。さもないと、どこかのオブジェクトファイルがデフォルトゴールになってしまう危険があります。make が makefile を処理するしくみの項を参照してください。


[訳注] 本文中の脚注(2)について。原典の脚注には「古いバージョンの GNU make の中には、ワイルドカード展開の結果をソートしないものもあった」と記されています。現在の GNU make では、各ワイルドカード式ごとに結果がソートされます。


前へ | 次へ | 目次 | 英語原版(gnu.org)