CMakeの特徴と基本的な使い方について解説

C/C++

CMakeは、クロスプラットフォームのビルドシステム生成ツールで、コンパイルに依存せずにプロジェクトのビルドを自動化することができます。

プラットフォームの多様性やプロジェクト管理のしやすさから非常に便利なため、C/C++のプロジェクトでは積極的に採用されています。

本記事では、CMakeの特徴や使い方を初心者でも分かるように解説していきます。

主な特徴

CMakeには、以下のような特徴があります。

  • クロスプラットフォームで動作
  • 様々なビルドシステムに対応
  • プロジェクト管理が容易
  • 高いカスタマイズ性と拡張性

クロスプラットフォームで動作

CMakeは、Windows、macOS、Linuxを含む多くの異なるOSで動作します。これにより、異なるOS間で容易に移植することが可能です。

様々なビルドシステムに対応

CMakeは、以下のような様々なビルドシステムに対応しています。

  • Makefile
  • Ninja
  • Visual Studio
  • Xcode

プロジェクト管理が容易

CMakeLists.txtファイルを通じて、ソースファイル、依存関係、ライブラリなどのプロジェクト設定を一元管理できます。

高いカスタマイズ性と拡張性

CMakeは非常に柔軟で、多様なビルド設定や条件分岐、プラットフォームごとの特殊な設定などが可能です。しかし、一方で柔軟な構文を学習するのに時間がかかる場合があります。

CMakeの使い方

CMakeの使い方について解説します。

インストール

以下のコマンドでインストールします。

[Debian/Ubuntuの場合]

$ sudo apt-get install cmake

[CentOSの場合]

$ sudo yum install cmake

「cmake –version」コマンドで以下のような出力が行われたらインストール成功です。

$ cmake --version
cmake version 3.22.1

C++ソースコードのビルド

サンプルのC++プロジェクトを用意し、CMakeでビルドしていきます。

※C++プロジェクトの詳細(ディレクトリ構成やファイルの中身など)は[(参考)サンプルC++プロジェクト]を参照してください。

①buildディレクトリの作成

まず、MyProjectディレクトリ配下に移動し、buildディレクトリを作成します。

~$ cd MyProject/
~/MyProject$ mkdir build

buildディレクトリを作成しなくてもbuildを行うことは可能ですが、作成することが一般的です。理由は以下の2点です。

  • ソースファイルとの分離:ビルドファイル(オブジェクトファイル、Makefile、実行可能ファイル等)をソースコードから物理的に分離することで、ソースファイルとビルドファイルが混在することを防ぐため。
  • 管理の容易さ:ビルドファイルをビルドディレクトリ内に限定することで、ビルド環境のリセット(クリーン)しやり直すことが容易になるため。(ビルドディレクトリを削除し、新たにビルドディレクトリを新たに作成するのみで行える。)

②cmakeコマンドを実行

buildディレクトリ内に移動し、cmakeコマンドを実行します。

~/MyProject$ cd build
~/MyProject/build$ cmake ..

cmakeコマンドの引数に指定している「..」はCMakeLists.txtが存在するディレクトリを指定しています。cmakeコマンドの実行が成功すると、buildディレクトリ配下にビルドに必要なファイル(Makefile等)が生成されます。

※Linux環境ではデフォルトでMakefileが生成されます。異なるビルドシステムを使用する場合は設定の変更が必要です。

③makeコマンドを実行

makeコマンドを実行し、ビルドを行います。

~/MyProject/build$ make

makeコマンド実行後、実行ファイル(MyProject)が生成されていることが分かります。

~/MyProject/build$ ls
CMakeCache.txt  CMakeFiles  Makefile  MyProject  cmake_install.cmake  mylib

④実行ファイルを実行

MyProjectを実行すると、適切にプログラムが実行されます。

~/MyProject/build$ ./MyProject
Running program.
Function in lib called.

CMakeLists.txtの関数

CMakeLists.txtでよく使用される関数と処理内容について解説します。

cmake_minimum_required

