CMakeとIDEを連携させる

# はじめに

私はCMakeを使って研究用の自作ライブラリをビルドしているのですが、普段IDE（Visual Studio, Xcode, Code::Blocksなど）を使っている学生が使いやすいようにCMakeをIDEと連携させる方法を調べて試したので、ここにまとめておきます。

# CMakeにIDEのプロジェクトファイルを生成させる方法

CMakeにはbuild system generatorを指定するオプション`-G`があります。

```terminal
$ cmake -G <generator> -S . -B build
```

[CMake Documentation >> cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html)
[CMake Documentation >> cmake-generators(7)](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#manual:cmake-generators(7))


## Code::Blocks

プロジェクトのルートディレクトリ下で以下のコマンドを打ち込めば`<project>.cbp`が生成されます。

```terminal
$ cmake -G "CodeBlocks - Unix Makefiles" -S . -B build
```

`Unix Makefiles`の部分は、他のgeneratorに差し替え可能です。
[CMake Documentation >> cmake-generators(7) >> CodeBlocks](https://cmake.org/cmake/help/latest/generator/CodeBlocks.html)

## Sublime Text 2

Sublime Text 2の場合は

```terminal
$ cmake .. -G "Sublime Text 2 - Unix Makefiles" -S . -B build
```

Code::Blocksと同様に、`Unix Makefiles`の部分は、他のgeneratorに差し替え可能です。
[CMake Documentation >> cmake-generators(7) >> Sublime Text 2](https://cmake.org/cmake/help/latest/generator/Sublime%20Text%202.html)

## Xcode

XcodeもCode::Blocksと同様にコマンドラインからプロジェクトファイルを作成できます。

```terminal
$ cmake -G Xcode -S . -B build
```

## Visual Studio

Visual Studioの場合はVisual Studio上で目的のプロジェクトフォルダを開けばIDE上で操作できます。

- [CMake projects in Visual C++](https://docs.microsoft.com/en-us/cpp/ide/cmake-tools-for-visual-cpp?view=vs-2017)

CMakeを使ってVisual Studioのプロジェクトファイルを生成することもできます。

```terminal
$ cmake -G "Visual Studio 17 2022" -S . -B build
```

[Documentation >> cmake-generators(7) >> Visual Studio 17 2022](https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2017%202022.html)

# はまったところ

## ヘッダーファイルがプロジェクトツリーに表示されない

はじめにCode::Blocksのプロジェクトファイルを生成してみたところ、ソースファイルはプロジェクトのツリーに表示されるのに、ヘッダーファイルは表示されませんでした。（ビルドはできましたが。）
調べると、ヘッダーファイルをCMakeのターゲットのソースとして加えないとIDEのプロジェクトツリーに正しく表示されないということがわかりました。

- [Listing header files in Visual Studio C++ project generated by cmake](https://stackoverflow.com/questions/1167154/listing-header-files-in-visual-studio-c-project-generated-by-cmake)

## `file(GLOB)`を使わずにヘッダー/ソースファイルをターゲットに追加する

つい最近知った[CMakeスクリプトを作成する際のガイドライン](https://qiita.com/shohirose/items/5b406f060cd5557814e9)に、IDEを使う場合は`file(GLOB)`を使うべきではないというものがあったので、この機会に`file(GLOB)`を使わないようにスクリプトを書きなおそうとしました。
書き直す前のプロジェクトはこんな感じ：

```terminal
/---
  |---CMakeLists.txt
  |
  |---include/
  |     |---dir1/
  |     |---dir2/
  |    ...
  |
  |---src/
        |---CMakeLists.txt
        |---dir1/
        |---dir2/
       ...
```

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(myproj CXX)
# ...
add_subdirectory(src)
# ...
```

```cmake:src/CMakeLists.txt
file(GLOB_RECURSE SRC_FILES "*.cpp")
add_library(mylib ${SRC_FILES})
target_include_directories(mylib
  PUBLIC ${PROJECT_SOURCE_DIR}/include
)
# ... 以下色々設定
```

### CMake 3.12まで

ぱっと調べて出てきた方法は、ソースファイルとヘッダーファイルごとに変数を作成してファイルを追加していく方法なのですが、[変数を使い過ぎない](https://qiita.com/shohirose/items/5b406f060cd5557814e9#%E5%A4%89%E6%95%B0%E3%82%92%E4%BD%BF%E3%81%84%E9%81%8E%E3%81%8E%E3%81%AA%E3%81%84)というガイドラインがあるので、この方法はよろしくありません。代わりに`target_sources`を使うという方法を見つけました。

- [Enhanced source file handling with target_sources()](https://crascit.com/2016/01/31/enhanced-source-file-handling-with-target_sources/)

`target_sources`と`add_subdirectory`を組み合わせて使えば、親ディレクトリで定義したターゲットに対して、子ディレクトリ以下のファイルを再帰的に後から追加していくことが可能となります。
これらのコマンドを使って以下のようにスクリプトを書き換えました。

```terminal
/---
  |---CMakeLists.txt
  |
  |---include/
  |     |---CMakeLists.txt
  |     |---dir1/
  |     |---dir2/
  |    ...
  |
  |---src/
        |---CMakeLists.txt
        |---dir1/
        |     |---CMakeLists.txt
        |    ...
        |
        |---dir2/
       ...
```

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(myproj CXX)
# ...
add_library(mylib)
add_subdirectory(include)
add_subdirectory(src)
target_include_directories(mylib PUBLIC include)
# ...
```

```cmake:src/CMakeLists.txt
add_subdirectory(dir1)
add_subdirectory(dir2)
# ... 同様に子ディレクトリを追加
```

```cmake:src/dir1/CMakeLists.txt
target_sources(mylib
  PRIVATE
    file1.cpp
    file2.cpp
    # ... 同様にソースファイルを追加
)
```

`include`ディレクトリ以下でも`src`ディレクトリと同様に子ディレクトリとヘッダーファイルを追加しています。

ところがビルドすると`target_sources`を使って追加したファイルが見つからない_というエラーが出てビルドできませんでした。なぜだろうと思ってドキュメントをよく読むと、

> Specify sources to use when compiling a given target. __Relative source file paths are interpreted as being relative to the current source directory__ (i.e. CMAKE_CURRENT_SOURCE_DIR).
>
> [target_sources: CMake Documentation](https://cmake.org/cmake/help/latest/command/target_sources.html)

ん？相対パスを渡すと現在のサブディレクトリに対する相対パスとして解釈される？怪しげな文言が…。でもサブディレクトリで加えているから問題ないはずじゃね？と思ったら、先ほど示したブログで

> In the target_sources() command above, each of the sources is prefixed by the directory of the CMakeLists.txt file. This is necessary because __the list of source files that are added to a target are interpreted by CMake as being relative to the directory in which the target is defined (i.e. where the add_library() call is made)__.
>
> [Enhanced source file handling with target_sources() by Craig Scott](https://crascit.com/2016/01/31/enhanced-source-file-handling-with-target_sources/)

相対パスって__ターゲットを定義したディレクトリに対する相対パス__かよ！`target_sources`を行っているディレクトリじゃないのねorz
というわけで、`CMAKE_CURRENT_SOURCE_DIR`を使って絶対パスで指定したら、今度はちゃんとビルドできました。つまり以下のようにしないとダメ：

```cmake:src/dir1/CMakeLists.txt
target_sources(mylib
  PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/file1.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/file2.cpp
    # ... 同様にソースファイルを追加
)
```

### CMake 3.13〜

CMake3.13からはポリシー[CMP0076](https://cmake.org/cmake/help/v3.13/policy/CMP0076.html#policy:CMP0076)を使って自動的に絶対パスへ変更することができるようになりました。すなわち、

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(myproj CXX)
# CMP0076を有効化
cmake_policy(SET CMP0076 NEW)
# ...
```

としておけば、

```cmake:src/dir1/CMakeLists.txt
target_sources(mylib
  PRIVATE
    file1.cpp
    file2.cpp
    # ... 同様にソースファイルを追加
)
```

として問題なくビルドできます。