makeに何をするかを伝える方法としてmakefileと呼ばれるファイルが必要です。ほとんどの場合、makefileはプログラムのコンパイルやリンクの仕方をmakeに伝えます。
この章では8つのCソースファイルと3つのヘッダファイルから成るエディタプログラムのコンパイルやリンクの方法について記述されたシンプルなmakefileについて説明します。makefileは他にも明白に要求された種々多様なコマンドの実行の仕方をmakeに教えます(例えば、掃除作業であるファイルを削除する、など)。より複雑なmakefileの例を知りたい場合、複雑なMakefileの例の項をご覧ください。
makeがエディタプログラムを再コンパイルする場合、更新されたCソースファイルのそれぞれを再コンパイルしなければいけません。ヘッダーファイルが変更されたなら、そのヘッダをインクルードしているファイルはすべて満遍なく問題なく再コンパイルされなければなりません。それぞれのコンパイル作業は各ソースの内容に相当するオブジェクトファイルを一つづつ生成します。そしていくつかのソースがコンパイルされれば、最終的に全てのオブジェクトファイル(新たに作られたものも以前のコンパイル作業で作られ保存されていたものも)をエディタプログラムを新しく生成するために一緒にリンクします。
単純なmakefileは以下のような"ルール"から成り立っています:
エディタプログラム ターゲット ... : 依存関係のファイル ... コマンド ... ...
ターゲットは通常、プログラムによって生成されたファイルの名前です。ターゲットの例として、実行可能なオブジェクトファイルがあります。"clean"のように実行するアクションの名前でも構いません(偽りのターゲット(Phony)の項を参照)。
依存関係(dependency)とは作成するターゲットを入力するのに利用されるファイルのことです。一つのターゲットはよくいくつかのファイルに依存しています。
コマンドとははmakeが実行するアクションのことです。ルールは複数のコマンドを持っていてもよく、それぞれが各自特有の行に並びます。[重要] コマンド行の始まりは必ずタブでなければいけません!! このことは気づきにくいため思いもよらぬ失敗になることがあります。
通常、コマンドは依存関係とルールに影響を受け、依存関係にあるファイルが少しでも変更されていればターゲットファイル作成します。しかし、指令を下してくれるルールは依存関係になる必要のないターゲットにも命令を下してしまいます。たとえば、依存関係のない"clean"ターゲットに関連付けされた削除コマンドもルールは含んでいます。
そして、ルールは特別なルールからのターゲットである、特定のファイルをいつどうやってリメイクするかを決めます。makeはターゲットを作成したり更新したりする目的で依存関係にあるファイルに関するコマンドを実行します。また、ルールはいつどのようにアクションを実行するかも決める事ができます。ルールの記述の項をご覧下さい。
makefileはルールの以外にも別のテキストも含むこともできますが、シンプルなmakefileではルールを含むだけで十分です。ルールはテンプレートで見られるよりもう少し複雑にできるようですが、多かれ少なかれこの型に申し分なく当てはまります。
editと呼ばれる実行可能ファイルが8つのオブジェクトファイルに依存し、それから8つのCソースと3つのヘッダに依存するという事を記述する方法として分かりやすいmakefileがこれです。
この例では全てのCファイルが"defs.h"をインクルードしていますが、Cファイルのうち編集コマンドを定義するだけが"command.h"をインクルードしていて、それからエディタバッファを変更する低級ファイルだけが"buffer.h"をインクルードします。
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
長いラインはバックスラッシュ(\)と改行を続けて書いて二つのラインに分割します。そうやって一つの長い行を扱うことで読みやすくなります。
"edit"という実行可能ファイルを作るのにmakefileを使うためには、こうタイプして下さい。
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中ではコマンド行を他の行と区別するために全てのコマンド行に必ずタブを最初につけてください。(make
はコマンドがどう動くかを何も知らないという事を心に留めておいて下さい)。適切にターゲットファイルを更新してくれるコマンドを作ると便利です。make
がしてくれる事は、ターゲットファイルを更新する必要があるときに指定したルールに従いコマンドをただ実行する事だけです。)
`clean'というターゲットはファイルではなく単なるアクション名です。通常、ルールでアクションを実行したくないなら、"clean"は他のいかなるルールにおいても依存関係ではありません。結果として具体的に指示する限りはmakeは絶対にアクションに関して何もしません。
注意:ルールは依存関係にならないだけではなく、いかなる依存関係も従えないので、ルールの目的は指定されたコマンドを実行するということだけになります。
ファイルを参照しない、つまり正真正銘のアクションであるターゲットをフォニーターゲットといいます。このようなターゲットについての情報を知るには偽りのターゲット(Phony)の項を参照して下さい。makeにrm
や他のコマンドからのエラーを無視させる方法についてはコマンド内エラーを参照して下さい。
make
のMakefile処理法デフォルトではmakeは一番最初のターゲットから開始します("."で始まるターゲットではない)。このターゲットのことをデフォルトゴールといいます。 (ゴールとはmakeが最終的に更新しようとしているターゲット群のことです。ゴールを指定する引数の項を参照して下さい。)
前項の単純な例では、デフォルトゴールが"edit"という実行可能プログラムを更新する事だったのでこのルールを最初に記述したのです。
だから、以下のようにコマンドを与えると…
make
makeはカレントディレクトリのmakefileを読み込み、一番初めのルールで処理を開始します。例では"edit"を再リンクするというルールでしたが、makeが完全にこのルールを処理しきる前に"edit"が依存するファイルのためのルールを処理しなければいけません。この場合、依存するファイルとはオブジェクトファイルのことです。 各ファイルそれぞれがそれ自身のルールに従い処理されます。これらのルールはそれぞれの".o"ファイルを、もととなるソースファイルをコンパイルして更新するように指令します。 ソースファイルや依存関係で名前を呼ばれたヘッダーが、オブジェクトファイルよりも新しい場合や存在しない場合は、再コンパイルしなければなりません。
ゴールの依存関係としてターゲットが出現すると他のルールが処理されることになります。もし、ゴールが依存していないルールがある場合は(あるいはゴールに依存しているルールがある場合など)、そのルールは処理されません。ただ、(make cleanのようなコマンドを使って)makeにそのルールを処理するように命令すればそうしますが。
一つのオブジェクトを再コンパイルするまえに、makeはその依存関係、つまりソースファイルとヘッダーファイルを更新する事を考えます。このmakefileは、いかなるルールのターゲットにもならない".c"や".h"ファイルに関する事は一切指定しないので、makeはその".c"や".h"については何もしません。ですが、makeはBisonやYaccで自動生成されたようなCプログラムは更新してしまうでしょう。この時ばかりはそれぞれ独自のルールに依ります。
オブジェクトファイルに再コンパイルの必要があったか否かに関わらず、オブジェクトファイルの再コンパイルの後は、makeが"edit"を再リンクするかどうかを判断します。"edit"というファイルが存在しない、またはプログラムよりオブジェクトファイルが新しければ、必ず再リンクされなければいけません。 たった一つのオブジェクトファイルが再コンパイルされた場合でも、今やそれは"edit"よりも新しいものであり、"edit"は再リンクされることになります。
このため、"insert.c"を変更して"make"を実行すると、makeはそれをコンパイルして"insert.o"を更新し、それから"edit"をリンクします。 "command.h"を変更してmakeを実行すると、makeは"kbd.o"、"command.o"、"files.o"というオブジェクトファイルを再コンパイルし、それから"edit"をリンクします。
今回使われた例では、"edit"のためのルールで二度全てのオブジェクトファイルを列挙しなければなりませんでした。
(繰り返される部分) 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を簡単にするのに変数を使うことができます。変数は一度定義した文字列を後の複数の場所で代用できます(変数の使い方の項を参照)。
一般的な慣習としてobjects
、 OBJECTS
、 objs
、 OBJS
、 obj
、OBJ
のどれかの変数を全てのオブジェクトファイルのリストとしてmakefile毎に与えます。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はコマンドを理解してるからです。つまり".o"ファイルを更新する時は、その名前と一致する名前の".c"ファイルを"cc -c"コマンドを使って更新するものだ、という暗黙のルールが働くからです。例えば、"main.c"から"main.o"にコンパイルするのには "cc -c main.c -o main.o"というコマンドを使います。それゆえオブジェクトファイルのルールではコマンドの省略ができます。暗黙のルールの利用の項をご覧下さい。
".c"ファイルがこの方法で自動的に使われると、依存関係のリストにも自動的に加えられます。それゆえコマンドを省略すると".c"ファイルを依存関係でも省略することができます。
ここにあるのはこれらの変更についての単なる例でしかなく、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のオブジェクトが暗黙のルールだけから作られる場合、makefileを代わりのやり方で利用できます。makefileをこのやり方をするとターゲットを用いる代わりに依存関係を用いてオブジェクトファイルを分類することができます。これがそういうやり方の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")するために全てのオブジェクトファイルや実行可能ファイルの削除の仕方を設定できます。
例のエディターの例を用いて掃除(clean)するmakeルールの書き方をここで示します。
clean: rm edit $(objects)
実際には、予期しない状況を処理するのにもう少し複雑な方法を書きたくなるでしょう。そういうときはこうします。
.PHONY : clean clean : -rm edit $(objects)
こうするとmakeが"clean"という実際のファイルと混同するのを防ぎ、rmのエラーがあってもmakeを続けます。(偽りのターゲット(Phony)の項とコマンド内エラーの項を参照。)
このようなルールは、デフォルトではして欲しくはないようなことなので、makefileの始まりに置くべきではありません! それゆえ、makefileの例では、エディターを再コンパイルしてくれるeditにデフォルトゴールを残しておいて欲しかったのでこのルールを必要としませんでした。
cleanはeditの依存関係ではないため、"make"コマンドに引数がなければ、このルールはまったく機能しません。cleanルールを実行させるには、"make clean"と打たなければなりません。makeの実行方法の項をご覧下さい。