依存関係の自動解決(第二回)
前回の記事で書いたようにMakefile依存関係の自動解決について書いていきます。
必要最小限なソースファイルだけ再コンパイルすればいい
sample0.cpp、sample1.cpp、sample2.cpp、sample3.cppの4つのソースファイルがあるとします。そしてこれらの依存関係が
- sample0: sample1 sample2
- sample1: sample2
であるとします。このときsample1.cppを書き換えたとき、sample2.cppのみ再コンパイルが必要であってsample0およびsample1は再コンパイルの必要はありません。しかしこれらの依存関係を全てMakefile中に記述・反映するのは結構大変です。
コンパイラオプション「-MMD」により依存関係を記述したファイルを作成
詳細はMakeでヘッダファイルの依存関係に対応する - wagavulinの日記に書かれています。こちらにある通り最終的に
$ g++ -O2 -MMD -c sample0.cpp -o sample0.o
とオプションに「-MMD」をつければいい。このオプションは#includeしているヘッダーファイルの依存関係も解決してくれる。コンパイルが無事に終了すると同じ階層にsample0.dという依存関係が記述されたファイルが作成される。
参照ブログと内容が重複するので、簡単に説明するとこのオプションには「-M」「-MM」「-MD」「-MMD」があり、Mはシステムディレクリ(stdio.hなど)にあるヘッダーファイルの依存関係もチェックし(出力結果2参照)、MMはシステムディレクトリを除外してチェックする(出力結果1参照)。またDを使うと依存関係を記述したファイルを作成ししてくれる。システムディレクトリを含む依存ファイルを作る必要はなく、依存関係を記述したファイルの出力もして欲しいことから「-MMD」を使えばいいということになる。
ソースファイル:C++でhallo worldを表示
main.cpp
#include <iostream> #include "header.h" int main(void){ std::cout<<"hallo world\n"; return 0; }
出力結果1:-MMDオプション
コンパイルコマンド
$ g++ -O2 -MMD -c main.cpp -o main.o
main.dの中身
main.o: main.cpp header.h
出力結果2:-MDオプション
コンパイルコマンド
$ g++ -O2 -MD -c main.cpp -o main.o
main.dの中身
main.o: main.cpp /usr/include/c++/4.6/iostream \ /usr/include/c++/4.6/i686-linux-gnu/./bits/c++config.h \ /usr/include/c++/4.6/i686-linux-gnu/./bits/os_defines.h \ (省略) /usr/include/c++/4.6/bits/istream.tcc header.h
作成した依存関係ファイルを使ってmakeする
次回のmake時にはこの依存関係ファイルを利用する。そのためには
-include main.d
として依存関係ファイルを取り込む。これはmakefile中のどこかに書いておけばよい。
MMDコマンドを導入したmakefile
GCC = g++ CFLAGS = -O2 -MMD -Wall -Wextra SRCS = main.cpp sample.cpp TARGET = main OBJS = $(SRCS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) .cpp.o: $(GCC) $(CFLAGS) -c $< -o $@ $(TARGET): $(OBJS) $(GCC) $(CFLAGS) -o $@ $+ default: $(OBJS) $(TARGET) clean: $(RM) $(OBJS) $(TARGET) -include $(DEPS)
まずコンパイルオプションを表す変数CFLAGSに「-MMD」を追加。その後、依存関係がかかれたファイル*.dをインクルードするために末行に「-include $(DEPS)」を追加。変数DEPSの中身はmain.d sample.dを表すように上部に「DEPS=$(SRCS:.cpp=.d)」を定義する。次回は多段Makefileについて書きます。