Makefileでのヘッダファイルの依存関係の追加
問題設定
まず以下の4つのファイルを作る
/* main.c */ #include "foo.h" int main(){ foo(10); return 0; } /* foo.h */ void foo(int a); /* foo.c */ #include "foo.h" #include <stdio.h> void foo(int a){ printf("%d\n", a); }
#Makefile PROG = myapp SRCS = main.c foo.c OBJS = $(SRCS:%.c=%.o) CC = gcc all: $(PROG) $(PROG): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) -c $<
これでmakeする
$ make gcc -c main.c gcc -c foo.c gcc -o myapp main.o foo.o $ ./myapp 10
このように問題なく実行できる.ここでfoo.hに変更を加えた場合を想定する.
$ touch foo.h
touchコマンドは空ファイルを作るだけでなく,ファイルの中身を変更の有無にかかわらずファイルの更新時間を現在時刻に変えられる.
この状態でもう一度makeすると,main.cとfoo.cはfoo.hをインクルードしているので,それらのファイルに対し,もう一度コンパイルをしてほしい.しかし,もうmakeし直すと,
$ make make: Nothing to be done for 'all'.
と出力され,何も起こらない.この原因はMakefile内にヘッダファイルの依存関係が書かれていないことにある.
解決案
この問題を解決ための,愚直な方法はMakefileの最終行にでも
main.o: main.c foo.h foo.o: foo.c foo.h
と書き込む方法だがファイル数が多くなると面倒.なので別の方法を考える.
MMDオプション
MMDオプションをつけると,各ファイルのコンパイル時に,#include文でダブルコーテーションで囲まれたヘッダファイルを.dファイルに出力してくれる.
(例)先ほどのMakefileを編集する.
#Makefile PROG = myapp SRCS = main.c foo.c OBJS = $(SRCS:%.c=%.o) CC = gcc all: $(PROG) $(PROG): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) -c -MMD $<
これでmakeしてみる.(その前に,先ほど作られたオブジェクトファイルなどを消去する.)
$ rm *.o myapp $ ls foo.c foo.h main.c Makefile $ make gcc -c -MMD main.c gcc -c -MMD foo.c gcc -o myapp main.o foo.o $ ls foo.c foo.d foo.h foo.o main.c main.d main.o Makefile myapp
.dのファイルが作られてるのが分かる.中身を見てみると
$ cat main.d main.o: main.c foo.h $ cat foo.d foo.o: foo.c foo.h
のように,依存ファイルが得られる.
includeオプション
つぎは,得られた依存ファイルの情報をMakefileに追加する.それにはincludeオプションを使う.
#Makefile PROG = myapp SRCS = main.c foo.c OBJS = $(SRCS:%.c=%.o) DEPS := $(SRCS:%.c=%.d) CC = gcc all: $(PROG) $(PROG): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) -c -MMD $< -include $(DEPS)
.dファイルがない場合(1回目でのmake)では,.dファイルがないというエラーが出てmakeが止まってしまうのでincludeの先頭にハイフンをつける(ハイフンをつけるとファイルがなくてもエラーが出なくなる)
補足1
includeオプションは,今のmakefileの処理を止めて,指定した別のmakefileの処理を行うというものらしい.上の例では,
-include $(DEPS)
の部分は
main.o: main.c foo.h foo.o: foo.c foo.h
と解釈される.なので,.dファイルもmakefileなのだろう(makefileの文法で書かれてる).また一番下にincludeを書いても真ん中の処理をする前にincludeの処理をするみたい.
補足2
includeオプションは,allよりも上に書いてはいけない.makeというコマンドは一番上のターゲットに対する処理を行うため,ターゲットが変ってしまう.今までの例で
-include $(DEPS) all: $(PROG)
としてしまうと,
main.o: main.c foo.h foo.o: foo.c foo.h all: $(PROG)
となり,main.oを作ることが目的となってしまう.