CMakeの使い方（その１）

# はじめに

２月のはじめ頃、CMakeを使ってビルドを自動化しようと思い、色々ググってCMakeLists.txtを作り試行錯誤したのですが、その時は結局断念しました。というのも、abcというステップがあるとすると、ググッて得られる解説はabcのaやbが抜けていきなりcから解説されたものが多く、初心者にはきつかったからです。特に[cmake.orgのチュートリアル](https://cmake.org/cmake-tutorial/)はまさにその典型で、チュートリアルのStep 1からして長過ぎて、本当に何が必要なのかわかりにくい・・・orz

ところが、今学期受けているHigh Performance Computingという講義で行われたCMakeの使い方の解説がわかり易すぎて、あっという間に自作ライブラリをCMakeでコンパイルすることができるようになり、CMakeの便利さに感動してしまいました。私と同じくCMake初心者という方にこの感動を共有すべく、ここに使い方をまとめておきます。私自身はc++を使っているので、下記ではc++の場合として説明しています。OSはUbuntu 16.04LTS、コンパイラはg++ 5.4.0です。

ここでは自分でコマンドラインからビルドする場合とCMakeの場合を対比させて説明しています。こうすると、CMakeの各コマンドとの対応がわかり、理解しやすいとおもいました。

もしこうした方がいいよとか、こういう使い方もあるよという場合はコメントを頂ければ幸いです。

## 関連記事

[CMakeの使い方（その２）](https://qiita.com/shohirose/items/637f4b712893764a7ec1)
[CMakeの使い方（その３）](https://qiita.com/shohirose/items/d2b9c595a37b27ece607)

## 参考資料

本記事を書くにあたり[CMakeの公式ドキュメント](https://cmake.org/cmake/help/latest/index.html)を参照しました。
その他にはいろいろなYoutubeの動画を参考にしています。
また私自身は購入していませんが、[Professional CMake: A Practical Guide](https://crascit.com/professional-cmake/)という本はCMakeのco-maintainerの一人が書いた本だそうで、より高度な内容を勉強したい場合はこちらがよさそうです。

# ステップ１：実行ファイルを作成

## コマンドラインからビルド

まずg++コンパイラを使ったビルドの基本を確認します。[こちら](https://qiita.com/seriru13/items/c2f5192615162c4c3f47)がコンパクトにまとめられていてわかりやすいかなと思います。詳細はgcc.gnu.orgの[output options](https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options)や[option summary](https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html)を読んで勉強しましょう。

まず下記のような簡単なソースコードがあるとします。

```
.
├── main.cpp
├── hello.hpp
├── hello.cpp
└── hoge.py
```

```cpp:main.cpp
#include "hello.hpp"

int main() {
    hello();
}
```

```cpp:hello.hpp
#ifndef HELLO_H
#define HELLO_H

void hello();

#endif
```

```cpp:hello.cpp
#include <iostream>
#include "hello.hpp"

void hello() {
    std::cout << "Hello!" << std::endl;
}
```

まずこれを普通にコマンドを打ち込んでビルドします。

```terminal
$ g++ -c main.cpp hello.cpp   # ソースファイルをコンパイルしてオブジェクトファイル（main.o, hello.o）を生成
$ g++ -o a.out main.o hello.o # オブジェクトファイルをリンクしてa.outという実行ファイルを生成
```

するとディレクトリの中は次のようになっているはずです。

```
.
├── a.out
├── main.cpp
├── main.o
├── hello.hpp
├── hello.cpp
└── hello.o
```

この`a.out`を実行すれば`Hello!`と表示されます。

```terminal
$ ./a.out
Hello!
```

あるいは次のようにすると、オブジェクトファイルは生成されず、直接実行ファイルが作られます。

```terminal
$ g++ main.cpp hello.cpp
```

## CMakeを使ってみる

では先程の最も簡単な例をCMakeを使ってビルドしてみましょう。CMakeを使ったビルドの設定は`CMakeLists.txt`というテキストファイルに記述します。ディレクトリの中身は次のようになります。

```
.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
└── hello.cpp
```

`CMakeLists.txt`の内容は次のようになります。

```cmake:CMakeLists.txt
# CMakeのバージョンを設定
cmake_minimum_required(VERSION 3.13)
# プロジェクト名と使用する言語を設定
project(test_cmake CXX)
# a.outという実行ファイルをmain.cppとhello.cppから作成
add_executable(a.out main.cpp hello.cpp)
```

CMakeを使ってビルドするときは、次のように必ずソースディレクトリとは別にビルド専用のディレクトリを作成し、その中でビルドします。

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

最終的なディレクトリ構成は以下のようになっているはずです。

```
.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
└── build/
    ├── a.out
    └── ...
```

このようなCMakeLists.txtの設定の場合、実行ファイルが生成され、途中のオブジェクトファイルは（その他色々）のディレクトリの中に生成されます。

さて、ここで最終的に得られるディレクトリの中身を比べてみましょう。普通にコマンドラインからビルドした場合、ソースコードとオブジェクトファイルおよび実行ファイルが混在し、元の状態に戻すのが面倒です。ところがCMakeを使った場合、ビルド結果は全て`build`ディレクトリ内に保存されているため、`build`ディレクトリを削除するだけで容易に元の状態に戻すことができます。これはout-of-sourceビルドと呼ばれています。こちらの記事（[CMake : out-of-sourceビルドで幸せになる](https://qiita.com/osamu0329/items/7de2b190df3cfb4ad0ca#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB)）で詳しく解説されています。


# ステップ２：静的・共有ライブラリを作成

## コマンドラインからビルド

さて、ライブラリを作っている場合は、静的・共有ライブラリ（*.a, *.so）を作成しておき、必要に応じて実行ファイルにリンクすることになります。まずこの作業をコマンドを打ち込んでやってみましょう。

ステップ１の例に`good_morning.hpp`、`good_morning.cpp`というファイルが加わったと考えます。

```
.
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
└── good_morning.cpp
```

```cpp:good_morning.hpp
#ifndef GOOD_MORNING_H
#define GOOD_MORNING_H

void good_morning();

#endif
```

```cpp:good_morning.cpp
#include <iostream>
#include "good_morning.hpp"

void good_morning() {
    std::cout << "Good morning!" << std::endl;
}
```

`main.cpp`を次のように修正します。

```cpp:main
#include "hello.hpp"
#include "good_morning.hpp"

int main () {
    hello();
    good_morning();
}
```

### 静的ライブラリを作成

`hello.cpp`と`good_morning.cpp`をコンパイルしてオブジェクトファイルを生成したあと、静的ライブラリ`libgreetings.a`を作成し、`main.cpp`をコンパイルする際にリンクして実行ファイルを作成します。

```terminal
$ g++ -c hello.cpp good_morning.cpp             # オブジェクトファイル(hello.o, good_morning.o)の作成
$ ar rvs libgreetings.a hello.o good_morning.o  # 静的ライブラリ(libgreetings.a)の作成
$ g++ main.cpp libgreetings.a                   # main.cppをコンパイルしてlibgreetings.aとリンクし実行ファイルa.outを作成
```

最終的なディレクトリ構成は以下のようになります。

```
.
├── a.out
├── main.cpp
├── hello.hpp
├── hello.cpp
├── hello.o
├── good_morning.hpp
├── good_morning.cpp
├── good_morning.o
└── libgreetings.a
```

### 共有ライブラリを作成

`hello.cpp`と`good_morning.cpp`をコンパイルしてオブジェクトファイルを生成したあと、共有ライブラリ`libgreetings.so`を作成し、`main.cpp`をコンパイルする際にリンクして実行ファイルを作成します。

```terminal
$ g++ -fPIC -c hello.cpp good_morning.cpp                 # オブジェクトファイル(hello.o, good_morning.o)の作成
$ g++ -shared hello.o good_morning.o -o libgreetings.so   # 共有ライブラリ（libgreetings.so）の作成
$ g++ main.cpp -L. -lgreetings -Xlinker -rpath -Xlinker . # main.cppをコンパイルしてlibgreetings.soとリンクし実行ファイルa.outを作成
```

最終的なディレクトリ構成は以下のようになります。

```
.
├── a.out
├── main.cpp
├── hello.hpp
├── hello.cpp
├── hello.o
├── good_morning.hpp
├── good_morning.cpp
├── good_morning.o
└── libgreetings.so
```

どちらの`a.out`を実行しても

```terminal
$ ./a.out
Hello!
Good morning!
```

と表示されるはずです。

## CMakeを使ってみる

さて、同じことをCMakeを使ってやってみましょう。ステップ１と同じく、CMakeLists.txtを加えます。

```
.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
└── good_morning.cpp
```

`CMakeLists.txt`の内容は次のようになります。

### 静的ライブラリを作成

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(test_cmake CXX)
# hello.cppとgood_morning.cppをコンパイルして静的ライブラリlibgreetings.aを作成
add_library(greetings STATIC hello.cpp good_morning.cpp)
# a.outという実行ファイルをmain.cppから作成
add_executable(a.out main.cpp)
# a.outを作成する際にlibgreetings.aをリンク
target_link_libraries(a.out greetings)
```

ステップ１と同様に`build`ディレクトリを作りビルドすると、ディレクトリの構成は以下のようになるはずです。

```
.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
├── good_morning.cpp
└── build/
    ├── a.out
    ├── libgreetings.a
    └── ...
```

### 共有ライブラリを作成

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(test_cmake CXX)
# hello.cppとgood_morning.cppをコンパイルして共有ライブラリlibgreetings.soを作成
add_library(greetings SHARED hello.cpp good_morning.cpp)
add_executable(a.out main.cpp)
# a.outを作成する際にlibgreetings.soをリンク
target_link_libraries(a.out greetings)
```

`build`ディレクトリを作りビルドすると、ディレクトリの構成は以下のようになるはずです。

```
.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
├── good_morning.cpp
└── build/
    ├── a.out
    ├── libgreetings.so
    └── ...
```

CMakeを使ってビルドする場合、静的・共有ライブラリを作成するときの違いは __たったの一語（STATIC/SHARED）です__ 。

```cmake
add_library(greetings [SHARED|STATIC] hello.cpp good_morning.cpp)
```

またコマンドラインでビルドする場合は、特に共有ライブラリをリンクするときにリンクオプションが色々面倒なのですが、 __CMakeを使うと全部自動でやってくれます__ 。素晴らしい。

### 静的・共有ライブラリの指定について

前節では`STATIC`/`SHARED`を`add_library`に直接指定しましたが無指定にするとどうなるのでしょうか。

```cmake
add_library(greetings hello.cpp good_morning.cpp)
```

この場合、`add_library`で指定されたターゲット（ここではgreetings）が共有または静的のどちらでコンパイルされるかは、グローバルオプション`BUILD_SHARED_LIBS`のON/OFF（デフォルトはOFF）で決まります。CMakeを使ってビルドする際に次のように指定することが可能です。

```terminal
$ cmake -S . -B build -DBUILD_SHARED_LIBS=ON
$ cmake --build build
```

一般的には、各ライブラリごとに静的・共有ライブラリのどちらでビルドするか選択できるオプションを作成しておくべきです。

```cmake
# GREETINGS_BUILD_SHARED_LIBSというオプションを作成。デフォルトをOFFに設定。
option(GREETINGS_BUILD_SHARED_LIBS "build greetings as a shared library" OFF)

if (GREETINGS_BUILD_SHARED_LIBS)
  add_library(greetings SHARED hello.cpp good_morning.cpp)
else()
  add_library(greetings STATIC hello.cpp good_morning.cpp)
endif()
```

こうすると先ほど同様にコマンドラインから指定することが可能です。

```terminal
$ cmake -S . -B build -DGREETINGS_BUILD_SHARED_LIBS=ON
$ cmake --build build
```


# ステップ３：サブディレクトリにソースが分散している場合

さて、実際のライブラリにおいては、ステップ１・２のようにライブラリのルートディレクトリ直下にソースコードを一括でおいているなどということはありません。先ほどのような簡単なものなら別に構わないのですが、もっと大きなライブラリの場合はソースコードをその役割ごとに別々のディレクトリに置いておかないとわけがわからないことになります。

ステップ２の例のディレクトリ構成を下記のように見なおしたものを使って解説します。

```
.
├── include/
│   ├── hello.hpp
│   └── good_morning.hpp
├── src/
│   ├── hello.cpp
│   └── good_morning.cpp
└── test/
    └── main.cpp
```

## コマンドラインからコンパイル

さて、これをコマンドラインからビルドしてみましょう。ルートディレクトリ直下にいるものとし、共有ライブラリを作成して実行ファイルにリンクするやり方をとります。

```terminal
$ cd src
$ g++ -fPIC -c hello.cpp good_morning.cpp -I../include
$ g++ -shared *.o -o libgreetings.so
$ cd ../test
$ g++ main.cpp -I../include -L../src -lgreetings -Xlinker -rpath -Xlinker ../src
```

ステップ２と異なり、今度は`-L`オプションに加えて`-I`オプションでインクルードファイルを探すディレクトリを指定する必要があります。ステップ２で`-I`オプションが必要なかったのは、カレントディレクトリはデフォルトでインクルードファイルを探す場所として認識されるからです。

ディレクトリ構成は以下のようになります。

```
.
├── include/
│   ├── hello.hpp
│   └── good_morning.hpp
├── src/
│   ├── hello.cpp
│   ├── hello.o
│   ├── good_morning.cpp
│   ├── good_morning.o
│   └── libgreetings.so
└── test/
    ├── a.out
    └── main.cpp
```

## CMakeを使ってみる

さて、サブディレクトリが存在する場合、各ディレクトリごとにCMakeLists.txtを作ります。

```
.
├── CMakeLists.txt
├── include/
│   ├── hello.hpp
│   └── good_morning.hpp
├── src/
│   ├── CMakeLists.txt
│   ├── hello.cpp
│   └── good_morning.cpp
└── test/
    ├── CMakeLists.txt
    └── main.cpp
```

ルートディレクトリにある`CMakeLists.txt`ではサブディレクトリの登録をします。

```cmake:/CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(test_cmake CXX)
# サブディレクトリを登録
add_subdirectory(src)
add_subdirectory(test)
```

`/src/CMakeLists.txt`および`/test/CMakeLists.txt`ではそれぞれのディレクトリにあるファイルのコンパイル方法を指定してあげます。

```cmake:/src/CMakeLists.txt
add_library(greetings
  SHARED
    hello.cpp
    good_morning.cpp
  )
# greetingライブラリのインクルードディレクトリを教えてあげる
# PROJECT_SOURCE_DIRはこのプロジェクトのルートディレクトリの絶対パス
target_include_directories(greetings
  PUBLIC ${PROJECT_SOURCE_DIR}/include
  )
```

```cmake:/test/CMakeLists.txt
add_executable(a.out main.cpp)
# a.outをコンパイルする際にgreetingsをリンクする
target_link_libraries(a.out greetings)
```

`add_library`および`add_executable`の最初の変数を「ターゲット」と呼びます。
キーワード`PRIVATE`/`PUBLIC`/`INTERFACE`の意味は、ターゲット（`greetings`）のみに必要（=`PRIVATE`）なのか、ターゲット（`greetings`）およびそれに依存するターゲット（`a.out`）にも必要（=`PUBLIC`）なのか、あるいはターゲットには必要ないがそれに依存するターゲットには必要（=`INTERFACE`）なのかを指します。

|キーワード|ターゲットに必要|そのターゲットに依存するターゲットに必要|
|:-------|:-----------:|:------------------:|
|PRIVATE|:o:|:x:|
|PUBLIC|:o:|:o:|
|INTERFACE|:x:|:o:|

この場合、インクルードディレクトリ`${PROJECT_SOURCE_DIR}/include`は`greetings`のコンパイルだけでなく、`a.out`のコンパイルにも必要なため、`PUBLIC`を指定します。
`INTERFACE`はヘッダーのみのライブラリに対して使います。

さてこれで準備は完了です。今までと同様に`build`ディレクトリを作成してcmakeを実行してビルドすることができます。