使用するCMakeの最小バージョンを指定します。実行環境で使用するCMakeが指定された最小バージョン未満であった場合、エラーが発生します。

cmake_minimum_required(VERSION 3.10)
project

プロジェクト名を設定します。プロジェクト名はビルド構成をカスタマイズする際に使用されたり、生成されたビルドシステム(MakefileやVisual Studio等)で使用されます。

project(MyProject)
set

変数を設定または変更します。具体的には、CMakeの組み込み変数の値の変更・設定に加え、新たに変数を定義することもできます。よく使用されるCMakeの組み込み変数の例を以下に示します。

  • CMAKE_CXX_STANDARD:使用するC++標準バージョンを指定します。
  • CMAKE_CXX_STANDARD_REQUIRED:指定されたC++標準がコンパイラによってサポートされていることが必要かどうかを指定します。
  • CMAKE_SOURCE_DIR:ルートのソースディレクトリを表します。
set(CMAKE_CXX_STANDARD 11)
add_executable

実行可能ファイルを生成するためのソースファイルを指定して、実行可能ファイルを生成します。

add_executable(MyProject src/main.cpp src/program.cpp)(※MyProjectは実行可能ファイル名)
add_library

ライブラリを生成するためのソースファイルを指定して、ライブラリを生成します。

add_library(mylib src/mylib.cpp)(※MyProjectはライブラリ名)
target_link_libraries

実行可能ファイルやライブラリに他のライブラリをリンクします。

target_link_libraries(MyProject mylib)(※MyProjectは実行可能ファイル名、もしくはライブラリ名)
include_directories

コンパイラのヘッダーファイル探索パスを指定します。

include_directories(include)
add_subdirectory

サブディレクトリをビルドプロセスに含めます。サブディレクトリは独自のCMakeLists.txtを持ち、add_subdirectoryが呼ばれたときにサブディレクトリでcmakeが実行されます。CMakeLists.txtがサブディレクトリに存在しない場合はエラーが発生します。

add_subdirectory(mylib)

まとめ

CMakeは専用の構文や使い方など覚えることは多いように感じますが、使いこなせれば非常に便利で開発効率が上がるように感じます。近年では、従来のMakefileからCMakeへの移行も進んでいるそうなので、C/C++を主に扱っているエンジニアの方や組み込みエンジニアの方は学んでおくべき内容であると思います。

参考文献

(参考)サンプルC++プロジェクト

サンプルC++プロジェクトのディレクトリ構成と各ソース・ヘッダファイル、CMakeLists.txtの内容を以下に示します。

※以下のC++プロジェクトは、私のGitHubリポジトリにもアップしています。
C++プロジェクトのディレクトリ構成
MyProject/
├── CMakeLists.txt
├── include/
│   └── program.h
├── src/
│   ├── main.cpp
│   └── program.cpp
└── mylib/
    ├── CMakeLists.txt
    ├── include/
    │   └── mylib.h
    └─── src/
        └── mylib.cpp
src/main.cpp
#include "program.h"
int main() {
    run();
    return 0;
}
src/program.cpp
#include <iostream>
#include "program.h"
#include "mylib.h"
void run() {
    std::cout << "Running program." << std::endl;
    lib_function();
}
include/program.h
#ifndef PROGRAM_H
#define PROGRAM_H
void run();
#endif // PROGRAM_H
mylib/src/mylib.cpp
#include <iostream>
#include "mylib.h"
void lib_function() {
    std::cout << "Function in lib called." << std::endl;
}
mylib/include/mylib.h
#ifndef LIB_H
#define LIB_H
void lib_function();
#endif // LIB_H
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyProject)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
include_directories(include)
include_directories(${CMAKE_SOURCE_DIR}/mylib/include)
add_subdirectory(mylib)
add_executable(MyProject src/main.cpp src/program.cpp)
target_link_libraries(MyProject mylib)
mylib/CMakeLists.txt
include_directories(include)
add_library(mylib src/mylib.cpp)
タイトルとURLをコピーしました