前述*1のように Makefile は非常に簡潔に記述できます.ただし,簡潔なMakefileを記述するためには make の仕組みを理解する必要があります.
結論から言うと, make は 簡潔な生成ルールのみを記述したMakefileから, 実際に必要となる詳細な生成ルールを自動的に生成します.
自動的に生成されたルールを確認するには
$ make -p
と "-p" オプションを付けて make を実行します.
以下,
の二つの具体例を挙げて,説明します.
前準備
Makefile と hoge.c を用意します.
all: hoge
int main()
{
return 0;
}
make -p の実行
$ make -p > log
大量の情報が 標準出力に出力されるので,一旦 log というファイルに出力を保存します.
make -p の結果を眺める(その1)
ここからが,本題です.
まず,make は makefile に記述された”生成ルール”にしたがってビルドを進めます.生成ルールは以下のように記述されます.
ターゲット: ソース
コマンド
これで,"ソース"から "コマンド"をつかって"ターゲット"を生成する,という意味になります.
また,ここからが重要なのですが,make にはデフォルトでいくつかのルールが登録されています.たとえば
hoge: hoge.c
とコマンドを省略したMakefileが与えられても,make は hoge.c から hoge を作るためのコマンドを自動的に補ったルールを生成します.
make コマンドを実行する際に”-p” オプションは,この自動的に生成したルールをみるためのオプションです.
それでは,"-p"オプションの結果を保存した log ファイルの中を眺めてみましょう.hoge をキーワードに検索を書けてみると見易いです.
hoge を検索すると以下のような部分が二つ見つかると思います.一つ目は all: hoge と,Makefile で与えたルールそのものです.
all: hoge
# Implicit rule search has been done.
# File does not exist.
# File has been updated.
# Successfully updated.
二つ目は,make が自動生成したルールです.
hoge: hoge.c
# Implicit rule search has been done.
# Implicit/static pattern stem: `hoge'
# Last modified 2007-02-18 14:27:19
# File has been updated.
# Successfully updated.
# commands to execute (built-in):
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
これが
ターゲット: ソース
コマンド
の形式で,コマンドの部分が
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
と自動的に補われたかたちになっています
ここで,$^と$@はmakeが使う特別な変数で,この場合はそれぞれ $^が hoge.c $@ が hoge に展開されます.
一方, $(LINK.c) は LINK.c をキーワードに log ファイルを検索すると
LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
と定義されています.CC は,検索すると
CC = gcc
と定義されているはずです.また,他の変数$(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)は未定義なので,
LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
は
LINK.c = gcc
と展開されることになります.
同様の手順で make -p を眺めていくと最終的に
hoge: hoge.c
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
は,
hoge: hoge.c
gcc hoge.c -o hoge
という生成ルールに展開できることが判ると思います.
このような make が自動的に補ってくれるコマンドと,その内部で使われる変数達を把握できるようになると, Makefile は非常に簡潔に記述できるようになります.
make -p の結果を眺める(その2)
同じ手順で,次にhoge.c とfuga.c から hoge を生成するmakefileについてmake -p の結果を眺めてみます.
Makefile は以下のものを使います
all: hoge
hoge: hoge.o fuga.o
make -p の結果から関連するところを抜き出すと
all: hoge
hoge: hoge.o fuga.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
hoge.o: hoge.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
fuga.o: fuga.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
となります.LINK.o COMPILE.c はそれぞれ
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
LINK.o = $(CC) $(LDFLAGS) $(TARGET_ARCH)
と定義されています.最終的にどのようなコマンドが実行されるのかは,冗長になるのでここでは割愛します.
まとめ
make -p の結果を読むことで,make がMakefileを元に内部でルールを自動生成していることを説明しました.
また,内部で生成しているルールを読むことで,
- コンパイラは CC で設定できる
- コンパイルオプションは CFLAGS か CPPFLAGS で指定できる
- リンクオプションは LDFLAGS で指定できる
ことが理解できたかと思います.
このようなmakeが自動的に補うルール,コマンドを意識すると makefile は非常に簡潔に書くことができます.是非一度 make に オプション"-p"を付けて実行してみることをおすすめします.
また以下の本を読むこともおすすめします.
おすすめ度の平均:
どのファイルを
コンパイルするかを指定する道具
おすすめ度の平均:
プログラマーのお友達Make
makeファイルを独学で学んできた人にもお勧め