RomeでCarthageのビルドコストを下げよう

## はじめに
iOSアプリ開発を行うとき、ライブラリの導入にCarthageを使うことがあると思います。
Carthageは、CocoaPodsに比べて事前にビルドを行うためコンパイル時間が短い、ワークスペースが弄られないといったメリットがあります。
前者の利用で使っているところも多いのではないでしょうか

## Carthageのビルド時間は長い？
Carthageはその特性上、一度全てのライブラリをビルドしてフレームワークを作成する必要があるため、環境構築に時間がかかります。
またSwiftのABI安定化はまだなので、XcodeをアップデートしてSwiftのバージョンが変わると再度ビルドする必要があり、地獄をみます。
ライブラリのアップデートが必要になった時も同じく地獄をみます。

このビルド時間問題で影響を受けるのは、数人〜数十人のエンジニアを抱えているチームだと思っています。
個人開発など、自分だけが再ビルドすれば良いのであれば特に問題にはならないと思いますが、他の方々もビルドする必要があると、その分進捗が死にます。
実際にCarthageのビルドがボトルネックとなったことがあり、割と笑えません。

よくある解決方法として、生成されたフレームワークをGitに含めるという方法もありますが、
使っているライブラリの量によってはウンGBになることもあり、リポジトリが肥大化するという問題が発生します。
またチーム内でXcodeのバージョンを揃えていない場合、前述したSwiftバージョン問題が発生する可能性があります。

…とはいえ、何らかの方法でフレームワークをキャッシュしてうまいことできないかなぁと調べていたところ、Romeというツールがいい感じだったのでご紹介いたします。

## Romeとは
Carthageの生成物をオブジェクトストレージに共有できるツールです。
現在Amazon S3, S3互換のあるMinio, Cephに対応しています。

[GitHub - blender/Rome: A cache tool for Carthage](https://github.com/blender/Rome)

## 使い方
READMEに記載されている通りなのですが、HomebrewかCocoaPods経由で導入ができます。
今回はHomebrewを使います。

```bash
brew install blender/homebrew-tap/rome
```

#### AWS S3の認証情報を設定する
キャッシュ先としてS3を利用しますので、
バケットへのアクセス権限を持ったユーザーの作成とアクセスキーID, シークレットキーを入手します。

```bash:~/.bash_profile
export AWS_ACCESS_KEY_ID=<ACCESS_KEY_ID>
export AWS_SECRET_ACCESS_KEY=<SECRET_ACCESS_KEY>
export AWS_REGION=<REGION>
```

#### Romeファイルの作成
次に、プロジェクトのあるディレクトリで`Romefile`を作成します。中身はYAMLで書いていきます。

##### Cache(必須)
`s3Bucket`に作成したバケット名を指定します。ローカルにキャッシュすることも可能です。

```yaml:Romefile
cache:
  s3Bucket: ios-xxapp-carthage-cache
#　　　　local: ~/Library/Caches/Rome
```

##### Repository Map
Romeは`Cartfile.resolved`を参照するため、リポジトリ名とフレームワーク名が異なる場合**うまくキャッシュができません。**依存ライブラリに関しても同様です。
Repository Mapでリポジトリ名とフレームワーク名を明記する必要があります。
例えば[Facebook-SDK-Swift](https://github.com/facebook/facebook-swift-sdk)ですと、フレームワーク名は「FacebookCore」, 「FacebookLogin」, 「FacebookShare」となるので、これらを指定する必要があります。

```yaml:Romefile
repositoryMap:
- Facebook-SDK-Swift:
    - name: FacebookCore
    - name: FacebookLogin
    - name: FacebookShare
- facebook-objc-sdk:
    - name: FBSDKCoreKit
    - name: FBSDKLoginKit
    - name: FBSDKShareKit
- xxxx-ios:
    - name: XXXX
```

#### Ignore Map
キャッシュする必要のない、したくないライブラリを指定できます。

```yaml:Romefile
ignoreMap:
- xxxxx:
    - name: xxxxx
```

#### 共有されているライブラリを扱う

##### S3からダウンロードする
```bash
rome download --platform iOS
```

##### S3へアップロードする
```bash
rome upload --platform iOS
```

##### S3に共有されていないライブラリを表示
```bash
rome list --missing --platform iOS
```

#### --cache-prefixについて
そのまま使っていると、`s3Bucket`で指定した階層にダウンロード/アップロードされてしまうため、チーム内のSwiftのバージョンが統一されていない場合Swiftバージョン問題が発生します。
これを回避するために、Romeではオプションとして`--cache-prefix`が用意されており、Swiftのバージョンごとにprefixを分けることができます。

```bash
rome download --platform iOS --cache-prefix Swift4_2
```
READMEには、`xcrun swift --version`で取得したバージョンを使う方法が記載されていました。
私はこちらを使っています。

> --cache-prefix \`xcrun swift --version | head -1 | sed 's/.*\((.*)\).*/\1/' | tr -d "()" | tr " " "-"\`

#### Gitから引っ張ってきた時とかにいい感じにライブラリを揃える
S3から引っ張ってきた後、未ビルドでアップロードされていないものだけをビルドしてアップロードするようにします。
ライブラリを追加した時や、gitからcheckout/pullした時に呼び出すといい感じにやってくれるはず。

```bash
cache_prefix=`xcrun swift --version | head -1 | sed 's/.*\((.*)\).*/\1/' | tr -d "()" | tr " " "-"`
platform='iOS'

rome download --platform $platform --cache-prefix $cache_prefix
rome list --missing --platform $platform --cache-prefix $cache_prefix | awk '{print $1}' | xargs sh -c "carthage update --platform $platform --cache-builds; rome upload --platform $platform --cache-prefix $cache_prefix"
```

## 感想
Romeのおかげでビルド地獄から抜け出せそうな気がしてます。ありがとうRome

## 参考
[GitHub - blender/Rome: A cache tool for Carthage](https://github.com/blender/Rome)