Visual Studio向けに使うCMake

CMake

Visual Studioのバージョンを固定できず,複数のバージョンでビルドをしなければいけない場合があります。
slnファイルやprojファイルのXMLを直接編集したり,Visual StudioのUIからビルド設定を手作業で編集するのはとても面倒な作業ですが, そんなときに便利なのがCMakeです。

out-of-source Build

https://qiita.com/osamu0329/items/7de2b190df3cfb4ad0ca
CMakesを使うときは,out-of-source ビルドをすると,gitを使うときに便利です。

例えば,次のようなディレクトリを考えます。

MyProject/
 ├ include/
 ├ src/
 └ CmakeLists.txt

以下のように,単にcmakeを作業ディレクトリで動作させるだけだと,ソースファイルの隣にプロジェクトファイルや中間ファイルが生成されてしまいます。

C:\MyProject>cmake . -G "Visual Studio 15 2017 Win64"
MyProject/
 ├ include/
 ├ src/
 ├ CMakeFiles/
 ├ ALL_BUILD.vcxproj
 ├ ALL_BUILD.filters
 ├ ALL_BUILD.user
 ├ CMakeCache.txt
 ├ MyProjet.sln
 └ CmakeLists.txt

gitを利用する場合にファイルステータスに中間ファイルやらビルド生成物が列挙されて見通しが悪くなります。 また,誤ってソリューションファイルの設定を弄ってしまって,他の人の環境と生成物が異なってしまう,みたいなことも起きそうです。

そこで,次のコマンドのようにout-of-sourceビルドをすると便利です。

C:\MyProject>mkdir build
C:\MyProject>cd build
C:\MyProject\build>cmake ../ -G "Visual Studio 15 2017 Win64"
MyProject/
 ├ build/
 │ ├ CMakeFiles/
 │ ├ ALL_BUILD.vcxproj
 │ ├ ALL_BUILD.filters
 │ ├ ALL_BUILD.user
 │ ├ CMakeCache.txt
 │ └ MyProjet.sln
 ├ include/
 ├ src/
 └ CmakeLists.txt

ビルド中間ファイルがbuildディレクトリ以下にまとまりました。 あとは,gitignoreでbuildディレクトリ以下を無視すれば,ソースディレクトリやインクルードディレクトリを綺麗なまま使えます。
buildディレクトリ以下はCMakeLists.txtの設定によって自動生成されるので,環境によりません。 多人数で開発していて他の人がリポジトリをクローンしたら動かなかった、 みたいなこともbuildディレクトリを一旦削除してcmakeしなおすだけで再現できるので,皆がビルドできる環境を再現しやすいです。

フィルタの利用

http://blog.techlab-xe.net/archives/5340

Visual Studioには,フィルタ機能という機能があります。 これは,実際のディレクトリ構造と別に,Visual Studio内部で利用されるツリー構造を保持する機能です。 CMake上でこれを有効化するには,次のコマンドをCMakeLists.txtに記述します。

set_property(GLOBAL PROPERTY USE_FOLDERS ON)
source_group(${FilterName} FILES ${SrcFiles})

ただ,大抵の場合は実際のディレクトリ構造とフィルタ構造は一致させたい場合が殆どだと思います。
その場合は,TREE指定をすることで再帰的にフィルタを作成してくれます。

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

set(PrefixDir "\\src")
source_group(TREE ${SrcDir} PREFIX ${PrefixDir} FILES ${SrcFiles})

これで,SrcDir以下に存在するSrcFilesの構造をフィルタに登録します。PrefixDirはフィルタの先頭に付与される親フィルタで, 上の例では"\\src"を指定しています。このPrefixDirはVisual Studioの場合は"\\"で先頭をエスケープした区切り文字でルートから記述しないとうまく動かないようでした。

プリコンパイル済みヘッダ

プロジェクト全体で利用されるような共通のヘッダ定義を事前にコンパイルすることで,ビルド全体の高速化を図るのがプリコンパイル済ヘッダです。 Visual StudioでもGUI上でプリコンパイル済ヘッダを作成・指定ができるのですが,これをCMakeから自動で行うこともできます。

https://stackoverflow.com/questions/148570/using-pre-compiled-headers-with-cmake

set_source_files_propertiesを利用して,直接プリコンパイル済ヘッダ作成・利用のためのオプションを指定しています。 手作業でファイルを追加するときには,プリコンパイル済みヘッダの指定を忘れてしまうこともありますが,CMakesで自動で行えると便利です。

doxygenとの連携

doxygenソースコードからドキュメントを生成するツールですが,これをCMakesと連携させておくと, デプロイ時や普段のちょっとした開発時に手早くドキュメントを作成できて便利です。

project(doc)
# ${CMAKE_CURRENT_BINARY_DIR}から${include_dir}への相対パス
file(RELATIVE_PATH relative_include_dir ${CMAKE_CURRENT_BINARY_DIR} ${include_dir})
# doxygenの場所
set(DOXYGEN_PATH "C:\\Program Files\\doxygen\\bin\\doxygen.exe")
# doc.config内部で利用する変数のセット
set(DOXYGEN_INPUT "${relative_include_dir}")
set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
set(DOXYGEN_ENABLED_SECTIONS "LANG_JP")
# テンプレートから,doc.configを生成
set(DOXYGEN_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/doc.config")
configure_file (
        ${CMAKE_CURRENT_SOURCE_DIR}/../template/doc.config
        ${DOXYGEN_CONFIG_FILE}
    )
# doxygen起動コマンドの指定
add_custom_target(
    ${PROJECT_NAME} ALL
    SOURCES ${include_files}
    COMMAND cmd /c $<SHELL_PATH:${DOXYGEN_PATH}> $<SHELL_PATH:${DOXYGEN_CONFIG_FILE}>
)

ポイントはdoxygenのコンフィグファイルをconfigure_file()で作成する点です。 このテンプレート内部ではCMakeの変数参照ができるので,これを利用して作成するビルドプランごとに異なるドキュメントを作成できます。 マルチプラットフォームなプログラムを書く場合や,各言語のドキュメントを用意するときに便利だと思いました。