C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 3.CMake導入 ~

# 概要
これまで、でアジャイル開発に対するテスト駆動の必要性、CUnitによるテスト実行の自動化を行ってきました。
今回は、CMakeを導入することでビルドプロセスを自動化していきます。

## 記事の全体像
- [C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 1.概念 ~](https://qiita.com/denden14/items/a1435a225e386c32dab5)
- [C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 2.CUnit導入 ~](https://qiita.com/denden14/items/27d29735caef9b5639e0)
- [C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 3.CMake導入 ~](https://qiita.com/denden14/items/7b1729c7ff273c4165bd)
- [C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 4.Jenkins導入 ~](https://qiita.com/denden14/items/30cbe6d327a404aaa2a7)

## 環境
OS : Linux ubuntu 18.04.5 LTS
テストツール : CUnit 2.1-3

コンパイラ : gcc 7.5.0
ビルドツール：CMake 3.10.2 → 3.19.4([追記より](#追記))

# ビルドツール
## そもそもビルドツールとは
実機(PCであったり組み込みマイコンであったり)でプログラムを動かすためのこれまでの遍歴を書くと、
手動ビルド → ビルドスクリプト → ビルドツール という流れで進化してきています。

#### 手動ビルド
ビルドに必要な処理を全て手動で行う。
ソースファイルが1つしか無い場合は良いが、多数存在する場合に正しい手順で作業するのは至難の業となる。
時間がかかる上に、ミスも起こりうる可能性が高いので色々と問題。

#### ビルドスクリプト
ビルドに必要な処理をスクリプト化して実行する。
手動ビルドの課題を色々と改善はしたが、人間は欲深き存在。
スクリプトは基本的に上から順に全て実行していくので、最後の方の工程でエラーが起きると最初からやり直しになる。
また、大きいシステムになるとコンパイルそのものに時間がかかる。

#### ビルドツール
ビルドスクリプトの課題を解決してくれるもの。
ビルドタスク（解析、コンパイル、リリース用ビルド、テスト用ビルド等）を定義して実行します。

ビルドツール自身が、ファイルの更新やファイルの依存関係を監視しタスクの最適化を行ってくれたりします(ツールによる)。
つまり、
ファイルが更新されていなければコンパイルを行わずに以前行ったコンパイル結果を使用してくれたり、
デプロイ用のビルドタスクではテスト用のファイルは不要なので、そこのコンパイルを省いてくれたりするというわけです。
これを__インクリメンタルビルド__といいます

代表的なツール

- make (`makefile`に記述されたルールに沿って、ビルドを行う ※`makefile`を記述する)
- autotools ([アジャイル開発とテスト駆動 ~ 2.CUnit導入 ~](https://qiita.com/denden14/items/27d29735caef9b5639e0)の)
- CMake (`makefile`等のファイルを自動で生成してくれる ※ `CMakeLists.txt`というテキストファイルを記述する)
- Meson
- Bazel
など

## make vs CMake
どちらも記載内容は下記のようになりますが、これらが楽してかけたほうが良いですよね。

|記載内容|例|
|:--|:--|
|ターゲットの分離|・リリース用<br>・デバッグ用|
|コンパイルの自動化|・ソースコードの一覧<br>・コンパイルオプション<br>・インクルードパス|
|リンクの自動化|・ライブラリパスの指定<br>・リンカオプションの設定|

#### makeのデメリット
`makefile`は独特で暗黙のルールが多く、学習コストや保守面で大変なようです。また、コンパイラに依存します。
すなわち、開発環境が異なる場合`makefile`が使えないということです。

#### CMakeのメリット
一方`CMake`は、コンパイラに依存しないツールです(マルチプラットフォーム対応のビルドツール)。
`makefile`とは若干位置づけが異なります。
`CMake`はコンパイラに依存するビルド手順(`makefile`相当)を生成し、ビルドの実行はその生成した手順を元に他のツール(`make`相当)が行います。
この__相当__というのが肝で、他の環境では`makefile`ではないビルド手順ファイルが生成され、`make`でないツールがビルドを行います。
つまり、`CMake`は各種ビルドツールのUIを担うと言って良いかもしれませんね。
ちなみに上で上げた`meson`や`Bazel`もこれにあたります。

また、`CMake`は`makefile`と比べてわかりやすくかけます。

ですので、今回はCMakeを使用します。

# CMake導入
#### CMakeのインストール

```bash:Bash
sudo apt install cmake -y
```

#### CMakeLists.txtの作成
インストール出来たので、早速`CMakeLists.txt`ファイルを作成します。

```cmake:CMakeLists.txt
# CMakeのバージョンを設定
cmake_minimum_required(VERSION 3.10.2)
# プロジェクト名と使用する言語を設定
project(unitTest_cmake C)

# unitTest.outという実行ファイルをadd.cとunitTest.cから作成
add_executable(unitTest.out add.c unitTest.c)
# unitTest.out作成時に libcunit.aをリンク
target_link_libraries(unitTest.out cunit)
```

これまで作成したファイルを整理しておきます。
また、ビルド時に中間ファイルが色々と生成されるので、build用のフォルダを追加しています。

```bash:Bash
$ mkdir build
$ tree
.
├── CMakeLists.txt
├── add.c // テスト対象ソースコード
├── add.h
├── build // build用のフォルダ
└── unitTest.c // テストソースコード

```
<details><summary>これまでに作成してきたファイル</summary><div>

```c:add.c
#include "add.h"

// バグのあるadd関数(テスト対象)
int add(int x, int y) {
		return 0;
}
```

```c:add.h
// #ifndef ADD_H_
// #define ADD_H_

int add(int x, int y);

//  #endif /* ADD_ */
```

```c:unitTest.c
#include <CUnit/CUnit.h>
// #include <CUnit/Console.h>
#include <CUnit/Automated.h>

#include "add.h"

/**
 * addテストスイート
 **/
// テスト関数001
void test_add_001(void) {
	CU_ASSERT_EQUAL(add(1, 2), 3);
}

// テスト関数002
void test_add_002(void) {
	CU_ASSERT_EQUAL(add(-1, -2), -3);
}

// main関数
int main() {
CU_pSuite add_suite;

    CU_initialize_registry(); // 2.テスト構造の初期化
    add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
    CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
    CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加

	CU_automated_run_tests();
    // CU_console_run_tests(); // 5.適切なインターフェースを使用してテストを実行
    CU_cleanup_registry(); // 6.テスト構造のクリーン
}

```
</div></details>

#### ビルドと実行
`bulid`フォルダに入って`cmake`コマンドを実行します。
`build`フォルダ内に`makefile`が生成されたのが確認できるはずです。

```bash:Bash
$ cd build/
$ cmake ..
-- The C compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/projectDir/build
$ tree -L 1
.
├── CMakeCache.txt
├── CMakeFiles
├── Makefile // これ
└── cmake_install.cmake
```

ビルドします。 次のコマンドでCMakeがビルドツールを選んで実行してくれます。
目的ファイルである`unitTest.out`が作成されているので、
実行すると、レポートファイルが吐き出されていることが確認できるはずです。

```bash:Bash
$ cmake --build .
Scanning dependencies of target unitTest.out
[ 33%] Building C object CMakeFiles/unitTest.out.dir/add.c.o
[ 66%] Building C object CMakeFiles/unitTest.out.dir/unitTest.c.o
[100%] Linking C executable unitTest.out
[100%] Built target unitTest.out

$ tree -L 1
.
├── CMakeCache.txt
├── CMakeFiles
├── Makefile
├── cmake_install.cmake
└── unitTest.out // これ

$ ./unitTest.out 
$ tree -L 1
.
├── CMakeCache.txt
├── CMakeFiles
├── CUnitAutomated-Results.xml // これ
├── Makefile
├── cmake_install.cmake
└── unitTest.out
```
以上で、CMakeを使用して、ビルドプロセスを自動化することが出来ました。

# おまけ
## フォルダを分ける
フォルダを分けたりして、本番環境に少しだけ寄せておこうと思います。

```bash:Bash
$ tree
.
├── CMakeLists.txt
├── add             // フォルダを追加
│   ├── include     // フォルダを追加
│   │   └── add.h
│   └── src         // フォルダを追加
│       └── add.c
├── main.c          // ファイルを新規作成 (アプリケーション側のメイン処理入る)
└── test            // フォルダを追加 (テスト側のメイン処理が入る)
    └── unitTest.c
```

別に大したことはやっていません。気になる方は見てください。
<details><summary>各種ファイルの中身</summary><div>

`CMakeLists.txt`の最初の4行は同じです。
主に下記3点を変更しています。

1. `./add/include`をインクルードフォルダとして登録
1. 実際のアプリケーションの作成指示
1. フォルダが変更に伴うパスの変更

```txt:CMakeLists.txt
# CMakeのバージョンを設定
cmake_minimum_required(VERSION 3.10.2)
# プロジェクト名と使用する言語を設定
project(unitTest_cmake C)

# ./add/include をincludeフォルダに指定
include_directories(./add/include) // 1.ココ

# Main という実行ファイルを main.cと add/src/add.c から作成
add_executable(Main main.c add/src/add.c) // 2.ココ

# UnitTest という実行ファイルを test/unitTest.c と add/src/add.c から作成
add_executable(UnitTest test/unitTest.c add/src/add.c) // 3.ココ
# unitTest.out作成時に libcunit.aをリンク
target_link_libraries(UnitTest cunit)
```

`main.c` のみ今回新規に追加しました。
実行するアプリケーションを想定したファイルです。

```c:main.c
#include <stdio.h>

#include <add.h>

int main() {
	int a = 1;
	int b = 2;

	int c = add(a,b);

	printf("1 + 2 = %d\n", c);
}
```

その他の変更点ですが、
 `#include "add.h"` を
 `#include <add.h>` に変更しています。
これは`CMakeLists.txt`でインクルードディレクトリを指定したためです。

```c:add.c
#include <add.h>

// バグのあるadd関数(テスト対象)
int add(int x, int y) {
		return 0;
}
```


```c:add.h
// #ifndef ADD_H_
// #define ADD_H_

int add(int x, int y);

//  #endif /* ADD_ */
```

```c:unitTest.c
#include <CUnit/CUnit.h>
#include <CUnit/Automated.h>

#include <add.h>

/**
 * addテストスイート
 **/
// テスト関数001
void test_add_001(void) {
	CU_ASSERT_EQUAL(add(1, 2), 3);
}

// テスト関数002
void test_add_002(void) {
	CU_ASSERT_EQUAL(add(-1, -2), -3);
}

// main関数
int main() {
CU_pSuite add_suite;

    CU_initialize_registry(); // 2.テスト構造の初期化
    add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
    CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
    CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加

	CU_automated_run_tests(); // 5.適切なインターフェースを使用してテストを実行
    CU_cleanup_registry(); // 6.テスト構造のクリーン
}
```
</div></details>

### 再度ビルドと実行
再度ビルドを行います。
Mainと、UnitTestの2つの実行ファイルが作成されているのが確認できました。

```bash:Bash
$ cd build
$ cmake ..
$ cmake --build .
$ tree -L 1
.
├── CMakeCache.txt
├── CMakeFiles
├── Main
├── Makefile
├── UnitTest
└── cmake_install.cmake
```

それぞれ実行してみます。

```bash:Bash
$ ./Main 
1 + 2 = 0

$ ./UnitTest
$ tree -L 1
.
├── CMakeCache.txt
├── CMakeFiles
├── CUnitAutomated-Results.xml // ココ
├── Main
├── Makefile
├── UnitTest
└── cmake_install.cmake
```

ちゃんと出来てますね。
よかった。

# 参考資料
今回の記事の趣旨から若干ずれてしまいますが、今回の勉強をするのに役立った参考資料のリンクです。
特にCMakeの使い方に関しては、本記事の趣旨から外れるので省略しますので、参考資料を参照していただければと思います。

##### そもそもビルドって？？
[C/C++のビルドの仕組みとライブラリ](https://kamino.hatenablog.com/entry/c%2B%2B-principle-of-build-library)

##### make関連の全体像
[makeとconfigureと、もっとナウいやつ](https://qiita.com/turenar/items/922a845cfc9350b0eece#configure)
CMakeについて悪く書かれてますが、とりあえず自分で使ってみて判断しようかなと。

##### CMake関連の参考資料
- 使い方概要
    - [勝手に作るCMake入門 その1 基本的な使い方](https://kamino.hatenablog.com/entry/cmake_tutorial1) その4まであります。
    - [ごく簡単なcmakeの使い方](https://qiita.com/termoshtt/items/539541c180dfc40a1189#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%8C%E5%88%86%E3%81%8B%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E5%A0%B4%E5%90%88)
    - [中規模なC++の新しいプロジェクトを作るときにやるべきこと 2016年版](https://qiita.com/m_mizutani/items/e51e9f17c1a29dbf5669#cmakelisttxt-)
    - [CMakeの使い方（その１）](https://qiita.com/shohirose/items/45fb49c6b429e8b204ac) 

- 詳細
    - [CMake: キャッシュ変数と環境変数](https://qiita.com/mrk_21/items/186439952a6665184444)
    - [たのしい組み込みCMake](https://qiita.com/tnishinaga/items/c01dfd5f43e446653cd1)
    - [CMakeスクリプトを作成する際のガイドライン](https://qiita.com/shohirose/items/5b406f060cd5557814e9)

- サンプル(使い方のイメージが湧くと思います)
    - [CMakeLists.txtのサンプル](https://github.com/m-mizutani/cpp-template/blob/master/CMakeLists.txt)

##### その他参考資料
[ありきたりなCMakeのプロジェクト作成 for C++](https://qiita.com/yumetodo/items/bd8f556ab56298f19ba8#%E5%B0%91%E3%81%97%E3%81%A5%E3%81%A4%E8%A8%AD%E5%AE%9A%E3%82%92%E5%A4%89%E3%81%88%E3%81%A6%E5%90%8C%E3%81%98%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E3%83%93%E3%83%AB%E3%83%89%E3%81%97%E3%81%9F%E3%81%84%E3%81%A8%E3%81%8D)
[make (UNIX)](https://ja.wikipedia.org/wiki/Make_(UNIX))

# 追記

## カバレッジレポートを出力させる
### 最新版のCMakeをインストールする
CMakeのバージョンが古く途中で引っかかったので、最新版をインストールします。
すでにインストールしてしまった方、申し訳ないですが、アンインストールしてください。

```bash:Bash
sudo apt remove cmake -y
```

こちらの記事を参考に最新版をインストールします (執筆時の CMake -version = 3.19.4)
[Ubuntu 18.04 に Cmake の Latest Release をインストールする](https://qiita.com/comachi/items/d0c1ce5d7b90fe30fced)


### ./bootstrapでエラーが出た場合
私の環境では`OpenSSL`が見当たらないといったエラーが発生しました。

```bash:Bash
Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_CRYPTO_LIBRARY OPENSSL_INCLUDE_DIR)
```

`libssl-dev`をインストールして再度./bootstrapを実行します。

```bash:Bash
sudo apt-get install libssl-dev
```

### gcovによるカバレッジの計測
GCCに付属しているgcovというツールを使用し、ステートメントカバレッジ(C0)を計測します。

そのために、CMakeLists.txtを修正します。
先程 CMakeのバージョンを上げたのは、`target_link_options`が未対応のバージョンだったためです。

```txt:CMakeLists.txt
# CMakeのバージョンを設定
cmake_minimum_required(VERSION 3.10.2) // ココの記載はあまり気にする必要は無いらしい
# プロジェクト名と使用する言語を設定
project(unitTest_cmake C)

# ./add/include をincludeフォルダに指定
include_directories(./add/include)

# Main という実行ファイルを main.cと add/src/add.c から作成
add_executable(Main main.c add/src/add.c)

# UnitTest という実行ファイルを test/unitTest.c と add/src/add.c から作成
add_executable(UnitTest test/unitTest.c add/src/add.c)
# unitTest.out作成時に libcunit.aをリンク
target_link_libraries(UnitTest cunit)

# コンパイルとリンクオプションに -coverageを追加 // ココ
target_compile_options(UnitTest PUBLIC -coverage) // ココ
target_link_options(UnitTest PUBLIC -coverage) // ココ
```

./buildフォルダを一旦消し、再度CMakeを使用してビルドします。

```bash:Bash
rm -rf build
mkdir build
cd build
cmake ..
cmake --build .
```

すると、`CMakeFiles/UnitTest.dir`の `add/src`と`test`ディレクトリの中に `*.gcno`ファイルが出来ていると思います。

```bash:Bash
$ tree ./CMakeFiles/UnitTest.dir/
/
./CMakeFiles/UnitTest.dir/
├── C.includecache
├── DependInfo.cmake
├── add
│   └── src
│       ├── add.c.gcno
│       └── add.c.o
├── build.make
├── cmake_clean.cmake
├── depend.internal
├── depend.make
├── flags.make
├── link.txt
├── progress.make
└── test
    ├── unitTest.c.gcno
    └── unitTest.c.o

```

ここで、`./UnitTest`を実行すると、先程と同じディレクトリに`*.gcda`ファイルが出力されます。
このファイルを後ほど使用します。

```bash:Bash
$ ./UnitTest
$ tree ./CMakeFiles/UnitTest.dir/
./CMakeFiles/UnitTest.dir/
├── C.includecache
├── DependInfo.cmake
├── add
│   └── src
│       ├── add.c.gcda
│       ├── add.c.gcno
│       └── add.c.o
├── build.make
├── cmake_clean.cmake
├── depend.internal
├── depend.make
├── flags.make
├── link.txt
├── progress.make
└── test
    ├── unitTest.c.gcda
    ├── unitTest.c.gcno
    └── unitTest.c.o
```

### 出力結果の確認

出力結果を確認します。
gcovで簡易的な出力結果を確認後、`add.c.gcov`が吐き出されるので中身を確認しています。
`add.c.gcov`にはadd.cのファイルの内容が記載されており、各行の左側の数字が、テストで何回実行されたかが記載されています。

```bash:Bash
$ gcov CMakeFiles/UnitTest.dir/add/src/add.c.gcda
File '/home/yusuke/Desktop/cunitSample/add/src/add.c'
Lines executed:100.00% of 2
Creating 'add.c.gcov'

$ cat add.c.gcov 
        -:    0:Source:/home/yusuke/Desktop/cunitSample/add/src/add.c
        -:    0:Graph:CMakeFiles/UnitTest.dir/add/src/add.c.gcno
        -:    0:Data:CMakeFiles/UnitTest.dir/add/src/add.c.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:
        -:    2:#include <add.h>
        -:    3:
        -:    4:// バグのあるadd関数(テスト対象)
        2:    5:int add(int x, int y) {
        2:    6:		return 0;
        -:    7:}
        -:    8:

```

# おわりに
次回はいよいよ Jenkinsを用いて、テストの自動化を行います。
またJenkinsで今回までに出力してきた、テスト結果とテストカバレッジの結果も表示できるようにします。

もし、間違いや指摘事項等あれば、コメントしていただけると大変助かります。