Help us understand the problem. What is going on with this article?

CMake : out-of-sourceビルドで幸せになる

@osamu0329 です。
この記事は CMake Advent Calendar 2014の記事です。

3.14から追加された コマンドラインオプションの説明を追記しました。

はじめに

cmake を実行する際に、cmake . としているサンプルをよく見かけますが、これはin-sourceビルドと呼ばれるやり方です。

CMake には、前述の in-source ビルドに加えて、out-of-source ビルド (
ビルドツリーとソースツリーを分ける方法) が利用できます。

この記事では、out-of-source ビルドの方法と、

in-sourceビルド (cmake .) の何が問題なのか?

まずは、in-source ビルドのどこが問題なのか、確認します。

  • git でソース管理する際に、ビルド成果物や cmakeコマンドが生成する各種ファイルによってリポジトリが汚れしまい git status した際に、本来追加すべきファイルを見落とす可能性がある
  • cmakeが生成したファイルをまとめて消したいときにディレクトリごとに CMakeCache.txtCMakeFiles ディレクトリを消すのが苦痛
  • Debugビルド、Releaseビルドを分けたい時に、ソースツリーを別々に用意する必要がある

out-of-source ビルドだと何がハッピーなのか?

まぁ、うえの裏返しです。

  • ツリーが汚れないので、まちがってビルド成果物(自動生成したファイルなど)をコミットしたりしない。ハッピー。
  • 成果物やCMakeのキャッシュを消したい場合に、ビルドツリーごとごっそり削除できる。ハッピー。
  • Debug ビルドや Releaseビルドを分けることができる。ハッピー。

out-of-sourceビルドの方法

out-of-sourceビルドの方法は簡単です。


(追記)

(ドキュメントを見る限りでは)CMake 3.13 から以下の形式で
ソースツリーとビルドツリーを指定できるようになりました。

$ cmake [<options>] -S <path-to-source> -B <path-to-build>

実機で確認したのは CMake 3.18 のみですが、以下の動作をします。

  • ビルドツリーが存在しない場合は自動的に作成する
  • ソースツリーを指定しない場合は、カレントディレクトリがソースツリーになる

そのため後述の手順は、以下のように記述できます。

$ ls CMakeLists.txt       # カレントディレクトリはソースツリー。(このコマンドは説明用なので実際には不要です)
CMakeLists.txt
$ cmake -B build          # ビルドツリーとしてbuildディレクトリを作成して
                          # プロジェクトファイル(Makefileとか)を生成
$ cmake --build build     # ビルドツリーの中でプロジェクトのビルド(`make` とか)を行う

git でクローンしてビルドするなら、以下のような形でできます。

$ git clone https://gitlab.com/cmake-example/foo
$ cmake -S foo -B foo_build
$ cmake --build foo_build

CMake 3.14 からcmakeコマンドのドキュメントで用途別の書式が整理されて分かり易くなっているため、
最近チェックしていない方は、再度参照してみると発見があるかもしれません。

CMake公式 cmake(1) の最新版ドキュメント
なお、バージョン差異で利用できない機能もあるため、利用しているCMakeバージョンをご確認ください。


今までのやり方。

  1. ビルドツリー用のディレクトリを作成する
  2. ビルドツリーのトップディレクトリにcd する
  3. cmake コマンドの引数にソースツリーのトップディレクトリ(CMakeLists.txt が配置されたディレクトリ)を指定する
  4. generatorに応じたコマンドを実行 (make とか nmake とか) だけです。
out-of-sourceのビルド方法
$ git clone hoge.git     # CMake プロジェクトをクローンする。
$ mkdir hoge-debug-build # ビルドツリーを作成する
$ cd hoge-noconfig       # ビルドツリーに移動
$ cmake ../hoge          # ソースツリーを指定してcmakeを実行する
$ make                   # 生成されたMakefileを実行する(実行すべきコマンドは指定したGeneratorによる)
note
ちなみに私個人は、ソースツリー内にbuildというディレクトリを作ってその配下にビルドツリーを作成しています(厳密にはout-of-sourceビルドではないらしい)。
buildディレクトリは .gitconfig で除外しています。

CMakeLists.txtの中で、ビルドツリー内のファイルを参照する方法

例えば、以下のユースケースで、ビルドツリー内のファイルを参照したいことがあります。

  • configure_file() コマンドで生成したファイルを install(FILES) したい
  • generate_export_header で生成したヘッダファイルを include_directory() するためにビルドツリーのディレクトリを指定したい

ビルドツリーのトップレベルディレクトリを参照するために、CMakeの変数 ${CMAKE_BINARY_DIR} を利用できますが、用途に応じて以下の変数が利用できます。

単一ディレクトリや単一のCMakeLists.txtからなるプロジェクトで、ほかのプロジェクトのサブプロジェクトとならない場合は、CMAKE_SOURCE_DIR と CMAKE_BINARY_DIR で充分だと思います。

ソースツリーとビルドツリーに関連する変数一覧

