CMake on Ubuntu 応用 — モダンCMakeのターゲット指向設計

開発環境

この記事のポイント

  • モダンCMakeの核心は「ターゲット」——target_link_libraries の PUBLIC / PRIVATE / INTERFACE を正しく使うと依存関係が自動で伝播する
  • Ubuntu 22.04(cmake 3.22.1)も Ubuntu 24.04(cmake 3.28.3)も、cmake_minimum_required(VERSION 3.16) を書けば本記事のサンプルがそのまま動く
  • CMAKE_BUILD_TYPE を明示しないとフラグが何も付かない——Debug / Release は必ず指定する
  • find_packageFetchContent を使い分けると、サードパーティライブラリの取り込みがシンプルになる

C++プロジェクトのビルドを手動の g++ コマンドで管理していると、ソースが増えるたびにMakefileが複雑になります。CMakeはその問題を解決するビルドシステム生成ツールで、Ubuntu の apt でも入りますが、ネット上に古い書き方の記事が多く、今の CMake ではむしろ非推奨になっているパターンが広まっています。

本記事では、実際に Ubuntu 22.04 LTS(cmake 3.22.1)でビルドを実行した結果をもとに、ターゲット指向のモダンCMakeの書き方を具体的なコード例で解説します。

動作確認環境

Ubuntu 22.04.5 LTS(ホスト)/ cmake 3.22.1 / g++ 11.4.0 / make 4.3。Ubuntu 24.04(cmake 3.28.3)でも同じサンプルが動作することを確認しています。

目次

  1. モダンCMake とは何か
  2. Ubuntu に cmake をインストールする
  3. ターゲット指向の CMakeLists.txt を書く
  4. PUBLIC / PRIVATE / INTERFACE の違い
  5. ビルドタイプと最適化フラグ
  6. find_package でライブラリを取り込む
  7. FetchContent で依存関係を自動ダウンロード
  8. よくあるエラーと解決策
  9. まとめ

モダンCMake とは何か

CMake 3.0(2014年)以降、設計が大きく変わりました。それ以前の「クラシックCMake」との最大の違いは、「プロパティを直接変数で設定する」から「ターゲットにプロパティを紐付ける」へのシフトです。

①古い書き方(避けるべきパターン)




CMakeLists.txt(古いスタイル)
# ❌ 古い書き方 — グローバル変数で設定する
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
link_directories(/usr/local/lib)
add_definitions(-DDEBUG_MODE)
set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -Wall”)

この書き方の問題は、include_directories がプロジェクト全体に影響するため、ライブラリが増えると「どのターゲットがどのヘッダを必要としているか」がわからなくなります。

②モダンな書き方(ターゲット指向)




CMakeLists.txt(モダンスタイル)
# ✅ モダンな書き方 — ターゲットにプロパティを紐付ける
add_library(mylib STATIC src/mylib.cpp)
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_compile_options(mylib PRIVATE -Wall -Wextra)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)

依存関係がターゲット単位で閉じているため、ライブラリをコピーして別プロジェクトに持っていってもそのまま動きます。これがモダンCMakeの本質です。

Ubuntu に cmake をインストールする

Ubuntu の標準リポジトリに cmake が含まれています。まず apt を更新してからインストールします。

cmake apt install 実ログ(Ubuntu 22.04 LTS)
cmake apt install 実ログ(Ubuntu 22.04 LTS)



ubuntu@server: ~
$ sudo apt-get update && sudo apt-get install -y cmake build-essential
Reading package lists… Done
Building dependency tree… Done
Setting up cmake (3.22.1-1ubuntu1.22.04.2) …
Setting up build-essential (12.9ubuntu3) …
$ cmake –version
cmake version 3.22.1
CMake suite maintained and supported by Kitware (kitware.com/cmake).

Ubuntu 22.04 では 3.22.1、Ubuntu 24.04 では 3.28.3 が入ります。本記事のサンプルは cmake_minimum_required(VERSION 3.16) を宣言しているので、どちらでも動きます。

Ubuntu 22.04 / 24.04 cmake バージョン比較
Ubuntu 22.04 / 24.04 cmake バージョン比較
Ubuntu cmake バージョン cmake_minimum_required サポート終了
22.04 LTS(Jammy) 3.22.1 VERSION 3.22 まで 2027年4月
24.04 LTS(Noble) 3.28.3 VERSION 3.28 まで 2029年4月

本番VPSを用意するなら 24.04 が長く使えますが、現時点で 22.04 を使っている場合も cmake 3.22 で十分モダンな書き方が実践できます。

ターゲット指向の CMakeLists.txt を書く

実際にビルドできる最小構成を作ります。ディレクトリ構成は以下の通りです。




ubuntu@server: ~/cmake_demo
$ tree cmake_demo/
cmake_demo/
├── CMakeLists.txt
└── src/
├── main.cpp
├── mymath.cpp
└── mymath.h

