make に何をさせたいかを伝えるには、makefile と呼ばれるファイルが必要です。多くの場合 makefile は、あるプログラムをどうコンパイルし、どうリンクするかを make に伝える役割を果たします。
この章では、8 個の C ソースファイルと 3 個のヘッダファイルからなるテキストエディタを、どうコンパイルしリンクするかを記述したシンプルな makefile を題材に説明します。makefile には、明示的に指示されたときだけ実行されるその他のコマンド(たとえば後片付けとして特定のファイルを削除する、といった作業)を記述しておくこともできます。もっと複雑な makefile の例を見たい方は、複雑なMakefileの例をご覧ください。
make がこのエディタを再コンパイルするとき、変更された C ソースファイルはそれぞれ再コンパイルされなければなりません。ヘッダファイルが変更された場合は、念のため、そのヘッダファイルを #include しているすべての C ソースファイルを再コンパイルする必要があります。コンパイルのたびに、対応するソースファイルからオブジェクトファイルが生成されます。そして最後に、どれか一つでもソースファイルが再コンパイルされたなら、新しく作られたものも以前のコンパイルから残っているものも含めて、すべてのオブジェクトファイルをリンクし、新しい実行可能なエディタを作り出さなければなりません。
シンプルな makefile は、次のような形をした「ルール」の集まりです:
target … : prerequisites …
recipe
…
…
ターゲット(target)とは、ふつうはプログラムによって生成されるファイルの名前です。たとえば実行可能ファイルやオブジェクトファイルがターゲットの例です。ターゲットには、実行すべきアクションの名前を指定することもできます。たとえば clean がそうです(偽りのターゲット(Phony)を参照してください)。
前提条件(prerequisite、旧称:依存関係)とは、ターゲットを作るための入力として使われるファイルのことです。一つのターゲットが複数のファイルに依存していることもよくあります。
レシピ(recipe、ルールの実行内容)とは、make が実行するアクションのことです。レシピには複数のコマンドを含めることができ、同じ行に並べても、それぞれ別の行に書いても構いません。[重要] レシピの各行は、必ず行頭にタブ文字を置いてください! これは分かりにくく、うっかり者を引っかける落とし穴です。タブ以外の文字でレシピを始めたい場合は、.RECIPEPREFIX 変数に別の文字を設定できます(その他の特別な変数を参照してください)。
ふつうレシピは、前提条件を伴うルールの中にあり、前提条件のどれかが変更されたときにターゲットファイルを作り直す役目を果たします。ただし、ターゲットのレシピを指定するルールに、必ずしも前提条件がなくてもかまいません。たとえば、ターゲット clean に対応する削除コマンドを含むルールには、前提条件がありません。
つまりルールとは、そのルールのターゲットである特定のファイルを、どのように、そしてどんなときに作り直すかを説明するものです。make は前提条件に対してレシピを実行し、ターゲットを作成または更新します。ルールは、どのように、そしてどんなときにあるアクションを実行するかを説明することもできます。ルールの書き方を参照してください。
makefile にはルール以外のテキストを含めることもできますが、シンプルな makefile であればルールだけで十分です。実際のルールは、このひな形よりいくらか複雑に見えることもありますが、いずれもおおむねこのパターンに収まります。
ここに、edit という実行可能ファイルが 8 個のオブジェクトファイルに依存し、それらのオブジェクトファイルがさらに 8 個の C ソースファイルと 3 個のヘッダファイルに依存している、という関係を素直に記述した makefile を示します。
この例では、すべての C ファイルが defs.h を #include していますが、command.h を #include しているのは編集コマンドを定義しているファイルだけ、buffer.h を #include しているのはエディタのバッファを変更する低レベルなファイルだけです。
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
ここでは長い行をバックスラッシュと改行で 2 行に分けています。これは 1 本の長い行を書くのと同じことですが、こうしたほうが読みやすくなります。長い行を分割するを参照してください。
この makefile を使って edit という実行可能ファイルを作るには、次のように打ちます:
make
この makefile を使って、実行可能ファイルとすべてのオブジェクトファイルをディレクトリから削除するには、次のように打ちます:
make clean
この例の makefile では、ターゲットには実行可能ファイル edit や、オブジェクトファイル main.o、kbd.o などが含まれます。前提条件は main.c や defs.h といったファイルです。実際のところ、各 .o ファイルはターゲットであると同時に前提条件でもあります。レシピには cc -c main.c や cc -c kbd.c が含まれます。
ターゲットがファイルである場合、その前提条件のどれかが変更されたら、ターゲットは再コンパイルあるいは再リンクされる必要があります。さらに、前提条件のうち、それ自体が自動的に生成されるものは、先に更新しておく必要があります。この例では、edit は 8 個のオブジェクトファイルそれぞれに依存し、オブジェクトファイル main.o はソースファイル main.c とヘッダファイル defs.h に依存しています。
ターゲットと前提条件を書いた各行のあとには、レシピを続けることができます。これらのレシピは、ターゲットファイルをどう更新するかを指示するものです。レシピを makefile 中の他の行と区別するため、レシピの各行の行頭にはタブ文字(あるいは .RECIPEPREFIX 変数で指定した文字。その他の特別な変数を参照してください)を置かなければなりません。(なお、make はレシピがどう動くかについては何も知らない、という点に注意してください。ターゲットファイルを正しく更新するレシピを用意するのは、あなたの責任です。make がするのは、ターゲットファイルの更新が必要になったときに、あなたの指定したレシピを実行することだけです。)
ターゲット clean はファイルではなく、単なるアクションの名前です。このルールのアクションはふだん実行したいものではないので、clean は他のどのルールの前提条件にもなっていません。そのため、特に指示しない限り make がこのルールを処理することはありません。このルールは他のルールの前提条件でないだけでなく、それ自身も前提条件を一つも持っていない点に注意してください。つまりこのルールの唯一の目的は、指定されたレシピを実行することにあります。ファイルを指すのではなく単なるアクションであるようなターゲットを、偽りのターゲット(Phony)と呼びます。この種のターゲットについては偽りのターゲット(Phony)を、rm やその他のコマンドが出すエラーを make に無視させる方法についてはレシピ中のエラーを参照してください。
デフォルトでは、make は最初のターゲットから処理を始めます(ただし名前が . で始まるターゲットは対象外です。ひとつ以上の / も含んでいる場合は別ですが)。これをデフォルトゴールと呼びます。(ゴールとは、make が最終的に更新しようと努めるターゲットのことです。)この挙動は、コマンドライン(ゴールを指定する引数を参照してください)や .DEFAULT_GOAL 特別変数(その他の特別な変数を参照してください)を使って変更できます。
前節のシンプルな例では、デフォルトゴールは実行可能プログラム edit を更新することです。だからこそ、私たちはそのルールを先頭に置いたのです。
そういうわけで、次のコマンドを与えると:
make
make はカレントディレクトリの makefile を読み込み、最初のルールから処理を始めます。この例では、最初のルールは edit を再リンクするためのものです。しかし make がこのルールを完全に処理する前に、edit が依存しているファイル——この場合はオブジェクトファイル——のルールを先に処理しなければなりません。これらのファイルはそれぞれ、自分自身のルールに従って処理されます。それらのルールは、各 .o ファイルを、そのソースファイルをコンパイルすることで更新せよ、と指示します。再コンパイルは、ソースファイルか、前提条件として挙げられたヘッダファイルのいずれかがオブジェクトファイルより新しい場合、あるいはオブジェクトファイルが存在しない場合に行われます。
その他のルールが処理されるのは、それらのターゲットがゴールの前提条件として現れるからです。もしあるルールが、ゴール(やゴールが依存するもの、さらにそれが依存するもの……)から依存されていなければ、そのルールは処理されません。ただし、make clean のようなコマンドで明示的に指示した場合は別です。
オブジェクトファイルを再コンパイルする前に、make はその前提条件であるソースファイルやヘッダファイルの更新も検討します。この makefile はそれらについて何もすべきことを指定していません——.c ファイルや .h ファイルはどのルールのターゲットでもありません——ので、make はこれらのファイルには何もしません。しかし、Bison や Yacc が生成するような、自動生成される C プログラムであれば、make はこの段階でそれら自身のルールに従って更新します。
必要なオブジェクトファイルを再コンパイルし終えると、make は edit を再リンクするかどうかを判断します。再リンクは、ファイル edit が存在しない場合、あるいはどれかのオブジェクトファイルが edit より新しい場合に行われます。あるオブジェクトファイルが今しがた再コンパイルされたなら、それは edit より新しくなっているので、edit は再リンクされます。
したがって、ファイル insert.c を変更して make を実行すると、make はそのファイルをコンパイルして insert.o を更新し、それから edit をリンクします。ファイル command.h を変更して make を実行すると、make はオブジェクトファイル kbd.o、command.o、files.o を再コンパイルし、それからファイル edit をリンクします。
先ほどの例では、edit のルールの中でオブジェクトファイルをすべて 2 回も列挙しなければなりませんでした(ここに再掲します):
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
このような重複は間違いのもとです。新しいオブジェクトファイルをシステムに追加するとき、片方のリストには加えても、もう片方には加え忘れてしまうかもしれません。変数を使えば、この危険をなくし、makefile をすっきりさせることができます。変数を使うと、あるテキスト文字列を一度だけ定義しておき、あとから複数の箇所でそれを差し込むことができます(変数の使い方を参照してください)。
どの makefile でも、すべてのオブジェクトファイル名を並べた objects、OBJECTS、objs、OBJS、obj、OBJ のいずれかの名前の変数を持たせるのが標準的なやり方です。そのような変数 objects は、makefile に次のような行を書いて定義します:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
そうすれば、オブジェクトファイル名のリストを置きたい箇所ごとに、$(objects) と書くことで、その変数の値を差し込むことができます(変数の使い方を参照してください)。
オブジェクトファイルに変数を使うと、先ほどのシンプルな makefile 全体は次のようになります:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)
個々の C ソースファイルをコンパイルするレシピは、いちいち書き出す必要はありません。make がそれを自分で割り出せるからです。make には、対応する名前の .c ファイルから .o ファイルを cc -c コマンドで更新する暗黙のルールが備わっています。たとえば make は、main.c を main.o にコンパイルするのに cc -c main.c -o main.o というレシピを使います。したがって、オブジェクトファイルのルールからレシピを省くことができます。暗黙のルールを使うを参照してください。
このように .c ファイルが自動的に使われるとき、その .c ファイルは前提条件のリストにも自動的に加えられます。したがって、レシピを省くのであれば、前提条件から .c ファイルを省くこともできます。
以上の 2 つの変更をどちらも施し、さらに先に提案した変数 objects を使うと、例の全体は次のようになります:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
rm edit $(objects)
実際の現場では、私たちはこのように makefile を書きます。(clean にまつわるややこしい事情については別の箇所で説明します。偽りのターゲット(Phony)とレシピ中のエラーを参照してください。)
暗黙のルールはとても便利なので、重要な機能です。これからも頻繁に使われているのを目にすることでしょう。
makefile のオブジェクトが暗黙のルールだけで作られる場合、もう一つ別の書き方ができます。この書き方では、エントリをターゲットごとではなく、前提条件ごとにまとめます。実際に書くと、次のようになります:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
ここでは defs.h がすべてのオブジェクトファイルの前提条件として与えられ、command.h と buffer.h は、それぞれに対して挙げられた特定のオブジェクトファイルの前提条件になっています。
こちらのほうが良いかどうかは好みの問題です。より簡潔ではありますが、各ターゲットに関する情報を一箇所にまとめてあるほうが分かりやすいと感じて、この書き方を好まない人もいます。
プログラムをコンパイルすることだけが、ルールを書きたくなる場面ではありません。makefile では、プログラムをコンパイルする以外にも、いくつかの作業のやり方を書いておくのが一般的です。たとえば、すべてのオブジェクトファイルと実行可能ファイルを削除して、ディレクトリを clean(きれいな状態)にする方法などです。
例のエディタを掃除する make のルールは、次のように書けます:
clean:
rm edit $(objects)
実際には、予期しない状況にも対応できるよう、もう少し凝った書き方をしたくなるでしょう。そのときは、次のようにします:
.PHONY : clean
clean :
-rm edit $(objects)
こう書くと、clean という名前の実際のファイルがあったときに make が混乱するのを防げますし、rm がエラーを出しても処理を続けるようになります。(偽りのターゲット(Phony)とレシピ中のエラーを参照してください。)
このようなルールは、makefile の先頭に置くべきではありません。デフォルトで実行されては困るからです! ですから例の makefile では、エディタを再コンパイルする edit のルールが、デフォルトゴールであり続けるようにしておきたいのです。
clean は edit の前提条件ではないので、引数なしで make コマンドを実行しても、このルールはまったく実行されません。このルールを実行するには、make clean と打つ必要があります。makeの実行方法を参照してください。