かみのメモ

コンピュータビジョン・プログラムな話題中心の勉強メモ

勝手に作る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の基本的な使い方についてまとめていきたいと思います。

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, MinGW GCC, Clang, MSVCなど複数のコンパイラが存在していますが、標準ライブラリだけを使ったコードであれば、どのコンパイラでもビルドすることができます(もちろんコードを書くときに若干の工夫は必要ですが)。

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

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

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

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

  1. CMakeで*.sln, *.xcodeproj, Makefileなどのプロジェクトファイルを生成する
  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の仕様で解析するように設定が行われます。 なお、条件が満たされていないときはエラー終了します。

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するとIDEがうまく動かなくなるため、プロジェクトディレクトリと同階層に作業用ディレクトリを作成しなければなりません。

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

<プロジェクトディレクトリ>
|- 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/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が自動でやってくれます。

【2020/07/15 追記】
また、最近はVisual Studio CodeがしっかりとしたCMakeプラグインを用意してくれているので、それを使うのも手です。 ちゃんと補完やアノテーションが働きますし、デバッグ実行もできます。 Windows, macOS, Linuxでの使用感も統一されているので、マルチプラットフォーム開発が目的ならvscode+CMakeは十分アリな選択肢だと思います。

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

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


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

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

「他の人が作ったCMakeライブラリをインストールして使いたいだけだよ」という方には、4記事目の後半が参考になると思います⇒勝手に作るCMake入門 その4 外部ライブラリを利用する


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