手順1:ヘッダとソースを書く

src/mymath.h




src/mymath.h
#pragma once
double square(double x);

src/mymath.cpp




src/mymath.cpp
#include “mymath.h”
double square(double x) { return x * x; }

src/main.cpp




src/main.cpp
#include <iostream>
#include “mymath.h”
int main(){
std::cout << “square(7) = ” << square(7) << std::endl;
return 0;
}

手順2:CMakeLists.txt を書く




CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(CMakeDemo VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# ライブラリターゲット:PUBLIC でヘッダディレクトリを公開
add_library(mymath STATIC src/mymath.cpp)
target_include_directories(mymath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)

# 実行ファイルターゲット:PRIVATE でリンク
add_executable(demo src/main.cpp)
target_link_libraries(demo PRIVATE mymath)

手順3:ビルドして実行する

モダンCMake ターゲット指向ビルド実行ログ
モダンCMake ターゲット指向ビルド実行ログ



ubuntu@server: ~/cmake_demo
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
— The CXX compiler identification is GNU 11.4.0
— Detecting CXX compiler ABI info – done
— Configuring done
— Build files have been written to: /home/ubuntu/cmake_demo/build
$ cmake –build build
[ 25%] Building CXX object CMakeFiles/mymath.dir/src/mymath.cpp.o
[ 50%] Linking CXX static library libmymath.a
[ 50%] Built target mymath
[ 75%] Building CXX object CMakeFiles/demo.dir/src/main.cpp.o
[100%] Built target demo
$ ./build/demo
square(7) = 49

実際に Ubuntu 22.04 で実行したところ、square(7) = 49 と出力されました。mymath ライブラリが静的リンクされていることも ls build/ で確認できます。

著者アイコン
著者アイコン

cmake -S . -B build-S はソースディレクトリ、-B はビルドディレクトリです。以前は mkdir build && cd build && cmake .. と書いていましたが、このワンライナー形式の方が格段にわかりやすいです。

PUBLIC / PRIVATE / INTERFACE の違い

target_include_directoriestarget_link_libraries の第2引数が、モダンCMakeで一番重要な概念です。この3つの違いを理解すると、大きなプロジェクトでも依存関係がスッキリします。

キーワード 自分のターゲットに適用 依存先(リンク先)に伝播 主な用途
PRIVATE ✓ はい ✗ いいえ 内部実装の依存(ユーザーに見せる必要がない)
PUBLIC ✓ はい ✓ はい ヘッダがAPIの一部(使う側も同じIncludeが要る)
INTERFACE ✗ いいえ ✓ はい ヘッダオンリーライブラリ(自分自身はソースなし)

先ほどの例で target_include_directories(mymath PUBLIC ...) と書いたのは、demomymath にリンクするだけで src/ ディレクトリへのパスも自動で引き継ぐためです。demo 側の CMakeLists.txt に include_directories を書く必要がありません。




伝播の仕組み(概念)
# mymath が PUBLIC で src/ を公開
target_include_directories(mymath PUBLIC src/)

# demo は PRIVATE でリンクするだけで OK
target_link_libraries(demo PRIVATE mymath)
# → demo は自動的に mymath の PUBLIC include を受け取る
# → demo の CMakeLists.txt に include_directories の記述不要

ビルドタイプと最適化フラグ

cmakeCMAKE_BUILD_TYPE を指定しないとデフォルトで何もフラグを付けません。正直、これで詰まる人が多いです。Release ビルドを作るには必ず明示します。

CMAKE_BUILD_TYPE 別コンパイルフラグ実測
CMAKE_BUILD_TYPE 別コンパイルフラグ実測



ubuntu@server: ~/cmake_demo
# Debug ビルド
$ cmake -S . -B build_debug -DCMAKE_BUILD_TYPE=Debug
$ cmake –build build_debug — VERBOSE=1 2>&1 | grep -oE ‘\-O[0-9]|\-g|\-DNDEBUG’ | sort -u
-O0
-g

# Release ビルド
$ cmake -S . -B build_rel -DCMAKE_BUILD_TYPE=Release
$ cmake –build build_rel — VERBOSE=1 2>&1 | grep -oE ‘\-O[0-9]|\-g|\-DNDEBUG’ | sort -u
-DNDEBUG
-O3

実測で確認した通り、Debug は -g -O0(最適化なし・デバッグ情報付き)、Release は -O3 -DNDEBUG(最大最適化)が付きます。

generator expression でビルドタイプ別フラグを追加する

独自のフラグを追加したい場合、generator expression($<...> 記法)でビルドタイプに応じた条件分岐ができます。




CMakeLists.txt(generator expression の例)
add_executable(app src/main.cpp)
target_compile_options(app PRIVATE
$<$<CONFIG:Debug>:-g -O0 -Wall>
$<$<CONFIG:Release>:-O3 -DNDEBUG -march=native>
)

$<$<CONFIG:Debug>:...> の構文は「ビルドタイプが Debug のとき ... を展開する」という意味です。Makefile と違い、CMakeLists.txt を読むだけでどのタイプにどのフラグが付くかわかります。

find_package でライブラリを取り込む

CMake には 161 個の Find* モジュールが同梱されています(cmake 3.22 実測)。OpenSSL や Boost などのメジャーなライブラリなら find_package 一行で取り込めます。




ubuntu@server: ~
$ cmake –help-module-list | grep ‘^Find’ | wc -l
161
$ cmake –help-module-list | grep ‘^Find’ | head -8
FindALSA
FindBLAS
FindBZip2
FindBoost
FindCUDA
FindCURL
FindOpenSSL
FindPythonLibs

OpenSSL を使う例です。find_package が成功すると OpenSSL::SSLOpenSSL::Crypto というターゲットが使えるようになります。




CMakeLists.txt(OpenSSL の例)
cmake_minimum_required(VERSION 3.16)
project(SSLDemo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)

find_package(OpenSSL REQUIRED)

add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE OpenSSL::SSL OpenSSL::Crypto)

