##はじめに
C や C++ で書かれたプログラムのビルドの際、Make でヘッダファイルの依存関係に対応するには、gcc の -MMD オプションを組み合わせればよいことが知られている。
通常はこの記事と同じように -MMD オプションを使ってコンパイルと依存関係ファイル .d の生成を同時に行うことが多いと思われるが、諸事情があり、コンパイルと依存関係ファイルの生成を別々に行いたくなった。その際のハマりどころについて調査した内容を Qiita に投稿した。
ところが、今までの試行錯誤はほとんど骨折り損のくたびれ儲けであることがわかった。
##結論
結局、以下のような Makefile に落ち着いた。
PROG := myapp
SRCS := main.cc foo.cc
OBJS := ${SRCS:%.cc=%.o}
DEPS := ${SRCS:%.cc=%.d}
CXX := g++
all: ${PROG}
${PROG}: ${OBJS}
${CXX} -o $@ ${OBJS}
%.o: %.cc
${CXX} $< -MM -MP -MF $*.d
${CXX} -c $< -o $@
clean:
${RM} ${PROG} ${OBJS} ${DEPS}
ifeq ($(findstring clean,${MAKECMDGOALS}),)
-include ${DEPS}
endif
.PHONY: all clean
以下のように、依存関係ファイルの生成とコンパイルを単に2行に分けて書いている。
%.o: %.cc
${CXX} $< -MM -MP -MF $*.d
${CXX} -c $< -o $@
パターンルールでは、$*
はターゲットの語幹部分が格納されている自動変数で、今回の場合は %.o
の %
の部分に相当する。余談だが、複雑なパターンマッチを設定していると $*
に格納される文字列がやや奇怪なことになるため、注意が必要である。例えば GNU Make のマニュアルには以下のような例が挙げられている。
- $*
- The stem with which an implicit rule matches (see How Patterns Match). If the target is dir/a.foo.b and the target pattern is a.%.b then the stem is dir/foo. The stem is useful for constructing names of related files.
##解説
前に書いた記事で述べた通り、最初は以下のように依存関係ファイル .d の生成ルールとオブジェクトファイル .o の生成ルールを独立させて書いていた。
-include ${DEPS}
%.d: %.cc
${CXX} $< -MM -MP -MF $@
%.o: %.cc %.d
${CXX} -c $< -o $@
というのも、「別のファイルを生成するルールは別の依存関係があるはずだ。それなら別々のルールとして書くべきだ。それが Makefile というものだ。」と思い込んでいたのであった。
しかし、よくよく観察してみると、「オブジェクトファイルは再生成しなければならないが、依存関係ファイルは再生成しなくてもよい」などという状況は普通は起こらないことに気づいた。オブジェクトファイルを再生成しなければならない状況ではソースコードが更新されているはずであり、それならやはり依存関係ファイルも再生成しなければならない。
その上、依存関係ファイルの生成ルールが独立して存在していることで、依存関係ファイルのインクルードの際に「Dry-run でも生成されてしまう」とか「わざわざ生成してから clean している」とか、無用な混乱を生んでいたのである。
ということは、手の込んだことをせずに、依存関係ファイルの生成とコンパイルを単に2行に分けて書くだけのほうがよかったということになる。
##参考文献
- Makeでヘッダファイルの依存関係に対応する (wagavulin's blog, 2012/04/05)
- Makeでヘッダファイルの依存関係を生成するルールを独立させる (Qiita, 2018/03/22)
- Makeでヘッダファイルの依存関係を生成するルールを独立させる・続 (Qiita, 2018/03/24)
- 10.5.3 Automatic Variables (GNU Make Manual, 2016/05/22)