CMake 基礎
CMake は、C/C++ プロジェクトのコンパイルプロセスを管理するためのクロスプラットフォームのビルドシステム生成ツールです。
CMakeLists.txt 設定ファイルを記述することで、プロジェクトのビルドルール、依存関係、コンパイルオプションを定義でき、CMake が対応するプラットフォームのビルドファイル(Unix の Makefile や Windows の Visual Studio ソリューションなど)を自動生成します。
CMakeLists.txt ファイル
CMakeLists.txt は CMake の中核となる設定ファイルであり、各 CMake プロジェクトには少なくとも1つの CMakeLists.txt ファイルが必要です。
CMake はファイル内のディレクティブを読み取ることで、プロジェクトの構造、ソースファイルリスト、コンパイル要件を把握します。
基本構文
CMakeLists.txt は一連の CMake ディレクティブで構成され、各ディレクティブの形式は次のとおりです:コマンド名(引数リスト)。
以下は、CMake プロジェクトを構築する際に最もよく使われるディレクティブとその例です。
CMake の最低バージョンを指定:
例
# このディレクティブは CMakeLists.txt の最上部に置く必要があります
# 例: CMake バージョン 3.10 以上を要求
cmake_minimum_required(VERSION 3.10)
プロジェクト名と言語を定義:
例
# 言語パラメータはオプション。一般的な値: CXX(C++)、C(C 言語)
# project() を呼び出すと PROJECT_NAME などの変数が自動的に設定されます
project(MyProject CXX)
実行ファイルを追加:
例
# 指定されたソースファイルをコンパイルして実行ファイルを生成します
add_executable(MyApp main.cpp utils.cpp)
ライブラリファイルを追加:
例
# STATIC: 静的ライブラリ(.a / .lib)。コンパイル時に実行ファイルに直接埋め込まれます
# SHARED: 動的ライブラリ(.so / .dll)。実行時にロードされます
# タイプを指定しない場合、BUILD_SHARED_LIBS 変数によって決まります
add_library(MyLib STATIC library.cpp)
ライブラリをターゲットにリンク:
例
# 指定されたライブラリをターゲット(実行ファイルまたは他のライブラリ)にリンクします
# 自分のプロジェクト内のライブラリターゲットにも、外部ライブラリにもリンクできます
target_link_libraries(MyApp PRIVATE MyLib)
変数を設定:
例
# 通常の変数を定義し、後で ${変数名} で参照します
set(CMAKE_CXX_STANDARD 17)
# 複数の値を同時に設定(リスト)
set(SOURCES main.cpp utils.cpp helper.cpp)
add_executable(MyApp ${SOURCES})
ターゲットにヘッダーファイルパスを指定:
例
# [BEFORE | AFTER]
# [SYSTEM]
# [PUBLIC | PRIVATE | INTERFACE]
# <パス>...)
# PUBLIC: 現在のターゲットとそれに依存するターゲットの両方がこのパスを使用できます
# PRIVATE: 現在のターゲットのみが使用できます
# INTERFACE: 現在のターゲットに依存する他のターゲットのみが使用できます
target_include_directories(MyApp PRIVATE ${PROJECT_SOURCE_DIR}/include)
インストールルールを設定:
例
# [RUNTIME DESTINATION <実行ファイルのインストールパス>]
# [LIBRARY DESTINATION <動的ライブラリのインストールパス>]
# [ARCHIVE DESTINATION <静的ライブラリのインストールパス>]
# [INCLUDES DESTINATION <ヘッダーファイルのインストールパス>])
# make install 実行時の各タイプのファイルのインストール場所を定義します
install(TARGETS MyApp RUNTIME DESTINATION bin)
条件文:
例
# CMake がサポートする条件式には、比較、論理演算、変数判定などがあります
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Debug モードでデバッグ情報を有効にする
message(STATUS "現在は Debug ビルドモードです")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
else()
# Release モードで最適化を有効にする
message(STATUS "現在は Release ビルドモードです")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
カスタムコマンド:
例
# TARGET <ターゲット>
# PRE_BUILD | PRE_LINK | POST_BUILD
# COMMAND <コマンド> [引数...]
# [COMMENT <説明>]
# [VERBATIM])
# PRE_BUILD: コンパイル前に実行
# PRE_LINK: リンク前に実行
# POST_BUILD: ビルド完了後に実行
add_custom_command(
TARGET MyApp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "ビルド完了!"
COMMENT "ビルド完了情報を出力"
)
完全な例
以下は、上記の複数のディレクティブを総合的に使用した完全な CMakeLists.txt の例です。
例
# 1. CMake の最低バージョンを指定(最上部に置く必要があります)
cmake_minimum_required(VERSION 3.10)
# 2. プロジェクト名、バージョン、言語を定義
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# 3. C++ 標準を C++17 に設定し、その標準の使用を強制
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 4. 静的ライブラリのターゲットを追加
add_library(MyLib STATIC
src/lib/core.cpp # コア機能の実装
src/lib/helper.cpp # 補助関数の実装
)
# 5. 実行ファイルのターゲットを追加
add_executable(MyApp
src/main.cpp # プログラムエントリ
src/utils.cpp # ユーティリティ関数
)
# 6. ライブラリターゲットに公開ヘッダーファイルパスを指定
# PUBLIC は、MyLib を使用するターゲットもこのパスにアクセスできることを示します
target_include_directories(MyLib PUBLIC ${PROJECT_SOURCE_DIR}/include)
# 7. 実行ファイルにプライベートヘッダーファイルパスを指定
target_include_directories(MyApp PRIVATE ${PROJECT_SOURCE_DIR}/src)
# 8. MyLib を MyApp にリンク
target_link_libraries(MyApp PRIVATE MyLib)
# 9. インストールルールを設定
install(TARGETS MyApp RUNTIME DESTINATION bin)
install(TARGETS MyLib ARCHIVE DESTINATION lib)
このプロジェクトを構築する典型的なコマンドは次のとおりです:
# 在项目根目录下创建 build 目录(推荐外部构建) $ mkdir build && cd build # 生成构建文件 $ cmake .. # 编译项目 $ cmake --build . # 安装到系统目录(可选) $ cmake --install .
変数とキャッシュ
CMake は変数を使用して設定情報を保存・伝達し、変数は CMakeLists.txt 内で定義して使用できます。
変数は2つのカテゴリに分けられます:通常の変数とキャッシュ変数、それらのスコープとライフサイクルは異なります。
通常の変数
通常の変数は CMakeLists.txt 内で定義され、スコープは現在のディレクトリとそのサブディレクトリです。
通常の変数は CMake の実行ごとに再計算され、永続化されません。
例
set(MY_APP_NAME "サンプル-app")
# リスト変数を定義(セミコロンまたはスペースで区切る)
set(SOURCE_FILES main.cpp utils.cpp helper.cpp)
# 変数の使用:${変数名} で参照する
message(STATUS "プロジェクト名:${MY_APP_NAME}")
# リスト変数の使用:各ソースファイルをループ処理する
foreach(FILE ${SOURCE_FILES})
message(STATUS "ソースファイル:${FILE}")
endforeach()
キャッシュ変数
キャッシュ変数は CMakeCache.txt ファイルに保存され、複数回の CMake 実行間で値が保持されます。
キャッシュ変数は通常、ユーザーが CMake 設定段階でビルド設定をカスタマイズするために使用されます。例えば、機能のオン/オフやインストールパスの指定などです。
キャッシュ変数の値は永続化されます。CMakeLists.txt 内のキャッシュ変数のデフォルト値を変更した場合、新しいデフォルト値を有効にするには CMakeCache.txt を削除するか、cmake を再実行する必要があります。
例
# 型は選択可能:STRING、BOOL、PATH、FILEPATH
# CMakeCache.txt にその変数がすでに存在する場合、set は既存の値を上書きしません
# 文字列型のキャッシュ変数を定義(ユーザーが変更可能なインストールパス)
set(MY_INSTALL_PATH "/usr/local" CACHE PATH "プログラムのインストールパス")
# ブール型のキャッシュ変数を定義(機能のオン/オフ)
set(ENABLE_LOGGING ON CACHE BOOL "ログ機能を有効にするかどうか")
# キャッシュ変数は他の変数と同じように使用する
if(ENABLE_LOGGING)
message(STATUS "ログ機能が有効です")
add_definitions(-DENABLE_LOGGING)
endif()
ユーザーはコマンドラインで-Dパラメータを指定してキャッシュ変数を変更できます:
# 在 cmake 命令中通过 -D 修改缓存变量 $ cmake .. -DMY_INSTALL_PATH=/opt/サンプル -DENABLE_LOGGING=OFF
ヘッダーファイル検索パス
CMake では、コンパイラがヘッダーファイルを検索するパスを指定する方法が2つあります:include_directories()とtarget_include_directories()。
両者の違いを理解することは、明確で保守しやすい CMake プロジェクトを作成するために非常に重要です。
include_directories と target_include_directories の比較
どちらもコンパイラにヘッダーファイル検索パスを追加できますが、作用範囲と制御の粒度が異なります。
| 特性 | include_directories() |
target_include_directories() |
|---|---|---|
| 作用範囲 | グローバルスコープで、現在のディレクトリおよびサブディレクトリ内のすべてのターゲットに影響する | 指定されたターゲットにのみ作用し、他のターゲットを汚染しない |
| 推奨度 | 推奨されない。旧版の CMake プロジェクトを保守する場合を除く | 優先的に使用することを推奨。現代の CMake ベストプラクティスに適合 |
| ターゲットとの関連性 | 特定のターゲットに直接関連せず、他のターゲットに意図せず影響を与える可能性がある | 指定されたターゲットに明示的にバインドされ、依存関係が一目でわかる |
| 保守性 | 低い。グローバルなパス汚染を引き起こしやすく、問題の特定が難しい | 高い。パスがターゲットにバインドされ、ロジックが明確 |
| 可視性の制御 | 伝播範囲を正確に制御できない | PUBLIC、PRIVATE、INTERFACE で正確に制御できる |
PUBLIC / PRIVATE / INTERFACE の意味
target_include_directories()の主な利点は、可視性キーワードによってヘッダーファイルパスの伝播を正確に制御できることです。
| キーワード | 現在のターゲットで利用可能 | このターゲットに依存する他のターゲットで利用可能 | 典型的なシナリオ |
|---|---|---|---|
PRIVATE |
である | 否 | 自身の実装にのみ必要な内部ヘッダーファイル |
PUBLIC |
である | である | ライブラリの公開 API ヘッダーファイル。自身と利用者の両方が必要 |
INTERFACE |
否 | である | ヘッダオンリーライブラリ |
例
# MyLib 自身は include/ が必要。MyLib を使用する MyApp も include/ が必要
# PUBLIC:自身と利用者の両方がそのパスにアクセスできる
target_include_directories(MyLib PUBLIC ${PROJECT_SOURCE_DIR}/include)
# PRIVATE:MyApp 自身のみがアクセスできる(内部ユーティリティ関数のヘッダーファイル)
target_include_directories(MyApp PRIVATE ${PROJECT_SOURCE_DIR}/src/internal)
# INTERFACE:MyLib はヘッダオンリーライブラリであり、自身はコンパイル不要。パスを利用者にのみ渡す
add_library(HeaderOnlyLib INTERFACE)
target_include_directories(HeaderOnlyLib INTERFACE ${PROJECT_SOURCE_DIR}/header_only)
ライブラリとパッケージの検索
多くのプロジェクトは外部ライブラリ(Boost、OpenCV など)に依存しており、CMake はfind_package()コマンドを提供して、これらの外部依存関係を自動的に検出・設定します。
find_package ディレクティブ
find_package()はシステムにインストールされたライブラリを検索し、対応する変数(ヘッダーファイルパス、ライブラリファイルパス、ライブラリターゲット名など)を設定します。
例
# REQUIRED は、見つからない場合に CMake 設定を中止することを意味する
find_package(Boost REQUIRED)
# 最低バージョンの指定:Boost バージョンが 1.70 以上であることを要求
find_package(Boost 1.70 REQUIRED)
# 検索パスの指定:カスタムパス配下で OpenCV を検索
find_package(OpenCV REQUIRED PATHS /path/to/opencv)
検索したライブラリを使用
find_package()成功すると、通常は以下の内容が提供され使用できます:
| 提供される内容 | 例 | 説明 |
|---|---|---|
| インポートターゲット | Boost::Boost |
現代の CMake で推奨される方法。target_link_libraries で直接使用 |
| ヘッダーファイルパス変数 | ${Boost_INCLUDE_DIRS} |
旧式の用法。include_directories と組み合わせて使用 |
| ライブラリファイルパス変数 | ${Boost_LIBRARY_DIRS} |
旧式の用法。link_directories と組み合わせて使用 |
強く推奨されるのはインポートターゲット方式(例:Boost::Boost)であり、ヘッダーファイルとライブラリパスを手動で組み立てるのではありません。インポートターゲットはすべてのコンパイルおよびリンクオプションを自動的に処理するため、設定エラーが減少します。
サードパーティライブラリの使用例
以下は、CMake プロジェクトで Boost ライブラリを導入して使用する方法を示す完全な例です。
例
cmake_minimum_required(VERSION 3.10)
project(MyBoostProject CXX)
# C++ 標準を設定
set(CMAKE_CXX_STANDARD 17)
# Boost ライブラリを検索。見つからない場合はエラーで終了
find_package(Boost REQUIRED)
# 実行可能ファイルを追加
add_executable(MyApp main.cpp)
# インポートターゲットを使用して Boost をリンク
# Boost::Boost がヘッダーファイルパスとライブラリファイルパスを自動的に設定する
target_link_libraries(MyApp PRIVATE Boost::Boost)
注意事項
target_xxx 系のコマンドを優先的に使用してください。include_directories() と link_directories() はグローバルコマンドであり、パス汚染を引き起こしやすいです。大規模プロジェクトでは、常に target_include_directories() と target_link_libraries() を使用すべきです。これらにより依存関係がより明確で保守しやすくなります。
常に C++ 標準を指定してください。CMAKE_CXX_STANDARD を設定しない場合、コンパイラはデフォルトの標準(通常は C++98 または C++14、コンパイラのバージョンによって異なります)を使用します。同時に CMAKE_CXX_STANDARD_REQUIRED を ON に設定することをお勧めします。これにより、コンパイラが指定された標準をサポートしていない場合に直接エラーになります。
PUBLIC / PRIVATE / INTERFACE の違いを理解してください。これらのキーワードの誤用は、CMake 初心者が最もよく陥る落とし穴です。簡単な覚え方:自身が必要かつ利用者も必要 → PUBLIC、自身のみが必要 → PRIVATE、利用者のみが必要(ヘッダオンリーライブラリなど)→ INTERFACE。
その他の拡張キャッシュ変数のデフォルト値は自動的に更新されません。CMakeLists.txt 内の set(... CACHE ...) のデフォルト値を変更しても、既存の CMakeCache.txt は上書きされません。build ディレクトリ内の CMakeCache.txt または build ディレクトリ全体を削除してから cmake を再実行する必要があります。