変数名 参照先のパス
CMAKE_SOURCE_DIR ソースツリーのトップディレクトリ
CMAKE_BINARY_DIR 上記に対応するビルドディレクトリ
CMAKE_CURRENT_SOURCE_DIR 現在処理中の CMakeLists.txt の配置ディレクトリ
CMAKE_CURRENT_BINARY_DIR 上記に対応するビルドディレクトリ
PROJECT_SOURCE_DIR 現在のプロジェクトのトップディレクトリ
PROJECT_BINARY_DIR 上記に対応するビルドディレクトリ
<name>_SOURCE_DIR プロジェクト name のトップディレクトリ
<name>_BINARY_DIR 上記に対応するビルドディレクトリ
CMAKE_CURRENT_LIST_DIR 現在処理中の cmake ファイルの配置ディレクトリ

CMAKE_BINARY_DIR

ビルドツリーのトップディレクトリ (cmakeコマンド実行時に指定したディレクトリに対応するディレクトリ)を参照します。

注意
プロジェクトが他のプロジェクトのサブプロジェクトの場合に、この変数を使用すると、親プロジェクトのビルドツリーのトップディレクトリを参照します。

CMAKE_CURRENT_BINARY_DIR

現在処理中のCMakeLists.txtが配置されたディレクトリに対応するビルドディレクトリを参照します。

PROJECT_BINARY_DIR

現在のプロジェクトのビルドツリーのトップディレクトリを参照します。

参照先のディレクトリは、簡単にいうと下記のようなロジックで決まります。
1. 現在のプロジェクトを見つける。PROJECT_BINARY_DIR を記述したディレクトリからルート方向にたどって 最初のproject()コマンドが記述されているCMakeLists.txtを見つける。
2. そのディレクトリに対応するビルドディレクトリが PROJECT_BINARY_DIR になる。

PROJECT_BINARY_DIR の例

以下のディレクトリ構成を持つ、CMakeプロジェクトを例にとります。
コメント部分は、そのディレクトリのCMakeLists.txtに記述されたコマンドです。

main_project/    # project(main), add_subdirectory(foo_project)
  foo_project/   # project(foo), add_subdirectory(foo_dir)
    foo_dir/     # ※ここから、foo_projectを参照したい

foo_dir に配置したCMakeLists.txtからfoo_projectに対応するビルドディレクトリを参照したいケースで PROJECT_BINARY_DIR を用います。CMAKE_BINARY_DIR を使用すると、 main_project のビルドディレクトリを参照します。

<name>_BINARY_DIR

project(<name>) が記載されたCMakeLists.txtに対応するビルドディレクトリを参照します。
他のプロジェクトから、別のプロジェクトを参照する際に使用します。

xxx_SOURCE_DIR

上記の xxx_BINARY_DIR に対応する xxx_SOURCE_DIR の変数が定義されています。こちらは、ビルドディレクトリではなくソースディレクトリを参照します。

CMAKE_CURRENT_LIST_DIR

out-of-sourceビルドとは関係ありませんが、類似の変数なので紹介します。

現在処理中の cmake ファイル(CMakeLists.txtとは限らない)が配置されたディレクトリを参照します。
一見、 CMAKE_CURRENT_SOURCE_DIR と同じようですが、 CMAKE_CURRENT_SOURCE_DIR は、現在処理中の CMakeLists.txt が配置されたディレクトリを参照します。

例えば、dirA の CMakeLists.txt から dirA/cmake/common.cmake をインクルードした場合、以下のようになります。コメントとして参照されるパスを示します。(本来は絶対パスですが、便宜上簡便化して記載しています)

dirA/CMakeLists.txt
message(STATUS ${CMAKE_SOURCE_DIR})       # dirA
message(STATUS ${CMAKE_CURRENT_LIST_DIR}) # dirA
include(dirA/cmake/common.cmake)
dirA/cmake/common.cmake
message(STATUS ${CMAKE_SOURCE_DIR})       # dirA
message(STATUS ${CMAKE_CURRENT_LIST_DIR}) # dirA/cmake

その他、関連する変数

CMAKE_INCLUDE_CURRENT_DIR

set(CMAKE_INCLUDE_CURRENT_DIR ON) とすると、${CMAKE_CURRENT_SOURCE_DIR}${CMAKE_CURRENT_BINARY_DIR} が自動的に include_directies() されます。デフォルト値はOFFです。

CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE

set(CMAKE_INCLUDE_CURRENT_DIR ON) とすると、${CMAKE_CURRENT_SOURCE_DIR}${CMAKE_CURRENT_BINARY_DIR} が自動的に INTERFACE_INCLUDE_DIRECTORIES プロパティに追加されます。デフォルト値はOFFです。

おわりに

out-of-source ビルドを行うと、CMakeLists.txt の記載が煩雑になる面もありますが、それ以上のメリットも多々あります。ソースの利用者がout-of-sourceビルドを選択できるように気をつかっていきたいですね。

明日は osamu0329 さんの『プロパティについて』です。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした