make はもちろん、プログラム開発でも利用される。 大規模なプログラム開発では、 一つのプログラムが数百のソースファイルに分割されていることも珍しくない。 このようなケースでは、コンパイルに数時間かかることもある。 一つのファイルをちょっと修正しただけで毎回 数時間もコンパイルするのでは あまりにも非効率である。 また、makeのマクロ定義などをうまく利用すれば、 コンパイルオプションの集中管理もできる。 ここでは、Cのプログラム開発に makeを使用した例を示す。 (なお、統合開発環境では、 同じことを「プロジェクトの管理」という名前で行なっている。)
ソースファイルが一つだけの場合には、 Makefile は次のような感じになる。
runrun : runrun.c →gcc -Wall -o runrun runrun.c -lm
コンパイラ名、コンパイラへのオブション、リンカへのオプションなどを マクロとして記述すると、以下のようになる。 Makefileを書く手間は増えるが、第3者に対する情報が増える。
ここで、$@
がターゲット、
$^
がソースに展開されるマクロ名である。
CFLAGS = -Wall CC = gcc $(CFLAGS) LFLAGS = -lm runrun : runrun.c →$(CC) -o $@ $(LFLAGS) $^ clean: →rm -rf runrun *.o
PRG = blackjack SRC = main.c sub1.c sub2.c sub3.c CFLAGS = -Wall LFLAGS = -lm -lX11 CC = gcc $(CFLAGS) $(PRG) : $(SRC) →$(CC) -o $@ $^ $(LFLAGS) clean : →rm -f $(PRG)
このMakefileでは、 main.c, sub1.c, sub2.c, sub3.c のどれかひとつでも変更されていたら、 実行ファイル blackjack が作成しなおされる。 しかし、 コンパイラがどういう処理を行なっているのかを考えると、 sub1.cだけを変更したときに、sub2.c, sub3.cを再コンパイルするのは無駄である。 (コンピュータが高速になっているので、この余分な処理にかかる時間は ほとんど気にならないことが多いが、それでもやはり 開発規模が大きくなると、この無駄が意外と無視できない。)
複数ファイルのときには、 それぞれのソースファイルからオブジェクトファイルを生成し、 最後にリンクすることで、実行ファイルが作成されている。 したがって、もしオブジェクトファイルが残っているならば、 変更がないオブジェクトファイルの生成を省略できる。 この点を踏まえて、記述されたMakefileは次のようになる。
PRG = blackjack OBJ = main.o sub1.o sub2.o sub3.o CFLAGS = -g -Wall LFLAGS = -lm -lX11 CC = gcc $(CFLAGS) $(PRG) : $(OBJ) →$(CC) -o $@ $^ $(LFLAGS) main.o : main.c →$(CC) -c $^ sub1.o : sub1.c →$(CC) -c $^ sub2.o : sub2.c →$(CC) -c $^ sub3.o : sub3.c →$(CC) -c $^ clean : →rm -f $(PRG) $(OBJ)
$(CC) -c $^と同じ形になっていることがわかる。
このように、 ターゲットファイル名とソースファイル名の サフィックス(いわゆる拡張子だと思えば良い)のみが異なり、 かつ、サフィックスの関係だけで行なうべきコマンドが決まる場合には、 次のようにまとめて記述することができる。 このようなルールの書き方を「サフィックスルール」と呼ぶ。
PRG = blackjack OBJ = main.o sub1.o sub2.o sub3.o CFLAGS = -g -Wall LFLAGS = -lm -lX11 CC = gcc $(CFLAGS) $(PRG) : $(OBJ) →$(CC) -o $@ $^ $(LFLAGS) .c.o: # ←サフィックスルールによる記述 →$(CC) -c $^ # clean: →rm -rf $(PRG) $(OBJ)
このサフィックスルールでは、 (明示的に依存関係が書かれていないので) ターゲットファイル名が「なんとか.o」のとき、 ソースファイル名は「なんとか.c」となる。 (依存関係が別に書かれている時には、 そのソースファイル名が用いられる。)
サフィックスルールの場合には、
コマンド中に $@
のような
自動変数をよく使うことになる。これ以外にも次のようなものがある。
変数 | 説明 |
---|---|
$@ | ターゲットファイル名 |
$^ | すべての依存するファイル名 |
$< | 最初の依存するファイル名 |
$? | ターゲットより新しいすべての依存するファイル名 |
$* | サフィックスを除いたターゲット名 |
仮に main.c が sub1.c 内の関数を呼びだし、
sub1.c内の関数が、sub2.c および sub3.c 内の関数を呼び出しているとすると、
各ソースファイルのヘッダには、例えば次のように#include
文が
書かれているはずである。
#include <stdio.h> #include "sub1.h"
#include <stdio.h> #include <string.h> #include "sub2.h" #include "sub3.h"
#include "sub2.h"
#include "sub3.h"
このような状況では、
もし sub1.h に変更が加えられた場合、main.c をコンパイルしなおす
(つまりmain.o
を作り直す)必要がある。
しかし、さきほどまでのMakefileにはその依存関係は含まれていない。
ヘッダファイルに関する依存関係を含めた
Makefileは次のようになる。
サフィックスルール(.c.o)のところのマクロが、
$^
から $<
に変わっていることに注意せよ。
PRG = blackjack OBJ = main.o sub1.o sub2.o sub3.o CFLAGS = -g -Wall LFLAGS = -lm -lX11 CC = gcc $(CFLAGS) $(PRG) : $(OBJ) →$(CC) -o $@ $^ $(LFLAGS) .c.o: # サフィックスルール →$(CC) -c $< # コンパイルには依存ファイルの最初のファイルだけを使う。 clean: →rm -rf $(PRG) $(OBJ) # 実際の依存関係はここから main.o : main.c sub1.h sub1.o : sub1.c sub1.h sub2.h sub3.h sub2.o : sub2.c sub2.h sub3.o : sub3.c sub3.hこれが一応の完成形である。
ところで、ファイルが多くなってくると、
Makefileの依存関係の部分を人が間違わずに書くことは難しくなってくる。
依存関係は、ソースファイル中の#inlcude
の情報で
判断することができる。
gccには、この依存関係を生成してくれる機能がある。
gcc -MM main.c sub1.c sub2.c sub3.cとすると、次のようなものが標準出力に出力される。
main.o : main.c sub1.h sub1.o : sub1.c sub1.h sub2.h sub3.h sub2.o : sub2.c sub2.h sub3.o : sub3.c sub3.h
この出力をMakefileに取り込むには次のようにする。
PRG = blackjack OBJ = main.o sub1.o sub2.o sub3.o CFLAGS = -g -Wall LFLAGS = -lm -lX11 CC = gcc $(CFLAGS) $(PRG) : $(OBJ) →$(CC) -o $@ $^ $(LFLAGS) .c.o: →$(CC) -c $< clean: →rm -rf $(PRG) $(OBJ)
gcc -MM main.c sub1.c sub2.c sub3.c >> Makefile