MENU

CMake — C/C++を多プラットフォームで束ねるメタビルド

CMake アイキャッチ
CMake

CMakeは2000年に米国Kitware社のビル・ホフマン氏らがNIH(米国国立衛生研究所)のITKプロジェクト向けに開発を始めた、C/C++を中心とするビルドスクリプト生成ツールです。プラットフォームごとに異なるビルド系(Unix Makefile・Ninja・Visual Studioソリューション・Xcodeプロジェクトなど)を、CMakeLists.txtという共通スクリプトから自動生成できる点が最大の特徴で、メタビルドツールと呼ばれます。LLVMやKDE、Qt6本体、Blender、Boostの一部などC/C++の主要プロジェクトで幅広く採用されており、現在ではC/C++ビルドの事実上の業界標準と言える存在になっています。

目次

この記事の目次

  1. CMakeLists.txtによる宣言的記述
  2. ジェネレータと多プラットフォーム生成
  3. find_packageとFetchContentで依存解決
  4. MakeやBazelとの位置取り
  5. まとめ

CMakeLists.txtによる宣言的記述

CMakeLists.txtによる宣言的記述

CMakeの中心はCMakeLists.txtで、project(MyApp CXX)でプロジェクトを宣言し、add_executable(myapp src/main.cpp)add_library(mylib STATIC src/lib.cpp)でターゲットを定義します。ターゲットに対しtarget_include_directoriestarget_link_librariestarget_compile_featuresを呼ぶ「ターゲット中心アプローチ」が現代CMake(バージョン3.x以降)の作法とされています。古い書き方ではinclude_directoriesをグローバル設定で散らかしがちでしたが、ターゲットごとに属性を閉じ込めることで巨大プロジェクトでも見通しを保てます。

サブディレクトリはadd_subdirectory(libs/json)で参照でき、各サブディレクトリにCMakeLists.txtを置く再帰的な構成が一般的です。条件分岐のif(WIN32)if(APPLE)if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")によりプラットフォーム別の設定を1ファイル内で表現でき、Windows/macOS/Linuxの差をスクリプトレベルで吸収します。CMake自体はチューリング完全のスクリプト言語ですが、複雑な処理を書き始めると保守性が落ちやすいため、シンプルに留める設計指針が広く共有されています。

ジェネレータと多プラットフォーム生成

ジェネレータと多プラットフォーム生成

CMakeはcmake -G "Ninja" -S . -B buildのようにジェネレータを指定すると、ビルドディレクトリにNinjaのbuild.ninjaファイルを生成します。-G "Unix Makefiles"ならMakefile、-G "Visual Studio 17 2022"なら.slnと.vcxproj、-G XcodeならXcodeプロジェクトと、出力先のビルド系を切り替えられるのが「メタビルド」の真骨頂です。ソース側は共通のCMakeLists.txt1つで済むため、Windows開発者がVisual Studioで、macOS開発者がXcodeで、CI/LinuxがNinjaで、と異なる環境を平和に共存させられます。

ジェネレータ選定では、現在はNinjaが速度面で推奨され、特にLLVMやChromiumのような巨大C++プロジェクトでは標準的に使われています。Visual Studioもジェネレータとして使えますが、IDE統合のためだけにスローダウンを許容するのは惜しいので、CMakeでNinjaを生成しつつVS Codeで開く構成も増えています。CMake 3.21以降は--workflowオプションでconfigure/build/test/installの一連を1コマンドで実行でき、CIスクリプトが簡素化されています。

find_packageとFetchContentで依存解決

find_packageとFetchContentで依存解決

C/C++には公式パッケージマネージャがないため、CMakeのfind_package(OpenSSL REQUIRED)のような関数で、OS側に入っているライブラリを探して紐づけるのが伝統的なやり方です。Boost・Qt・OpenCV・OpenSSLなど主要ライブラリはConfigパッケージ(Boost::filesystemのようなターゲット)を提供しており、target_link_libraries(myapp PRIVATE Boost::filesystem)で簡潔に書けます。FindXXX.cmakeモジュールという旧来方式と、現代的なBoostConfig.cmakeのようなConfigモジュール方式の2系統が並存している点はC++コミュニティの歴史的な負債と言えます。

近年はFetchContent_DeclareFetchContent_MakeAvailableを使って、依存ライブラリのソースをGitHubから取得しビルドツリーへ組み込む方式が定着しています。Microsoft製のvcpkgやJFrog傘下のConanといった外部パッケージマネージャもCMakeと深く連携し、vcpkg.jsonconanfile.txtに依存を書けばCMake側はfind_packageで参照できる構成を取れます。「依存解決はvcpkg/Conanに任せ、ビルドはCMake」という分担が、現代C++プロジェクトの王道になりつつあります。

MakeやBazelとの位置取り

MakeやBazelとの位置取り

C/C++ビルドツールの選択肢は今やCMake以外にも豊富で、Mesonは2013年にJussi Pakkanen氏が公開したPython製のメタビルドで、CMakeより簡潔な構文が支持を集めています。GNOMEプロジェクトはAutotoolsからMesonへ移行しており、新規プロジェクトでMesonを選ぶ事例も増えています。BazelやBuck2はモノレポ志向で、CMakeとは方向性が違い、企業内モノレポでGoogleや一部金融機関が採用しています。

Make(1976年〜)はそのままでは多プラットフォーム対応が大変なため、結局はCMakeかautoconf/automake(Autotools)でMakefileを生成する運用が標準です。Autotoolsは古くからのGNUプロジェクトで使われ続けていますが、Windowsとの相性が悪くMSYS2やCygwinが前提となるため、新規開発でCMakeに置き換える事例が多いです。迷ったら「主要OSと主要IDEを跨ぐならCMake、シンプルで小規模ならMesonかMake」という基準が現実的です。

まとめ

CMakeは2000年にKitware社が公開したC/C++向けメタビルドツールで、CMakeLists.txtから各プラットフォームのビルド系を生成する設計でクロスプラットフォーム開発を支えてきました。ターゲット中心の設計・find_package・FetchContent・vcpkg/Conan連携により、巨大C++プロジェクトでも保守性の高いビルドが実現できます。MesonやBazelと棲み分けつつ、LLVMやQtなど主要プロジェクトを支える現代C/C++ビルドの事実上の標準として定着しています。

※本記事はIT用語辞典の手書きドラフトです。公開前に最新情報・出典を確認のうえ加筆修正してください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次