makefile の書き方
初版 2004.2.27
最終更新 2004.2.27
unix でプログラムを開発するときに不可欠なコマンドが make という
コマンドです。make コマンドは makefile というファイルの中に
書かれた規則に従って動作します。ここでは makefile の書き方に
ついて説明します。
Fortran や C でプログラムを開発する場合、通常は
プログラムはいくつかのファイルに分かれています。
実行ファイルは「コンパイル」→「リンク」という手順を経て
作成されます。以下のような場合について考えます。
コンパイル リンク
a.c ------(1)------> a.o ------+
b.c ------(2)------> b.o ------+-(4)----> abc
c.F ------(3)------> c.o ------+
(1)(2)(3)(4) では
次のようなコマンドを実行する必要があります。
(1) cc -c a.c
(2) cc -c b.c
(3) f77 -c c.F
(4) f77 -o abc a.o b.o c.o
なお、この例では C と fortran の混合プログラムなので、
リンクのコマンドとして f77 を使用しています。
a.c を更新した場合を考えます。この場合は、
(1) と (4) の処理を行えば良いです。(2) と (3) は行う
必要がありません。ですから、次のようなルールが
あれば良いことになります。
- a.c と a.o の日付を比べて、a.c の方が新しいなら cc -c a.c を実行する
- b.c と b.o の日付を比べて、b.c の方が新しいなら cc -c b.c を実行する
- c.F と c.o の日付を比べて、c.F の方が新しいなら f77 -c c.F を実行する
- abc と a.o, b.o, c.o の日付を比べて、a.o, b.o, c.o のうち1つでも
abc より新しいなら f77 -o abc a.o b.o c.o を実行する
このようなルールを記述したファイルが makefile であり、makefile で
記述したルールに従って、必要があれば「コンパイル」「リンク」を
行うのが make コマンドです。makefile の内容は次のようになります。
abc : a.o b.o c.o
<--tab-->f77 -o abc a.o b.o c.o
a.o : a.c
<--tab-->cc -c a.c
b.o : b.c
<--tab-->cc -c b.c
c.o : c.F
<--tab-->f77 -c c.F
ここで、<--tab--> と記述してある場所は tab を1つ入れる
という意味です。スペースを入れるとエラーになりますので注意
して下さい。makefile におけるルールは以下のように
記述します。
ファイル名 : そのファイルを作る元となるファイルの名前
<--tab-->そのファイルを作る方法
make コマンドには暗黙のルールというのがデフォルトで含まれて
います。実は、「xxx.c をコンパイルして xxx.o を作成するときは
cc -c xxx.c を実行する」
「yyy.F をコンパイルして yyy.o を作成するときは f77 -c yyy.F を
実行する」というルールはデフォルトで定義されているので、
上記の makefile 中で定義されている4つのルールのうち、
a.o : a.c 以下は全て不要です。
次に「xxx.c をコンパイルして xxx.o を作成するときは cc -c -g xxx.c を
実行する」というルールを作る方法を説明します。次のように makefile を
書きます。
.SUFFIXES: <--- この行があると全ての暗黙のルールを無効にする
.SUFFIXES: .o .c .F <--- ルールを記述する拡張子
.c.o :
<--tab-->cc -c -g $<
.F.o :
<--tab-->f77 -c -g $*.F
abc : a.o b.o c.o
<--tab-->f77 -o abc a.o b.o c.o
「f77 -c -g $*.F」は「f77 -c -g $<」と書いても同じです。
また、この場合は一番最初の行は不要です。暗黙のルールを
無効にする必要がある場合として、次のような場合があります。
プリプロセス コンパイル
a.x ----- (1) ------> a.F -----(2)-----> a.o
xypp a.x > a.F f77 -c a.F
当研究室ではグラフ描画のサブルーチンをコールするソースプログラムは
拡張子 .x を使用し、プリプロセスしてからコンパイルします。
そこで、次のようなルールを「順番に適用すること」が必要です。
- .x という拡張子のファイルがあるなら .x と .o の日付を調べ、(1)(2) の
手順を実行する。
- .F という拡張子のファイルがあるなら .F と .o の日付を調べ、(2) の
手順を実行する。
これを、実現するには次のようにルールを書く必要があります。
必要です。
.SUFFIXES: <--- 暗黙のルールを無効にする
.SUFFIXES: .o .c .x .F <--- .x の順番は .F より前に
.c.o :
<--tab-->cc -c -g $<
.x.o :
<--tab-->xypp $*.x > $*.F
<--tab-->f77 -c -g $*.F
.F.o :
<--tab-->f77 -c -g $*.F
makefile においては暗黙のルールが優先するようです。
1行目を省略すると、「暗黙の .F ---> .o」のルールが
makefile 中で定義した「.x ---> .o」のルールより先に
適用されてしまうため、c.x を更新しても c.F の日付は c.o と
同じであるため、再プリプロセス、再コンパイルが行われません。
コンパイル リンク
a.c ---------------> a.o ------+
b.c ---------------> b.o ------+--------> abc
c.F ---------------> c.o ------+
d.c ---------------> d.o ------+
e.c ---------------> e.o ------+--------> def
f.F ---------------> f.o ------+
上のような場合は次のように makefile を作ります。
all : abc def
abc : a.o b.o c.o
<--tab-->f77 -o abc a.o b.o c.o
def : d.o e.o f.o
<--tab-->f77 -o def d.o e.o f.o
変数を導入して分かりやすく記述することも出来ます。
EXE1 = abc
EXE2 = def
OBJ1 = a.o b.o c.o
OBJ2 = d.o e.o f.o
all : $(EXE1) $(EXE2)
$(EXE1) : $(OBJ1)
<--tab-->f77 -o $(EXE1) $(OBJ1)
$(EXE2) : $(OBJ2)
<--tab-->f77 -o $(EXE2) $(OBJ2)