CMake 高度な機能
CMake の基礎を習得した後、高度な機能を利用すると、複雑なプロジェクトのビルド構成をより柔軟に管理できます。
この記事では、カスタムモジュール、マルチ構成ビルド、高度なパッケージ検索、カスタムビルドステップ、クロスプラットフォームとクロスコンパイル、ターゲット属性構成の6つの主要テーマを扱います。
全体内容の概要:
| テーマ | 中核機能 | 典型的な適用シナリオ |
|---|---|---|
| カスタムモジュールとスクリプト | 再利用可能な CMake ロジックのカプセル化 | コンパイラオプションの統一、カスタム検索ロジック、チーム共有ビルドルール |
| ビルド構成とターゲット | マルチ構成とマルチターゲットの管理 | Debug/Release の切り替え、複数の出力成果物のビルド |
| 高度な検索と構成 | 外部依存関係を柔軟に特定する | 特定コンポーネントの検索、構成ファイルを介したビルド情報の受け渡し |
| カスタムビルドステップ | ビルドプロセスの拡張 | コード生成、リソース処理、ビルド後の自動化 |
| クロスプラットフォームとクロスコンパイル | マルチプラットフォーム向けビルド | 組み込み開発、異なるOS向けのコンパイル |
| ターゲット属性と構成 | コンパイルとリンクを細かく制御 | 特定のターゲットに警告レベル、リンクパスを設定する |
カスタム CMake モジュールとスクリプト
複数の CMakeLists.txt で同じロジックを繰り返す必要がある場合、そのロジックをカスタムモジュールにカプセル化すると、コードの冗長性を大幅に削減できます。
カスタムモジュールとスクリプトの本質は、よく使う CMake 関数、マクロ、設定を独立したファイルに抽出し、複数のプロジェクトで再利用できるようにすることです。
カスタム CMake モジュール
カスタムモジュールは、.cmakeを拡張子とするファイルであり、include で読み込める CMake 関数とマクロが定義されています。
作成手順:
| 手順 | 操作 | 説明 |
|---|---|---|
| 1 | 作成cmake/ディレクトリ |
プロジェクトルート直下に作成し、カスタムモジュールを集中管理する |
| 2 | モジュールファイルの作成 | 例:cmake/MyModule.cmakeカスタム関数を記述する |
| 3 | モジュール検索パスの拡張 | CMakeLists.txt で CMAKE_MODULE_PATH を使用してそのディレクトリを登録する |
| 4 | モジュールの読み込み | include() を使用してモジュールを読み込み、その中の関数を呼び出す |
カスタムモジュールの例——MyModule.cmake:
例
# カスタムモジュール:プロジェクト共通のヘルパー関数を提供する
# 関数:すべてのターゲットにコンパイラの警告オプションを一括設定する
# 引数 ARG_TARGET:警告を設定する必要があるターゲット名
function(サンプル_set_warnings ARG_TARGET)
# コンパイラの種類に応じて異なる警告フラグを設定する
if(MSVC)
# MSVC 编译器:启用 /W4 警告级别
target_compile_options(${ARG_TARGET} PRIVATE /W4)
else()
# GCC / Clang 编译器:启用常用警告
target_compile_options(${ARG_TARGET} PRIVATE -Wall -Wextra -Wpedantic)
endif()
endfunction()
# 関数:ターゲットの基本的なビルド情報を出力する
function(サンプル_print_target_info ARG_TARGET)
message(STATUS "目标名称:${ARG_TARGET}")
message(STATUS "源文件列表:$<TARGET_PROPERTY:${ARG_TARGET},SOURCES>")
endfunction()
CMakeLists.txt でモジュールを読み込んで使用する:
例
cmake_minimum_required(VERSION 3.10)
project(MyProject CXX)
# 将 cmake/ 目录添加到模块搜索路径
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# 加载自定义模块
include(MyModule)
# 添加可执行文件目标
add_executable(MyApp main.cpp)
# モジュール内のカスタム関数を呼び出して、MyApp に警告オプションを設定します
サンプル_set_warnings(MyApp)
# モジュール内のカスタム関数を呼び出して、目標情報を出力する
サンプル_print_target_info(MyApp)
CMAKE_MODULE_PATHこれは、CMake がモジュールファイルを探すときの検索パスのリストです。CMake はまず組み込みモジュールパスを探し、見つからない場合は CMAKE_MODULE_PATH で指定されたパスを探します。
カスタム CMake スクリプトの使用
カスタムスクリプトはモジュールに似ていますが、通常は再利用可能な関数を定義するためではなく、構成操作を実行するために使用されます。
スクリプトは include() で直接読み込むことができ、CMake はその中の各命令を順番に実行します。
スクリプトファイル config.cmake を作成します:
例
# カスタムスクリプト:プロジェクトの設定オプションを一元管理する
# デフォルトのビルドタイプを Release に設定する
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "构建类型" FORCE)
message(STATUS "未指定构建类型,默认使用 Release")
endif()
# ビルドタイプに応じて異なるコンパイルオプションを設定する
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "启用 Debug 模式:关闭优化,开启调试符号")
else()
message(STATUS "启用 Release 模式:开启 O2 优化")
endif()
CMakeLists.txt でスクリプトを呼び出します:
# 使用绝对路径加载脚本
include(${CMAKE_SOURCE_DIR}/config.cmake)
ビルド構成とターゲット
CMake は、1つのプロジェクト内で複数のビルド構成(Debug や Release など)を管理し、複数のビルドターゲットを定義できます。
マルチ構成ジェネレーター
CMake ジェネレーターによって、構成の処理方法は異なります。
Visual Studio や Xcode のようなマルチ構成ジェネレーター同じビルドディレクトリ内で Debug/Release を切り替えることができますが、Unix Makefiles などの単一構成ジェネレーターは構成段階でビルドタイプを決定する必要があります。
| ジェネレーターの種類 | 代表的なジェネレーター | 構成の指定方法 |
|---|---|---|
| 単一構成 | Unix Makefiles、Ninja | 構成時に-DCMAKE_BUILD_TYPE=Release指定する |
| マルチ構成 | Visual Studio、Xcode | ビルド時に--config Release切り替える |
CMakeLists.txt でデフォルト構成を設定する:
# 仅对单配置生成器生效,多配置生成器会忽略此变量 set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type")
CMAKE_BUILD_TYPE は単一構成ジェネレーターでのみ有効です。Visual Studio を使用する場合は、cmake --build . --config Release または IDE で構成を切り替えてください。
ビルドターゲット
1つの CMake プロジェクトで複数のビルドターゲットを定義でき、各ターゲットはコンパイルオプションとリンク依存関係を個別に構成できます。
これにより、1回のビルドで複数の実行ファイルや複数のライブラリを生成できます。
例
add_executable(MyApp src/main.cpp)
add_executable(MyTool src/tool.cpp)
# ターゲットごとに異なるコンパイルマクロを設定し、条件コンパイルを制御する
set_target_properties(MyApp PROPERTIES COMPILE_DEFINITIONS "APP_MODE")
set_target_properties(MyTool PROPERTIES COMPILE_DEFINITIONS "TOOL_MODE")
# 異なるターゲットに異なるライブラリをリンクする
target_link_libraries(MyApp PRIVATE MyLib)
target_link_libraries(MyTool PRIVATE MyLib CLI11::CLI11)
高度な検索と構成
基本的な find_package の使い方に加えて、CMake はコンポーネントの指定、バージョンの詳細な制御、構成ファイルテンプレートによるビルド情報の受け渡しもサポートしています。
find_package の高度な使い方
COMPONENTSキーワードを使用すると、パッケージ全体ではなく、ライブラリ内の特定のサブモジュールのみを検索できます。
これは Boost のような大規模なライブラリにとって非常に重要です。必要なものだけを導入することで、不要なコンポーネントのリンクを避けられます。
例
# filesystem は内部で system に依存するため、両方を宣言する必要がある
find_package(Boost REQUIRED COMPONENTS filesystem system)
# 必要なコンポーネントが見つかったか確認する
if(Boost_FOUND)
message(STATUS Boost バージョン:${Boost_VERSION})
message(STATUS Boost ヘッダーファイルパス:${Boost_INCLUDE_DIRS})
# 必要な各コンポーネントを個別にリンクする
target_link_libraries(MyApp PRIVATE
Boost::filesystem
Boost::system
)
endif()
検索パスの指定(非標準のインストール場所向け):
# 方式一:通过变量指定根目录 cmake .. -DBOOST_ROOT=/path/to/custom/boost # 方式二:在 CMakeLists.txt 中预先设置 set(BOOST_ROOT "/path/to/custom/boost") find_package(Boost REQUIRED COMPONENTS filesystem)
構成ファイルとビルドオプション
configure_file()このディレクティブは CMake 変数の値をコードファイルに注入し、コンパイル時に設定情報を渡すことを可能にします。
これは、バージョン番号やビルド時刻などの情報をプログラムにコンパイルする必要がある場合に非常に便利です。
設定テンプレートファイル config.h.in を作成:
例
// CMake 設定テンプレートファイル。configure_file() によって config.h を生成する
// プロジェクトバージョン番号(CMake 変数 @PROJECT_VERSION@ で置き換えられる)
#define サンプル_VERSION "@PROJECT_VERSION@"
// ビルドタイプ(Debug または Release)
#define サンプル_BUILD_TYPE "@CMAKE_BUILD_TYPE@"
// コンパイルタイムスタンプ
#define サンプル_BUILD_TIMESTAMP "@BUILD_TIMESTAMP@"
CMakeLists.txt で設定し、ヘッダーファイルを生成する:
例
string(TIMESTAMP BUILD_TIMESTAMP "%Y-%m-%d %H:%M:%S")
# config.h.in 内の @変数@ を CMake 変数の値に置き換える
# build ディレクトリ内の config.h に出力する
configure_file(config.h.in config.h)
# build ディレクトリをヘッダーファイルの検索パスに追加し、ソースファイルが config.h を見つけられるようにする
include_directories(${CMAKE_BINARY_DIR})
生成された設定をソースファイルで使用する:
// 在 main.cpp 中包含生成的配置文件 #include "config.h" std::cout << "サンプル Version: " << サンプル_VERSION << std::endl; std::cout << "Build Type: " << サンプル_BUILD_TYPE << std::endl;
カスタムビルドステップの生成
標準のコンパイルとリンクに加えて、CMake ではビルドプロセスにカスタム操作(コード生成、リソースコピー、ビルド後の通知など)を挿入できます。
カスタムコマンド
add_custom_command()あるファイルを更新する必要があるときに実行されるカスタムコマンドを定義します。
このディレクティブの核となる概念は依存関係駆動——OUTPUT ファイルが存在しないか、DEPENDS ファイルが更新された場合にのみ、コマンドが実行されます。
例
# generated_file.cpp が存在しないか、input_template.txt が更新された場合のみ実行される
add_custom_command(
# 出力ファイル(CMake はこのファイルをチェックしてコマンドを再実行するか決定する)
OUTPUT ${CMAKE_BINARY_DIR}/generated_file.cpp
# 実行するコマンド(ここでは cmake -E で組み込みのファイル生成操作を実行)
COMMAND ${CMAKE_COMMAND}
-E echo "// 自動生成されたソースファイル" > ${CMAKE_BINARY_DIR}/generated_file.cpp
# 依存ファイル(このファイルが変更されるとコマンドが再実行される)
DEPENDS ${CMAKE_SOURCE_DIR}/input_template.txt
# 実行時に出力するメッセージ
COMMENT "テンプレートからソースファイルを生成しています..."
)
ビルド段階でトリガーされるコマンド——リンク完了後に実行:
例
add_custom_command(
TARGET MyApp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:MyApp>
${CMAKE_SOURCE_DIR}/deploy/
COMMENT "MyApp をデプロイディレクトリにコピーします"
)
カスタムターゲット
add_custom_target()出力ファイルを生成しないビルドターゲットを定義します。通常、複数のカスタムコマンドをまとめるために使用します。
ALL キーワードを使用しない限り、カスタムターゲットはデフォルトのビルドでは実行されません。ターゲット名を明示的に指定する必要があります。
例
# ALL はデフォルトのビルド(make)で自動実行されることを示す
add_custom_target(generate_code ALL
# 前述の OUTPUT ファイルに依存する
DEPENDS ${CMAKE_BINARY_DIR}/generated_file.cpp
)
# カスタムターゲット: 補助タスクを実行する(デフォルトのビルドでは実行されない)
add_custom_target(deploy
COMMAND ${CMAKE_COMMAND} -E echo "サーバーにデプロイしています..."
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_BINARY_DIR}/bin
/opt/サンプル/deploy/
COMMENT "デプロイ操作を実行します"
)
# ビルド時にこのターゲットを個別にトリガーする
# $ cmake --build . --target deploy
add_custom_command と add_custom_target の違い:前者は「ファイルをどのように生成するか」というルールを定義し、依存関係によってトリガーされます。後者は「何をするか」というアクションターゲットを定義し、明示的に呼び出すか、ALL 指定時に自動実行されます。
クロスプラットフォームとクロスコンパイル
CMake の核心的な利点の 1 つはクロスプラットフォーム対応です。同じ CMakeLists.txt から、異なる OS やアーキテクチャに対応するビルドファイルを生成できます。
クロスプラットフォームビルド
CMake は現在のプラットフォームのコンパイラとシステム環境を自動的に検出し、それに合ったビルドファイルを生成します。
変数を使用してターゲットプラットフォーム情報を明示的に指定することもできます。
| 変数 | 役割 | サンプル値 |
|---|---|---|
CMAKE_SYSTEM_NAME |
ターゲット OS | Linux、Windows、Darwin、Android |
CMAKE_SYSTEM_PROCESSOR |
ターゲットプロセッサアーキテクチャ | x86_64、arm、aarch64 |
CMAKE_C_COMPILER |
C コンパイラのパス | /usr/bin/arm-linux-gnueabihf-gcc |
CMAKE_CXX_COMPILER |
C++ コンパイラのパス | /usr/bin/arm-linux-gnueabihf-g++ |
クロスコンパイル
クロスコンパイルとは、現在のプラットフォーム(例: x86_64 Linux)上で、別のアーキテクチャ(例: ARM)で動作するプログラムをコンパイルすることです。
CMake は、ツールチェーンファイル(Toolchain File)を使用して、クロスコンパイルに必要なコンパイラ、システム情報、リンカー設定を指定します。
ツールチェーンファイル toolchain.cmake を作成:
例
# ARM Linux クロスコンパイルツールチェーン設定ファイル
# ターゲットシステムは Linux
set(CMAKE_SYSTEM_NAME Linux)
# ターゲットプロセッサアーキテクチャは ARM
set(CMAKE_SYSTEM_PROCESSOR arm)
# クロスコンパイラを指定(必須:パスは実際のツールチェーンの場所に合わせて調整すること)
set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++)
# ターゲットシステムのルートファイルシステムのパスを指定(任意。ライブラリとヘッダーファイルの検索に使用)
set(CMAKE_FIND_ROOT_PATH /path/to/arm-sysroot)
# プログラムの検索はターゲットシステム内のパスだけを検索する(ホストのパスは検索しない)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# ライブラリの検索はターゲットシステム内のパスだけを検索する
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# ヘッダーファイルの検索はターゲットシステム内のパスだけを検索する
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
ツールチェーンファイルを使用してクロスコンパイルを実行する:
# 配置时通过 -DCMAKE_TOOLCHAIN_FILE 指定工具链文件 mkdir build_arm && cd build_arm cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake .. # 使用生成的 Makefile 编译(将调用 ARM 交叉编译器) make
ツールチェーンファイルは、最初に cmake を実行するときに指定する必要があります。既に CMakeCache.txt が存在する場合は、build ディレクトリを削除して再設定しないとツールチェーンを切り替えられません。
ターゲット属性と構成
CMake では、ターゲット単位でコンパイルオプション、リンクオプション、さまざまなプロパティを細かく制御できます。これは現代の CMake で推奨される方法です。
ターゲット属性
set_target_properties()ターゲットレベルのプロパティを設定するために使用します。これらのプロパティはコンパイラとリンカーの動作に影響します。
| プロパティ | 役割 | 例 |
|---|---|---|
COMPILE_OPTIONS |
コンパイラオプションを設定する | -Wall、-O2 |
COMPILE_DEFINITIONS |
プリプロセッサマクロを設定する | DEBUG、VERSION=2 |
LINK_FLAGS |
リンカーフラグを設定する | -L/path/to/lib |
OUTPUT_NAME |
出力ファイル名を変更する | ターゲット名は MyApp、出力は my_app |
例
add_executable(MyApp main.cpp)
# ターゲットにコンパイルオプションとリンカーフラグを設定する
set_target_properties(MyApp PROPERTIES
COMPILE_OPTIONS "-Wall;-Wextra"
COMPILE_DEFINITIONS "サンプル_DEBUG"
LINK_FLAGS "-L/usr/local/lib"
OUTPUT_NAME "my_app"
)
# 上記の set_target_properties は、以下の 3 つのディレクティブと同じ効果を持つ:
# target_compile_options(MyApp PRIVATE -Wall -Wextra)
# target_compile_definitions(MyApp PRIVATE サンプル_DEBUG)
# target_link_options(MyApp PRIVATE -L/usr/local/lib)
現代の CMake では、set_target_properties() の代わりに target_compile_options()、target_compile_definitions()、target_link_options() の使用が推奨されます。これらは PUBLIC/PRIVATE/INTERFACE の可視性制御をサポートし、依存関係がより明確になるためです。
カスタムコンパイル/リンクオプション
target 系のディレクティブを使用すると、オプションの伝播範囲をより精密に制御できます。
例
# PRIVATE: MyApp 自身のコンパイル時のみこれらのオプションを使用する
target_compile_options(MyApp PRIVATE -Wall -Wextra -Wpedantic)
# MyApp のプリプロセッサ定義を設定する
# ソースファイルに #define サンプル_VERSION "1.0" と書くのと同じ
target_compile_definitions(MyApp PRIVATE サンプル_VERSION="1.0")
# MyApp のリンクオプションを設定する
target_link_options(MyApp PRIVATE -L/usr/local/lib)
# ライブラリの場合、PUBLIC を使用すると、このライブラリにリンクするターゲットもこれらのオプションを継承できる
# 例: MyLib を使用する任意のターゲットは自動的に C++17 が有効になる
target_compile_features(MyLib PUBLIC cxx_std_17)
注意事項
CMAKE_BUILD_TYPE はシングルコンフィギュレーションジェネレーターでのみ有効です。プロジェクトで Makefile と Visual Studio の両方をサポートする必要がある場合は、CMAKE_BUILD_TYPE の値に依存しないでください。ジェネレーター式を使用し、
$<$<CONFIG:Debug>:DEBUG_VALUE>実際の構成に応じて動的に切り替えます。
target_xxx 系のディレクティブを優先的に使用してください。set_target_properties は強力ですが、可視性の制御が不足しています。PUBLIC/PRIVATE/INTERFACE のセマンティクスが必要な場面では、target_compile_options などの専用ディレクティブを使用する方が良い選択です。
ツールチェーンファイルは最初の cmake 時に指定する必要があります。クロスコンパイル時、既存の CMakeCache.txt があるディレクトリでツールチェーンを再指定すると、不完全な構成が生成される可能性があります。常にクリーンな build ディレクトリでクロスコンパイルを構成してください。
その他の拡張機能カスタムモジュール内の関数名にはプレフィックスを付けることをお勧めします。CMake の組み込みコマンドや他のモジュールとの名前の競合を避けてください。例えば、プロジェクト名をプレフィックスとして使用します:myproject_set_warnings()。