CMake+Ninja 使ってるヤツは直ちに CMake-3.9 に乗り換えろ


タイトルだけで完結しました。 CMake 愛に関しては冥界一を自負し、かつ Ninja-build 大好きな中村です。

以下、私が llvm-dev に投稿したポエムの翻訳ではありません。
http://lists.llvm.org/pipermail/llvm-dev/2017-July/115646.html

Other Changes -- CMake-3.9 Release Notes

The Ninja generator has loosened the dependencies of object compilation. Object compilation now depends only on custom targets and custom commands associated with libraries on which the object’s target depends and no longer depends on the libraries themselves. Source files in dependent targets may now compile without waiting for their targets’ dependencies to link.

add_library(foo.c), add_executable(foo.c) などにおいて、foo.c のコンパイルが target_link_libraries(foo ...) のライブラリに依存しなくなります。つまりなにもしていないのに並列度が向上します。具体的な例を、先日投稿した記事(CMake: target_link_libraries(PUBLIC/PRIVATE/INTERFACE) の実践的な解説)SHAREDの例を使って説明しましょう。

add_library(lib1 SHARED lib1.c)

add_library(lib2 SHARED lib2.c)
target_link_libraries(lib2 PRIVATE lib1)

add_library(lib3 SHARED lib3.c)
target_link_libraries(lib3 PRIVATE lib2)

add_executable(foo foo.c)
# キーワードは省略可もしくはPRIVATE/PUBLIC
# foo は lib1 にも依存しているのでここで明示
target_link_libraries(foo lib1 lib3)

CMake < 3.9 においては、この例におけるすべてのアクションが直列に実行されます。64コアプロセッサを使ってるデベロッパには到底許されることではありません。

Order アクション
1 lib1.o: lib1.c
2 lib1.dll: lib1.o
3 lib2.o: lib2.c
4 lib2.dll: lib2.o lib1.dll
5 lib3.o: lib3.c
6 lib3.dll: lib3.o lib2.dll
7 foo.o: foo.c
8 foo.exe: foo.o lib1.dll lib3.dll

CMake-3.9+Ninja では以下のように並列化されます。DAGを図示した方がいいのですがよだきい(方言)のでご容赦ください。
ポイントは、*.o: *.c が並列化されているところです。もちろん先日の記事にてINTERFACEを利用している場合には *.a: *.o も並列化されます(DLL どうしはシンボル解決の必要があるため依存関係を崩せない)。

Order アクション
1 lib1.o: lib1.c lib2.o: lib2.c lib3.o: lib3.c foo.o: foo.c
2 lib1.dll: lib1.o
3 lib2.dll: lib2.o lib1.dll
4 lib3.dll: lib3.o lib2.dll
5 foo.exe: foo.o lib1.dll lib3.dll

※実際には foo.exe は lib2.dll に依存していないので直接の依存関係はありません。

なお、既存のルールにおける互換性として、add_custom_command(), add_custom_target(), add_dependencies() により追加された依存は先送りされず add_library(lib1.c)lib1.o: lib1.c に依存するようになります。つまりヘッダファイル生成などのカスタムルールが無視されたりすることはありません。逆にいうと add_executable(foo.c)foo.o: foo.c は、もし依存ライブラリが add_dependencies() を持っていた場合、それに依存することになります。

以上、CMake-3.9 における劇的な改良点のひとつを紹介しました。個人的には dirty hack が実装されたようにしか見えないんですけどね…

先日の投稿(CMake: target_link_libraries(PUBLIC/PRIVATE/INTERFACE) の実践的な解説) のノウハウを骨抜きにしてしまいかねない改良ですが、あくまでも本記事の内容は -GNinja でのみ有効です。CMake を使わされているみなさんは諦めてキレイな CMakeLists.txt を書きましょう。

CMake そのものが汚いけどな!!! (俺が言いたかったのはこれだけ)