@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.txt
やCMakeFiles
ディレクトリを消すのが苦痛 - 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バージョンをご確認ください。
今までのやり方。
- ビルドツリー用のディレクトリを作成する
- ビルドツリーのトップディレクトリに
cd
する -
cmake
コマンドの引数にソースツリーのトップディレクトリ(CMakeLists.txt
が配置されたディレクトリ)を指定する - generatorに応じたコマンドを実行 (
make
とかnmake
とか)
だけです。
$ git clone hoge.git # CMake プロジェクトをクローンする。
$ mkdir hoge-debug-build # ビルドツリーを作成する
$ cd hoge-noconfig # ビルドツリーに移動
$ cmake ../hoge # ソースツリーを指定してcmakeを実行する
$ make # 生成されたMakefileを実行する(実行すべきコマンドは指定したGeneratorによる)
ちなみに私個人は、ソースツリー内に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
現在のプロジェクトのビルドツリーのトップディレクトリを参照します。
参照先のディレクトリは、簡単にいうと下記のようなロジックで決まります。
- 現在のプロジェクトを見つける。
PROJECT_BINARY_DIR
を記述したディレクトリからルート方向にたどって 最初のproject()
コマンドが記述されているCMakeLists.txtを見つける。 - そのディレクトリに対応するビルドディレクトリが
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
をインクルードした場合、以下のようになります。コメントとして参照されるパスを示します。(本来は絶対パスですが、便宜上簡便化して記載しています)
message(STATUS ${CMAKE_SOURCE_DIR}) # dirA
message(STATUS ${CMAKE_CURRENT_LIST_DIR}) # dirA
include(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 さんの『プロパティについて』です。