GNU makeとinclude

 ずっとmakefileでわからなかったことがあったのだが、今日まじめに調べてようやく分かった。

 makefileでソースの依存関係をいちいち手書きするのが面倒なので、

SRC=$(shell ls *.cc)

dep:
	g++ -MM -MG $(SRC) >makefile.depend

というルールを作っておき、makefileの最後で

-include makefile.depend

としておく。こうすると

$ make dep 

とやればmakefile.dependができる。さて、makefile.dependが古かったり存在しなかったりした場合に自動的に作りたくて、

SRC=$(shell ls *.cc)

makefile.depend:
	g++ -MM -MG $(SRC) >makefile.depend

-include makefile.depend

 とかやっていると、全然関係のないルール、たとえばcleanをやろうとしてもmakefile.dependを作ろうとしてしまう。これがずっと謎だった(無論 cleanは PHONYにしてある)。

 で、もしかしたらincludeしているのが原因かと思い、まずはこんなmakefileを作ってみた。

FOO=foo

foo:
        echo $(FOO)

-include hoge

そして、以下の内容のhogeを作っておく。

FOO=bar

この状態でmake fooすると、

$ make foo
echo bar
bar 

となる。つまり、先にinclude処理が行われている。さて、ここでhogeを消し、makefileを以下のように書き換える。

FOO=foo


foo:
        echo $(FOO)

hoge:
        echo "FOO=bar" > hoge

-include hoge

makeをしてみる。

$ls
makefile

$ make
echo "FOO=bar" > hoge
echo bar
bar

$ ls
hoge makefile

$ make
echo bar
bar 

これでようやく合点がいった。まとめると以下のような感じ。

  1. GNU makeはinclude処理を一番最初にやる。
  2. その際、includeするためのファイルは暗黙的に全てのルールに必要だと仮定される。
  3. したがって、includeしたいファイルが存在せず、かつそのファイルの作り方を知っていたら、必ず作りに行く。
  4. ただし、ファイルが存在せず、かつ作り方を知らなくてもエラーは出さない。

というわけで、makefile.dependをインクルードしている場合、そのファイルが無くて、かつ作り方を知っていれば、たとえcleanのような関係ないルールであってもmakefile.dependを作りに行く。なぜならmakefile.dependがcleanの定義を書き換えるかもしれないから。 make cleanやmake tarでmakefile.dependが作られるのは気持ち悪いが、

makefile.depend: $(SRC) $(HED)
	g++ -MM -MG $(SRC) >makefile.depend

としておけば、とりあえずソースに変更があれば必ずmakefile.dependが作り直されるようになると。