かみのメモ

コンピュータビジョン・プログラムな話題中心の勉強メモ(記事一覧は https://kamino.hatenablog.com/archive へ)

勝手に作るCMake入門 その1 基本的な使い方

風呂敷を広げすぎてぐちゃぐちゃになったのでお蔵入りしていた記事なんですが、なんとなく納得できる形にまとまってきたので公開してみます! 文字数が50000字弱になったので4記事の連載という形にしてみます。


この連載は、ここ1年くらいでCMakeに入門した筆者が勉強した内容をまとめたものです。

初回のこの記事ではCMakeとは何なのかと、CMakeの基本的な使い方について書いていきます。

全体の目次

  1. 基本的な使い方【今ここ】
  2. プロジェクトの階層化
  3. プロジェクトの設定
  4. 外部ライブラリを利用する

この記事の目次

1. はじめに

1.1. この記事で書くこと

この記事は筆者の独断と偏見によって書かれたC++ユーザーのためのCMake入門記事です。

筆者はコンピュータビジョンを専門にしているのですが、ここ数年はWindows, Mac, Ubuntuを使い分けながら開発しています。 ホントはどれか1つにまとめたいんですが、Windowsにしか対応していないデバイスがあったり逆に*nix系でしか構築できない実行環境があったりするので、なかなか統合が進まないです…。

そんな中で一度書いたC++をなるべく別のOSでも使い回せるようにしたいと思い、去年あたりからCMakeを使い始めました。 最近はなんとなく勘所が分かってきて「CMakeまあまあ便利だな」と思えるようになってきました。 特にコンピュータビジョン系のライブラリのはCMakeを使って開発されていることが多いのですが、これらをスムーズにインストールできるようになったのは大きなメリットでした。

この記事ではそんな自分の知識の整理も兼ねてCMakeの基本的な使い方についてまとめていきたいと思います。

1.2. 必要な事前知識

この記事ではコマンドライン操作をある程度知っているという前提で解説していきます。 一応WindowsにはCMake GUIというツールがあるのですが、CMakeの挙動を理解するためにはCUI操作を覚える方が良いと思います。 とりあえずはターミナルの開き方と、cd, mkdir, ./, ../, 環境変数, PATH等のワードを知っていれば大丈夫です。

またC++のビルドやライブラリの仕組みを何となく理解していることも前提としています。 こちらは、標準ライブラリ, リンカ, 静的/動的ライブラリ等のワードを理解していれば読み進められると思います。 一応この辺りの話は以前の記事でも紹介しています。

kamino.hatenablog.com

2. CMakeについて

2.1. CMakeとは?

f:id:kamino-dev:20190604011955p:plain:w200
CMakeのロゴ ( from : https://cmake.org )

まずはCMakeって何?というところから。

CMakeはC, C++, CUDA, Fortran, assemblerなどのプロジェクトのビルドをコンパイラに依存せず自動化するためのツールです。

本来、プログラミング言語の仕様は標準ライブラリとコンパイラの実装に依存するので、開発環境が違えば異なるプログラムを書く必要があります。 しかし、これらの言語は古くから標準化が行われているおかげで、ソースコードはそのままに異なるコンパイラでビルドすることができます。

例えばC++にはGCC, MinGWGCC, Clang, MSVCなど複数のコンパイラが存在していますが、標準C++ライブラリだけを使ったソースコードであればどのコンパイラでもビルドすることができます(もちろんソースを書くときに若干の工夫は必要ですが)。

このようにソースコードは共通のものを使い回せるのですが、その一方でコンパイラのオプションつまりビルドに際しての設定項目の仕様やそれを格納する設定ファイルのフォーマットは統一されていません。

そのため、異なるコンパイラでビルドするためには、

といったように開発環境に合わせてプロジェクトファイルを使い分ける必要があります

これはなかなか面倒な話です。 実際にはコンパイラIDEのバージョンごとにプロジェクトファイルの仕様が変わるので、ほぼ無数のプロジェクトファイルを用意しなければならず、全て管理し続けるなんてことは不可能です。

そこで登場したのがCMakeです。

CMakeではCMakeLists.txtという設定ファイルを作成しておけば、そこからCMakeがサポートする任意のプロジェクトファイルを作成することができます。 つまりどの開発環境でも

  1. CMakeで*.sln*.xcodeprojMakefileなどのプロジェクトファイルを生成する
  2. 生成したプロジェクトファイルを使ってビルドする

という2ステップでプロジェクトをビルドできるようになります。

色々なコンパイラでビルドできるようになるということは、色々なOSの上でビルドできることに繋がります。 それゆえCMakeはクロスプラットフォームC++プロジェクトを開発するために欠かせないツールとなっています。

最近は類似のツールとしてBazel, Meson, Premakeなどが登場していますが、古くから使われていることと挙動が安定していることから未だCMakeが最もよく利用されている印象です。

2.2. CMakeのインストール

CMakeは大抵OSパッケージマネージャーを通してインストールできるようになっています。

  • macOS + brew : brew install cmake
  • Debian系OS : apt install cmake cmake-curses-gui
  • Red Hat系OS : yum install cmake

Windowsの場合は公式サイトのダウンロードページからインストーラーをダウンロードできるので、それを利用してください。 またコマンドラインからcmakeコマンドを利用するために、PATHに関するインストールオプションのところで「Add CMake to the system PATH for all users」にチェックを入れておいてください。

3. はじめてのCMakeプロジェクト

最初にCMakeを使った開発の流れを確認するため、簡単なhello_worldプロジェクトを作ってみましょう。

一応、プロジェクトの完成形をGitHubに上げておきますので必要に応じて参照してください(https://github.com/kamino410/blog_cmake_tutorial/tree/master/step1)。

3.1. main.cppとCMakeLists.txtを書く

まずは適当なプロジェクトディレクトリを作成し、その中にmain.cppCMakeLists.txtというファイルを作成します。

<プロジェクトディレクトリ>
|- main.cpp
|- CMakeLists.txt

main.cppの中にはhello worldのプログラムを書いておきます。

#include <iostream>

int main() {
  std::cout << "Hello, world!" << std::endl;
  return 0;
}

いつも通りですね。

次にCMakeLists.txtを編集します。 このファイルがCMakeプロジェクトの設定ファイルの役割を果たします。

CMakeLists.txtをはじめとするCMakeの各種設定ファイルは、独自の手続き型のスクリプトを使って記述します。 このスクリプトには変数や関数の概念があり、雰囲気はshellスクリプトに近いです。

とはいえ、いきなりCMakeスクリプトの話をしてもあまり意味がないので、まずはシンプルなCMakeLists.txtを書いてみましょう。

cmake_minimum_required(VERSION 3.1)
project(hello_world CXX)
add_executable(main_app main.cpp)

実は最後の行だけ書いておけば一応動くのですが、最低限やっておきたいという設定がこの3行です。

1行目 CMakeのバージョン指定

1行目のcmake_minimum_required(VERSION 3.1)CMakeのバージョンを指定するコマンドです。 minimum requiredというと単にCMakeのバージョンが一定以上であるかを確認するだけのように見えますが、実際にはバージョンの確認に加えて暗黙的にcmake_policyコマンドを呼び出し、このCMakeLists.txtをCMake 3.1の仕様で解析するように設定が行われます。 なおCMakeのバージョンがこの条件を満たさないときはエラー終了します。

2019年5月現在CMakeの最新版は3.14.4ですが、今回のhello_worldプロジェクトでは特に新しい機能は使わないので3.1を指定しています。 CMakeでは後方互換性が担保されているので指定するバージョンにそこまで神経質になる必要はありません。 とりあえず最新のバージョンを指定しておいて問題が出たら調整する、くらいのノリでも問題ないと思います。

ちなみに、検索しているとたまにcmake_minimum_required(VERSION 2.4 FATAL_ERROR)のようにFATAL_ERRORのオプションが付いているのを見かけるかもしれませんが、これはCMake 2.4以前では明示的に指定しない限りバージョン異常をエラー扱いしなかったためです。 現在はFATAL_ERRORを書く必要はありません。

2行目 プロジェクトの設定

2行目のproject(hello_world CXX)プロジェクト名とプロジェクトで使用するプログラム言語を指定しています。 「このファイルはhello_worldというC++プロジェクトの設定ファイルです」と宣言しているわけです。 ここで定義したプロジェクト名は、後々Visual Studioのソリューションファイル名や、Xcodeのプロジェクトファイル名に使われます。

3行目 実行ファイルの作成

3行目のadd_executable(main_app main.cpp)はビルドする実行ファイル名とそれを構成するソースファイルを指定しています。 つまり「main.cppを使ってmain_appという名前の実行ファイルを作成する」という宣言です。

CMakeでは、ビルドの対象になるものつまり実行ファイルやライブラリをターゲットと呼びます。 CMakeの記事を読んでいるときに「ターゲット」と書いてあったら、add_executableadd_libraryで宣言したやつのことだな、と思ってください。

ちなみに複数のソースファイルを指定するときは以下のようにファイルを後ろに追加していけばOKです。

add_executable(main_app main.cpp my_class1.cpp my_class2.cpp)


最小限の設定は以上です。 他の設定項目には全てデフォルト値が使われます。

3.2. ConfigureとGenerate

さっそく作成したhello_worldプロジェクトを使ってみましょう。

CMakeではビルドの前にConfigure, Generateという2つの作業を行います。

Configureとは、先ほど書いたCMakeLists.txtをCMakeに実行させてビルドに必要な情報を収集する作業のことです。 もしCMakeLists.txtにバグがあったり、依存パッケージへのパスなどビルドに必要な情報を見つけられなかった場合はここでエラーが出ます。

Generateは、Configureで集めた情報を基に自分の開発環境に合わせたプロジェクトファイルを生成する作業です。 こちらは作成したいプロジェクトファイルの種類を指定して実行するだけですべて自動でやってくれます。

作業ディレクトリの作成

ConfigureとGenerateを実行すると、いくつかの中間ファイルと目的のプロジェクトファイルが生成されます。 これらのファイルがもとのファイル(main.cppやCMakeLists.txt)と混ざってしまうと面倒なので、まずは作業用のディレクトを作成しましょう。 プロジェクトディレクトリの中にbuildというディレクトリを作成し、その中に移動します。

mkdir build
cd build

※ ただしEclipseを使用する場合、プロジェクトの子ディレクトリにGenerateするとEclipseがうまく動かなくなるため、プロジェクトディレクトリと同階層に作業用ディレクトリを作成しなければなりません。

現在のディレクトリ構造はこんな感じになっているはずです。

<プロジェクトディレクトリ>
|- main.cpp
|- CMakeLists.txt
|- build/

Configurate & Generate

作成したbuildディレクトリ内でConfigureとGenerateを行います。 コマンドは以下の通りです。

cmake ..

..の部分はCMakeLists.txtがあるディレクトリを指定しています。 今はbuildディレクトリの中なので、相対パス../を指定しているわけですね。

このコマンドではGeneratorの指定(どの種類のプロジェクトファイルを生成するのかの指定)を省略しているので、各OSにおけるデフォルトのGeneratorが選択されています。 Windowsでは、使用しているCMakeがサポートしており、かつPCにインストールされている最も新しいバージョンのVisual Studioがデフォルトとして選択されているはずです。 *nix系のOSの場合はUnix Makefilesがデフォルトとして選択されます。

Generatorを明示的に指定したい場合はcmake .. -G"Visual Studio 16 2019"cmake .. -GXcodeのようにオプションを追加しましょう。 Generatorの一覧はcmake --helpで確認できます。 なお、CMakeのオプションは指定子の後のスペースを省略できるので-G Xcode-GXcodeはどちらも正しく動きます。


コマンド実行後、以下のように「Configuring done」「Generating done」の文字が表示されていれば成功です。 buildディレクトリの中にMakefile, *.sln, *.xcodeprojなどのプロジェクトファイルとその他の必要なファイルが生成されているはずです。

-- The CXX compiler identification is AppleClang 10.0.1.10010046
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/kamino/Documents/cpp/cmake_test/build

3.3. ビルドする

あとは作成されたプロジェクトファイルを使ってビルドするだけです。

せっかくここまでCMake任せでやってきたので、ビルドもCMake任せでやってしまいましょう。 次のコマンドを実行すれば、CMakeが適当なビルドツールを選んでビルドを実行させてくれます。

cmake --build .

.の部分はConfigureとGenerateを実行したフォルダつまりbuildフォルダを指定しています。

以下はmacOS+Makefileで実行したときのログですが、裏でmakeコマンドを起動しているのでmakeコマンド形式のログが表示されています。 Visual StudioXcodeを使っていれば全く違うビルドログが表示されているはずです。

Scanning dependencies of target main_app
[ 50%] Building CXX object CMakeFiles/main_app.dir/main.cpp.o
[100%] Linking CXX executable main_app
[100%] Built target main_app

ビルドに成功すると、buildフォルダの中に実行ファイルmain_appが作成されます。 Visual Studioを使っているなら./build/x64/Debug/main_app.exeXCodeなら./build/Debug/main_appあたりに作成されていると思います。

作成されたmain_appを実行すればちゃんとHello, world!が表示されるはずです。

./main_app    # 実行結果 : Hello, world!

3.4. IDEデバッグする

CMakeでVisual StudioXcodeなどのIDE向けにGenerateしたプロジェクトはIDEで開いて編集・デバッグすることもできます。 buildディレクトリの中に作成された*.sln*.xcodeprojIDEで開いてみましょう。

CMakeで生成されるプロジェクトファイルには、宣言したターゲット(今回はmain_appのみ)の他にALL_BUILD, ZERO_CHECKというビルド対象が追加されます。

ALL_BUILDはその名の通りで、これをビルド対象にすると先ほどのcmake --build .と同様に全ての実行ファイルやライブラリがビルドされます。

ZERO_CHECKは、Generateの後でCMake関連のファイルを編集したときにその変更をプロジェクトファイルに反映させるために使われます。 Generate後にCMakeLists.txtなどを編集した場合は、ZERO_CHECKを対象にビルドを実行し変更を反映させる必要があります。

いつものようにデバッグしたい場合はビルド対象をmain_appに変更しましょうVisual Studioの場合は「ソリューションエクスプローラー > main_appプロジェクトを右クリック > スタートアッププロジェクトに設定」を選択すればよいです。 Xcodeの場合は左上の「ALL_BUILD > My Mac」を「main_app > My Mac」に変えればよいです。

f:id:kamino-dev:20190604010448p:plain:w400
Visual Studioでmain_appをデバッグ実行した様子

f:id:kamino-dev:20190604010247p:plain:w400
Xcodeでmain_appをデバッグ実行した様子

ちなみに最近はVisual Studio自体がCMakeプロジェクトの開発に対応し始めているので、そちらの機能を使ってみるのも手です。 その場合はConfigure, GenerateもIDEが自動でやってくれます。

3.5. おかしいな?と思ったら

CMakeLists.txtを編集しながら何度かConfigureしていると、スクリプトは正しいはずなのにエラーが出ることがあります。 これは後の章で説明するようにCMakeがConfigureの結果をキャッシュしているのが原因です。 こうしたときは一度CMakeCache.txtもしくはbuildディレクトリの中身をまるっと削除してもう一度試してみてください。


以上、CMakeの基本的な使い方を紹介しました。

次の記事はプロジェクトの階層化の話です⇒勝手に作るCMake入門 その2 プロジェクトの階層化


もしよければ↓の☆を1クリックお願いします!