GNU Make の order-only prerequisites をはじめとして、多くのビルド自動化ツールが order-only の依存を表現することができる。
order-only の依存とは何か。order-only の依存は、通常の依存と同様、ターゲットを更新する前に更新されているべきターゲットを宣言する。しかし、通常の依存と異なり、そもそもターゲットを更新する必要があるか否かの判定で考慮されない。 Make でいうと、order-only prerequisites のタイムスタンプが新しくなっても、ターゲットを更新するレシピは実行されない。
何に使うのか? 例えば、自動生成されるヘッダファイルが存在する場合に、再コンパイルの量を減らすことができる。具体例として、 protoc
で生成するヘッダファイルに依存を張る方法を考える。次の 3 つの方法を比較する。
- 何もなし
- normal prerequisites
- order-only prerequisites
実装は、添付の Makefile を参照せよ。
$ make AUTOGEN_PREREQ_STYLE=None clean
rm -f *.pb.cc *.pb.h *.o *.d app
$ make AUTOGEN_PREREQ_STYLE=None
g++ -MMD -o app.o -c app.cc
app.cc:1:10: fatal error: foo.pb.h: No such file or directory
#include "foo.pb.h"
~~~~~~~~
compilation terminated.
Makefile:37: recipe for target 'app.o' failed
make: *** [app.o] Error 1
自動生成されるヘッダファイルに依存を張らなければ、ヘッダファイルが存在しないのだからコンパイルが通らない。
$ make AUTOGEN_PREREQ_STYLE=Normal clean
rm -f *.pb.cc *.pb.h *.o *.d app
$ make AUTOGEN_PREREQ_STYLE=Normal
protoc -I. --cpp_out=. foo.proto --dependency_out=foo.proto.d
protoc -I. --cpp_out=. bar.proto --dependency_out=bar.proto.d
g++ -MMD -o app.o -c app.cc
g++ -MMD -o foo.pb.o -c foo.pb.cc
g++ -MMD -o bar.pb.o -c bar.pb.cc
g++ -o app app.o foo.pb.o bar.pb.o -lprotobuf
コンパイルできるようになった。
$ touch bar.proto
$ make AUTOGEN_PREREQ_STYLE=Normal
protoc -I. --cpp_out=. bar.proto --dependency_out=bar.proto.d
g++ -MMD -o app.o -c app.cc
g++ -MMD -o foo.pb.o -c foo.pb.cc
g++ -MMD -o bar.pb.o -c bar.pb.cc
g++ -o app app.o foo.pb.o bar.pb.o -lprotobuf
しかし、いずれかのヘッダファイルを更新すると、全ての .cc ファイルが再コンパイルされてしまう。
$ make AUTOGEN_PREREQ_STYLE=OrderOnly clean
rm -f *.pb.cc *.pb.h *.o *.d app
$ make AUTOGEN_PREREQ_STYLE=OrderOnly
protoc -I. --cpp_out=. foo.proto --dependency_out=foo.proto.d
protoc -I. --cpp_out=. bar.proto --dependency_out=bar.proto.d
g++ -MMD -o app.o -c app.cc
g++ -MMD -o foo.pb.o -c foo.pb.cc
g++ -MMD -o bar.pb.o -c bar.pb.cc
g++ -o app app.o foo.pb.o bar.pb.o -lprotobuf
$ touch bar.proto
$ make AUTOGEN_PREREQ_STYLE=OrderOnly
protoc -I. --cpp_out=. bar.proto --dependency_out=bar.proto.d
g++ -MMD -o bar.pb.o -c bar.pb.cc
g++ -o app app.o foo.pb.o bar.pb.o -lprotobuf
bar.pb.h
が更新された場合、 bar.pb.cc
だけが再コンパイルされるようになった。
$ touch foo.proto
$ make AUTOGEN_PREREQ_STYLE=OrderOnly
protoc -I. --cpp_out=. foo.proto --dependency_out=foo.proto.d
g++ -MMD -o app.o -c app.cc
g++ -MMD -o foo.pb.o -c foo.pb.cc
g++ -o app app.o foo.pb.o bar.pb.o -lprotobuf
foo.pb.h
が更新された場合は、 app.cc
と foo.pb.cc
だけが再コンパイルされる。