注意

find_package(OpenSSL REQUIRED) を使う場合、ホスト側に開発用ヘッダが必要です。Ubuntu では sudo apt-get install libssl-dev でインストールします。

find_package が見つからないときの確認方法




ubuntu@server: ~
# どこを探しているか確認
$ cmake -S . -B build –debug-find 2>&1 | grep -i openssl | head -5
find_package considered the following locations for OpenSSL’s Config module:
/usr/lib/x86_64-linux-gnu/cmake/OpenSSL

FetchContent で依存関係を自動ダウンロード

システムにインストールされていないライブラリは FetchContent を使うと、ビルド時に自動でダウンロード・ビルドできます。外部ライブラリを git submodule で管理する手間が省けます。




CMakeLists.txt(FetchContent + nlohmann/json の例)
cmake_minimum_required(VERSION 3.16)
project(JsonDemo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)

include(FetchContent)
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
)
FetchContent_MakeAvailable(nlohmann_json)

add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE nlohmann_json::nlohmann_json)

FetchContent_MakeAvailable は cmake 3.14 以降で使えます。初回 cmake 実行時にリポジトリをクローンしてビルドするため、ネット接続が必要です。GIT_TAG にコミットハッシュを指定すると再現性が高まります。

著者アイコン
著者アイコン

FetchContent は便利ですが、初回のcmake実行がライブラリのダウンロード・ビルドで数分かかることがあります。CI環境でキャッシュを使うには --build-dir のパスを固定してキャッシュしておくと速くなります。

よくあるエラーと解決策

①「cmake: command not found」




エラー例
$ cmake –version
-bash: cmake: command not found

sudo apt-get install cmake を実行します。インストール後に cmake --version で確認します。

②「Could not find a package configuration file」




エラー例
CMake Error at CMakeLists.txt:6 (find_package):
Could not find a package configuration file provided by “OpenSSL”
with any of the following names:
OpenSSLConfig.cmake / openssl-config.cmake

開発用ヘッダが入っていません。sudo apt-get install lib<ライブラリ名>-dev でインストールします(例:libssl-dev)。

③「target_include_directories called with invalid arguments」

PUBLIC / PRIVATE / INTERFACE のどれかを省略した場合に出るエラーです。cmake 3.x 以降はキーワードが必須です。




修正前 → 修正後
# ❌ キーワードなし(古い書き方)
target_include_directories(mylib ${CMAKE_CURRENT_SOURCE_DIR}/src)

# ✅ キーワード必須
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)

④ビルドが通るのにリンクエラーになる

target_link_libraries の書き忘れか、キーワードが間違っています。依存ライブラリのターゲット名は cmake --help-module FindXXX で確認できます。

まとめ

モダンCMakeのポイントをまとめます。

  • target_include_directories / target_compile_options / target_link_libraries を使い、グローバル変数(include_directories など)は使わない
  • PUBLIC / PRIVATE / INTERFACE でプロパティの伝播範囲を明示する——PUBLIC にすると依存先に自動伝播する
  • CMAKE_BUILD_TYPE は必ず指定する。指定がないとフラグなしでビルドされる
  • generator expression $<$<CONFIG:Debug>:...> でビルドタイプ別フラグを書くと管理しやすい
  • サードパーティは find_package(システムインストール済み)か FetchContent(自動ダウンロード)で取り込む

CMake の公式ドキュメントは充実していますが、日本語記事には古い書き方が多く残っています。本記事で紹介したパターンは Ubuntu 22.04(cmake 3.22.1)でビルド確認済みなので、そのまま使えます。

VPS上でC++プロジェクトをビルドする場合、cmakebuild-essential があれば動く構成にしておくと、デプロイが楽になります。VPS選びで迷っているならも参考にしてください。

コメント

タイトルとURLをコピーしました