これは何
GNU make から ninja に移行を検討中のメモです。
想定読者
makefile
を自分でかける人
.PHONY: clean
を毎回書くのに飽き飽きした人
素の make の動作が遅いなぁと思う人
ninja 向けのメタビルドツールを作りたい人
ninja って何?
https://mattn.kaoriya.net/software/ninja/20140121141906.htm こことか読んでください。
libfoo.a のビルドを比較
a.c
, b.c
, c.c
の3ファイルから構成される libfoo.a
をビルドする例を makefile
と build.ninja
で示します。
実行ファイルのビルドについては 前回を見てください。
今回は、ソースファイルの置き場と、ビルドの作業領域が別ディレクトリとしています。下記のようなファイル配置です。
./
|-- src/a.c
|-- src/b.c
|-- src/c.c
|-- build/makefile
`-- build/build.ninja
make する場合は、 make -C build
を行います。 ninja する場合は ninja -C build
となります。
makefile の場合
makefile
の例を示します。デフォルトルールを全力で利用した場合、下記のようになると思います。
並列ビルドを行うと、 libfoo.a
が破壊されるので実用性は皆無です。デフォルトルールを使わない方が良いでしょう。
CFLAGS = -Wall -Os -MMD -MP
CPPFLAGS = -DNDEBUG
vpath %.c ../src
.PHONY:clean
libfoo.a: libfoo.a(a.o) libfoo.a(b.o) libfoo.a(c.o)
libfoo.a(a.o):
libfoo.a(b.o):
libfoo.a(c.o):
clean:
$(RM) libfoo.a a.o a.d b.o b.d c.o c.d
-include a.d
-include b.d
-include c.d
相変わらず、clean ルールの記述が面倒くさい。
初回 make は、中間ファイルが削除されるので、ちょっと気持ちがいい。
しかし、このルール記述だと毎度 ar が走るので、イマイチです。
ninja.build for gcc の場合
上記 makefile と同等の build.ninja を示します。 ninja は、暗黙のルールや変数を持ちません。
そのため、すべて明示的に定義しています。
cc = cc
cflags = -Wall -Os
cppflags = -DNDEBUG
ar = ar
arflags = rv
rule compile
deps = gcc
depfile = $out.d
command = $cc -MMD -MP -MF $out.d $cflags $cppflags $target_arch -c -o $out $in
rule archive
command = $ar $arflags $out $in
build libfoo.a: archive a.o b.o c.o
build a.o: compile ../src/a.c
build b.o: compile ../src/b.c
build c.o: compile ../src/c.c
今回は、makefile と記述量があまり変わりません。変わらない理由は、makefile で変数展開を駆使していないからですが。。。
rule文とbuild文を明確に分けて記述するだけなので、読む側からは理解しやすいです。
makefile だと、サフィックスルールの記述方法が2種類あって、どっちがどうだったとか、ライブラリ用の書き方がどうだったかとかで混乱します。
そして、clean がビルトインされているのがうれしい。
感想
make は暗黙ルールを使わないと負けた気がして、無理矢理使ってしまいがちです。私だけかもしれませんが。
とは言え、暗黙ルールと暗黙の変数を使用しておかないと、環境変数で CLFAGS
や CC
をオーバーライドしたりできなくなってしまうので、使わないとダサい makefile になっちゃいます。
暗黙ルールに従った makefile であれば、 gcc から clang に変えたければ export CC=clang
してから make すれば良いので便利。クロスコンパイルしたければ、export CC=arm-linux-gcc
してから make すれば OK だったりして便利。
といっても、こんな簡単にいかないから autotools や cmake が流行っている訳で。
思い返すと、 makefile を初めて書いたとき、build.ninja のように全てのルールと依存関係を記述していた。慣れてきて、暗黙ルールを駆使して記述量が減ることに喜んでいました。しかしなぁ、全部書くのと機能に違いが無いんだから、書きゃいいよなぁ。昔と違って、エディタもPCリソースも豊かなんだし。ということで、ninja がいいと思います。
参考リンク
The Ninja build system
Static library built-in rule in GNU make