Kotlin/Native ProjectをBitriseのiOSプラットフォームでビルドする

## 2019/2/19追記
[こちらの記事](https://qiita.com/Keeeeen/items/95f3bff3741a4a8ecd96)により簡潔な方法を書きました！

## この記事について
　この記事では、Kotlin/Nativeのチュートリアル等は解説しません。チュートリアルにおすすめの記事は

- [公式 iOS Tutorial](https://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html)
- [Kotlin/Nativeチュートリアル Android, iOS編 - AABrain](https://aakira.app/blog/2018/10/kotlin-native/)

辺りがおすすめです
　この記事では、[Kotlin/Native](https://kotlinlang.org/docs/reference/native-overview.html) ProjectをBitriseのiOSプラットフォームでビルドする方法について述べます。正確には`gradle`で`.framework`を生成する方法を記述しようと思います。記事の内容は公式のtutorialに沿って記述します
　Kotlin/Nativeは非常に新しいプロジェクトで、日々変わりつつあります。盛り上げて行きましょう！

## CIの全体的なステップの流れ
　細かい事は省いて大まかに組むと以下の様になります。この記事では2と3について書いていきます
1. Clone
2. `./gradlew packForXcode`を実行するのに必要な依存ツールのインストール
3. `.framework`生成（`./gradlew packForXcode`実行）
4. 証明書とか
5. アーカイブ、テスト等

　注意しなければならないのは、iOSをビルドする前に一度`./gradlew packForXcode`を実行する必要があるということです。これは、`SharedCode/build.gradle`の`packForXCode`タスクとXcodeの`Build Phases`で追加したスクリプトを照らし合わせるとわかるのですが、

```groovy
task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")
    final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'

    inputs.property "mode", mode
    dependsOn kotlin.targets.iOS.compilations.main.linkTaskName("FRAMEWORK", mode)

    from { kotlin.targets.iOS.compilations.main.getBinary("FRAMEWORK", mode).parentFile }
    into frameworkDir

    doLast {
        new File(frameworkDir, 'gradlew').with {
            text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
            setExecutable(true)
        }
    }
}

tasks.build.dependsOn packForXCode
```
`packForXCode`の`final File frameworkDir = new File(buildDir, "xcode-frameworks")`で、`SharedCode/build/code-frameworks`を作成し、このフォルダに`.framework`が置かれますが、Xcodeの`Build Phases`には

```bash
cd "$SRCROOT/../../SharedCode/build/xcode-frameworks"
./gradlew :SharedCode:packForXCode -PXCODE_CONFIGURATION=${CONFIGURATION}
```
と書く様になっています。`build/`はgitignore対象だと思いますので、clone段階では`build/xcode-frameworks`ディレクトリは生成されておらず、cdしようとしても`No such a file or dictionary`と怒られてしまうのです。

　さらに、`build/xcode-frameworks`以下に`gradlew`ファイルも生成されていませんので、先に`./gradlew packForXcode`を実行する必要があります。最初に実行すれば、キャッシュが効くので`Build Phases`ではタスクを実行するのに時間はかかりません。

## .frameworkを生成するのに必要なもの
### [Android SDK](https://developer.android.com/studio/?hl=ja)
　Androidをビルドするのに必要。ローカルではAndroid Studioをインストールする時に入ってきます。CI上ではSDKのみCLIでインストールします。Android SDKはSettingsにある`Project Type Selector`を`Android`にしないと使えないそうです。[参考]
(https://discuss.bitrise.io/t/install-missing-android-tools-failed/3630)
　つまり、`Project Type Selector`をAndroidにしたらiOSがビルドできなくなるので、自分でAndroid SDKを入れる必要があるということです

### Java
　Andorid SDKを使う時に必要になります。バージョンはプロジェクトの設定によります。自分のではJava8を使っているのでJava8をインストールします

## Homebrew-caskを使ってインストール
　幸いなことに、どちらもhomebrew-caskでインストールすることができます。brewは`Project Type Selector`がiOSであれば使うことが出来ます
`Script` Stepを追加して、installします

```bash
brew cask install android-sdk java8
```
これだけでは、`./gradlew packForXCode`実行時に怒られてしまいます

```
FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':app'.
> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s
Command PhaseScriptExecution failed with a nonzero exit code
```
`local.properties`に`sdk.dir`を設定するか、`ANDROID_HOME`を設定してくれと言われていますね。`local.properties`はgitignore推奨ですので、`ANDROID_HOME`を設定します。

　ローカルでは、`ANDROID_HOME`を設定しなくても動いたと思うのですが、これはよしなに`local.properties`が生成されて`sdk.dir`を設定されているからです。ここをコメントアウト等すると上記のエラーが出ると思います。
　ローカルでは`bash_profile`にexportするなり、`local.properties`に記述するでもどちらでもいいと思います。しかし、`local.properties`を使わない場合は、`Build Phases`で使うShellが環境変数`ANDROID_HOME`を設定していないShellだとエラーが出てしまいます

　ということで、CI上では環境変数`ANDROID_HOME`を設定したいと思います。ただ、デフォルトのXcodeの`Build Phases`のスクリプトのShellは`/bin/sh`で、Bitriseは`/usr/bin/env bash`になっており、ただexportするだけでは解決しません
　そこで今回は、`.bash_profile`を生成してそこに`ANDROID_HOME`を設定して、`Build Phases`でそれを読み込みたいと思います。先程の`brew cask`の下に書きます。ついでに、`JAVA_HOME`を設定してJava8を使うようにしておきましょう

```bash
touch ~/.bash_profile
echo 'export ANDROID_HOME=/usr/local/share/android-sdk' >> ~/.bash_profile
echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)' >> ~/.bash_profile
source ~/.bash_profile
```
`Build Phases`のスクリプトに`source ~/.bash_profile`を追加します

```bash
cd "$SRCROOT/../../SharedCode/build/xcode-frameworks"
source ~/.bash_profile # ここ追加
./gradlew :SharedCode:packForXCode -PXCODE_CONFIGURATION=${CONFIGURATION}
```

　良さそうです。やっとだ〜と思い実行するとまたまたエラーが…

```
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':app'.
> Failed to install the following Android SDK packages as some licences have not been accepted.
     platforms;android-28 Android SDK Platform 28
     build-tools;27.0.3 Android SDK Build-Tools 27.0.3
  To build this project, accept the SDK license agreements and install the missing components using the Android Studio SDK Manager.
  Alternatively, to transfer the license agreements from one workstation to another, see http://d.android.com/r/studio-ui/export-licenses.html
  
  Using Android SDK: /usr/local/share/android-sdk
```

　ライセンスに同意していないよ。とのことなので同意します。

```bash
yes | sdkmanager --licenses
```
　これは全ライセンスに同意していますが、`sdkmanager "platforms;android-28"`だけでも通るかもしれません

これで`./gradlew packForXcode`が`success`し、無事ビルドできると思います :tada:

## Homebrew-cask以外の選択肢
　brew cask以外にも、`wget`等で引っ張ってきて、解凍してシンボリックリンクを張る等などがあると思います

```bash
wget https://dl.google.com/android/repository/sdk-tools-darwin-4333796.zip
unzip ...
```

## 完成したスクリプト
```bash
#!/usr/bin/env bash
# fail if any commands fails
set -e
# debug log
set -x

brew cask install android-sdk java8

export JAVA_HOME=`/usr/libexec/java_home -v "1.8"`
export ANDROID_HOME=/usr/local/share/android-sdk

PATH=${JAVA_HOME}/bin:${PATH}
PATH=${ANDROID_HOME}/bin:${PATH}

yes | sdkmanager --licenses

cd $BITRISE_SOURCE_DIR
./gradlew packForXCode
```

## 最後に
本来ならば、gradleのiOSのビルドにAndroid sdkは要らない気がするのですが、ビルド時にAndroidのタスクも走ってるため何かしらが絡んでしまっているのだと思います。gradleの知識不足でわからず…
gradle勉強して探ってみたいと思います
道のりは長かった…