2013年02月08日
Makefileの依存関係の記述を自動化する簡単な方法
ビルドにmakeコマンドを使うときにはMakefileにファイルの依存関係を書いておく必要があります。
しかし、開発の途中でインクルードするヘッダファイルが増えてくると、これらの依存関係を手作業で記述するのは面倒です。(そして、これをサボったことでハマることもよくあることです。:)
ここでは、gccの機能を使ってこれを自動化する方法を紹介します。
gccのプリプロセッサで依存関係を自動生成する
gccでコンパイルするときに -MD というオプションをつけると、コンパイルとともに拡張子が.dのファイルが作成されます。これはMakefileと同じ書式でそのファイルの依存関係が記述されています。
詳しくはgccのマニュアルを参照してください。
http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Preprocessor-Options.html
生成した依存関係のファイルをMakefileにとりこむ
Makefileの中でCのコンパイルオプションに -MDを追加するには、Makefileの最初の方で以下を追加します。
CFLAGS += -MD
生成された.dファイルをとりこむにはMakefileの最後の方に以下を追加します。
-include *.d
行頭の'-'は*.dのファイルが無くてincludeが失敗した場合でもそれを無視するための指定です。
また、自動生成された *.dのファイルを削除するために、cleanのところに以下を追加するとよいでしょう。
rm -f *.d
サンプル
簡単な例で説明します。
$ cat hello.c #include <stdio.h> #include "message.h" main() { printf("%s, world\n", MESSAGE); } $ cat message.h #define MESSAGE "Hello"
このように、hello.cはmessage.hをインクルードしています。
とりあえず以下のようなMakefileでこれをmakeすることができます。
CFLAGS = -g OBJS = hello.o hello: $(OBJS) clean: rm -f hello $(OBJS)
しかしこれには hello.oがmessage.h に依存していることが記述されていないため、message.hを修正してもhello.oは再コンパイルされません。
以下の赤字の部分を追加します。
CFLAGS = -g CFLAGS += -MD OBJS = hello.o hello: $(OBJS) clean: rm -f hello $(OBJS) rm -f *.d -include *.d
これでmakeすると
$ make cc -g -MD -c -o hello.o hello.c cc hello.o -o hello
自動生成されたhello.dの内容は以下の通りです。
$ cat hello.d hello.o: hello.c /usr/include/stdio.h /usr/include/features.h \ /usr/include/x86_64-linux-gnu/bits/predefs.h \ /usr/include/x86_64-linux-gnu/sys/cdefs.h \ /usr/include/x86_64-linux-gnu/bits/wordsize.h \ /usr/include/x86_64-linux-gnu/gnu/stubs.h \ /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \ /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \ /usr/include/x86_64-linux-gnu/bits/types.h \ /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \ /usr/include/_G_config.h /usr/include/wchar.h \ /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \ /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \ /usr/include/x86_64-linux-gnu/bits/sys_errlist.h message.h
次回のmakeではこのhello.dがMakefileにインクルードされるので、message.hを修正したらhello.oが再コンパイルされます。
おまけ
以下のワンライナーでコンパイルに必要な全てのソースファイルを一覧できます。
$ cat *.d | sed -e 's/^.*://' -e 's/\\$//' | tr ' ' '\012' | sort | uniq
(sedで行頭の:までを削除、行末のバックスラッシュを削除。trで空白を改行に変換。sortして重複を削除。)