include-what-you-useとjenkinsでC/C++プロジェクトから不要な#includeを洗い出す

# はじめに

include-what-you-useはC/C++のコードから不要な#includeを洗い出し、前方宣言への置き換えや削除を提案してくれるツールです。コードから不要な#includeが減るとコンパイル時間が短くなり、コードが読みやすくリファクタリングしやすくなります[^1]。不要な#includeを減らすにはまず洗い出しが必要です。この作業はinclude-what-you-useを使うと簡単に済ませることができます。ここではinclude-what-you-useの導入方法とinclude-what-you-useの結果をJenkinsで集計する方法を紹介します。

[^1]: [Why Include What You Use?](https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/WhyIWYU.md)



# インストール
- Windows/Macの人は[include-what-you-use公式HPのdownload](https://include-what-you-use.org/downloads/)からビルド済バイナリをダウンロードしてください。
- Ubuntuの人は[apt-getでインストールできます](https://www.howtoinstall.co/en/ubuntu/trusty/iwyu)

# ひとつのコードをinclude-what-you-useしてみる

まずはひとつのコードに対してinclude-what-you-useをしてみます。

```bash
$ include-what-you-use test.cpp
```

追加のインクルードパスが必要であれば -I オプションを利用てください。

```bash
$ include-what-you-use test.cpp -IhogeLibInclude -IfugaLibInclude
```

結果はこのようになります。

```bash
test.cpp should add these lines:
class Piyo;

test.cpp should remove these lines:
- #include "Fuga.h"  // lines 2-2
- #include "Piyo.h"  // lines 3-3

The full include-list for test.cpp:
#include "Hoge.h"  // for Hoge
class Piyo;
---
```

この例では#include "Piyo.h"は前方宣言に置き換え、#include "Fuga.h"は削除できることがinclude-what-you-wantにより指摘されています。

# プロジェクトにinclude-what-you-useを実行する

## プロジェクトがCMakeを利用している場合

いくつかやり方があります。

### 1. CMAKE_C(XX)_INCLUDE_WHAT_YOU_USEを利用する

cmake3.3以降で使える方法です。通常のコンパイルと並行してinclude-what-you-useします。cmakeするときにCMAKE_C(XX)_INCLUDE_WHAT_YOU_USE変数にinclude-what-you-useへのパスをセットします。

```
cmake . -DCMAKE_C_INCLUDE_WHAT_YOU_USE=path/to/include_what_you_use -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=path/to/include_what_you_use
```

### 2. CMAKE_C(XX)_COMPILERを利用する

コンパイラをinclude-what-you-useに置き換える方法です。cmakeするときにCMAKE_C(XX)_COMPILER変数にinclude-what-you-useへのパスをセットします。このときcmakeによるコンパイラの簡易チェックをパスするために、CMAKE_C(XX)_COMPILER_WORKSを1にセットしなければいけません。

```
cmake . -DCMAKE_C_COMPILER=path/to/include_what_you_use -DCMAKE_CXX_COMPILER=path/to/include_what_you_use -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER_WORKS=1
```

## CMake以外のビルドシステムを使っている場合

基本的にはコンパイラをinclude-what-you-useに置き換えればよいはずです。(やったことないので正直わかりません)

# Jenkinsで集計する

include-what-you-useの結果はJenkins Warning Pluginで集計することができます。

## Jenkins Warning Pluginにinclude-what-you-use用のパーサーを追加する

"Jenkinsの管理"->"システムの設定" を開き、"コンパイラの警告"にinclude-what-you-use用の設定をしていきます。"名前"、"リンク名"、"推移レポート名"にはなんでも良いのですがとりあえずinclude-what-you-useを入力してください。"正規表現"には次の内容を入力してください。

```
(.*) should (.*) these lines:$|^The full include-list for (.*)|(.*) // lines (.*)-(.*)$|(.+)
```

"マッピングスクリプト"には次のスクリプトを入力してください。

```java
import hudson.plugins.warnings.parser.Warning
import hudson.plugins.analysis.util.model.Priority

public class Line{
    def public static String filename;
    def public static String category;
}

if (matcher.group(2) == "add" || matcher.group(2) == "remove"){
    Line.filename = matcher.group(1);
    Line.category = matcher.group(2);
    return;
}

if (matcher.group(3) != null){
    Line.filename = null;
    Line.category = null;
    return;
}

if(Line.category == "remove"){
    return new Warning(Line.filename, matcher.group(6).toInteger(), "iwyu", Line.category, Line.category + " " + matcher.group(4), Priority.HIGH)
}
else if(Line.category == "add"){
    return new Warning(Line.filename, 0, "iwyu", Line.category, Line.category + " " + matcher.group(7).replaceAll("<|>", ""), Priority.LOW)
}

return;
```

## include-what-you-use用のジョブを作成する

* Jenkinsにジョブを新規作成(フリースタイル・プロジェクトのビルド)し、ジョブ内でinclude-what-you-useで全ビルドかけるようなシェルスクリプト・バッチファイルを用意します。
* ジョブの"設定"->"ビルド後の処理"に"コンパイラの警告の集計(scan for compiler warnings)"を追加します。
* "コンパイラの警告の集計"の"パーサー"にinclude-what-you-useを指定します。
* "コンパイラの警告の集計"の"常に実行"にチェックを入れます。

# 結果の扱い方

基本的にはinclude-what-you-useの提案通りにコードを修正していけばよいのですが例外があります。include-what-you-useは「他のヘッダファイルで宣言されたシンボル(型や関数、マクロなど)を利用するときは、それらが宣言されているヘッダファイルを直接#includeするべきだ」というポリシーを持っています。このポリシーと相容れないコードについては、include-what-you-useの提案を無視しなければいけません。例えばクライアントコードにおける#includeの数を減らすため、もしくはモジュール外に提供するシンボルを限定するために複数の#includeをまとめているヘッダファイルなどはinclude-what-you-useのポリシーに反するものです。(まとめヘッダファイルの#includeを削除し、まとめヘッダファイルが#includeしていた実際にシンボルが宣言されているヘッダファイルを#includeするように提案されてしまいます。)