Android NDK で ccache を有効にする

# はじめに
NDK のビルド時 ccache を導入してビルド時間を短縮しましょう。
ccache は簡単に説明すると、コンパイル時に生成されるオブジェクトをキャッシュ化するソフトウェアです。詳しい説明は本家と wikipedia を参照してください。

- ccache
 - http://ccache.samba.org/
- wikipedia
 - http://ja.wikipedia.org/wiki/Ccache

# 設定
## ccache のインストール
### Ubuntu 

```bash

% sudo apt-get install ccache
```

### MacOS(homebrew)

```bash

% brew install ccache
```

## ccache のキャッシサイズの最大値の指定
サイズは大きいほうが良いのでキャッシュ総容量を 20GB に指定します。

```bash
% ccache -M 20G
```

## 環境変数設定
お使いのシェルのドットファイル( `~/.bashrc` or `~/.zshrc` )に環境変数を設定してください。

```bash
# ccache
export USE_CCACHE=1
export NDK_CCACHE=/usr/local/bin/ccache # Ubuntu なら /usr/bin/ccache
export CCACHE_CPP2=yes
export CCACHE_COMPILERCHECK=content
```

設定を反映します。

```
% source ~/.bashrc (or ~/.zshrc)
```

# 手順
環境変数 `USE_CCACHE` と `NDK_CCACHE` が設定されていると、ndk-build が ccache を利用するようになります。
いつも通り ndk-build コマンドを実行するとキャッシュされます。

```bash

% cd /path/to/android_project/jni
% ndk-build
```

# 無効化
`USE_CCACHE` と `NDK_CCACHE` を unset すると ccache を無効化出来ます。

```
% unset USE_CCACHE
% unset NDK_CCACHE
% ndk-build
```

再度有効化するには source コマンドでドットファイルを再読込するか、USE_CCACHE, NDK_CCACHE 環境変数を再設定してください。


# キャッシュのクリア
ビルドが失敗するようになったり、キャッシュのヒット率がわるくなったら ccache のキャッシュをクリアしてください。

```
ccache -cC
```

# Android NDK CMake での ccache の有効方法

ここ数年でビルドシステムが `ant` から `gradle` へ、 IDE が Eclipse から Android Studio へ、そして NDK ビルドも `ndk-build` から `CMake` へ移行しました。
CMake ビルドでは `NDK_CCACHE` の環境変数設定だけでは ccache が有効になりません。

試行錯誤の上、NDK CMake ビルドでの ccache を有効にできたので、設定方法について記載します。
(もっと効率の良い設定方法があればご教示くださいませ :bow: )

## CMake
NDK の `CMakeList.txt` へ ccache の設定を入れ込みます。

```CMake:CMakeLists.txt

option(ENABLE_CCACHE "Enable ccache?" OFF)

# ccache
if (NOT $ENV{NDK_CCACHE} EQUAL "")
  set(CCACHE_EXE $ENV{NDK_CCACHE})
  if(ENABLE_CCACHE)
    if(CMAKE_C_COMPILER_LAUNCHER)
      set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_C_COMPILER_LAUNCHER}" "${CCACHE_EXE}")
    else()
      set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_EXE}")
    endif()
    if(CMAKE_CXX_COMPILER_LAUNCHER)
      set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_CXX_COMPILER_LAUNCHER}" "${CCACHE_EXE}")
    else()
      set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_EXE}")
    endif()
  endif()
endif()
```

`NDK_CCACHE` 環境変数の定義があれば `CMAKE_C_COMPILER_LAUNCHER` と `CMAKE_CXX_COMPILER_LAUNCHER` へ `NDK_CCACHE` の値(ccache のフルパス)を設定します。
( `ndk-build` と ccache の設定を共存するために行っています。ふつーであれば `find_program(CCACHE_EXE ccache)` で ccache コマンドが存在する場合に設定したほうがより汎用的です)

`NDK_CCACHE` 環境変数宣言に影響せずに ccache の有効無効を切り替えられるように `ENABLE_CCACHE` オプションを追加します。

## app/build.gradle

```gradle:app/build.gradle
def ndkCcache = System.getenv("NDK_CCACHE")
if (ndkCcache == null) {
    Properties properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())
    ndkCcache = properties.getProperty('ccache.path')
    environment("NDK_CCACHE", ndkCcache)
}

android {
    ...
    buildTypes {
        release {
            ...
        }

        debug {
            ...
            externalNativeBuild {
                cmake {
                    arguments '-DENABLE_CCACHE=ON'
                }
            }
        }
    }
}
```

最初の処理は `NDK_CCACHE` 環境変数が未定義の場合に `local.properties` の `ccache.path` の値を取得し、 `NDK_CCACHE` 環境変数を定義する処理です。(Android Studio で gradle を実行すると ~/.bashrc や ~/.zshrc の環境変数を取得できないようなのでこのような処理を入れています。)

`local.properties` のサンプルは下記のとおりです。

```:local.properties
...
ccache.path=/usr/local/bin/ccache
```

Debug ビルドのときのみ ccache が有効になるように `android.buildTypes.debug.externalNativeBuild.cmake.arguments` で `-DENABLE_CCACHE=ON` を設定しています。 Release ビルドでも ccache を有効にする場合は `android.buildTypes.release.externalNativeBuild.cmake.arguments` へ `-DENABLE_CCACHE=ON` を設定してくださいね。

# build
さて、これで設定が完了しました。早速ビルドし ccache が有効になっているか確認してください。

```bash
./gradlew clean assembleDebug
./gradlew clean assembleDebug # キャッシュ有効かの確認
ccache -s
```

二回目の `./gradlew` が速かったり、 `ccache -s` でキャッシュが hit した統計が取れてれば ccache 有効になっています。

おめでとうございます。 :tada: