酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

xcframeworksに対応したRomeを使ってCarthageのキャッシュをS3にアップロードしてBitriseで超高速にビルドしよう! #bitrise #bitrisearticle

CarthageのキャッシュをS3で共有できる「Rome」がxcframeworksに対応して帰ってきた!!この記事はiOS Advent Calendar 2021の12日目の記事です。


ライブラリを事前にビルドしておいてiOSアプリのビルド時間を短縮するプロダクトに「Carthage」がある。Carthageの成果物をローカル・またはS3のリモートでキャッシュしておいてBitriseなどのCIサービスでもビルド時間を短縮するプロダクトに「Rome」がある。どちらもポエニ戦争に由来する名称でCarthageもRomeもググラビリティが最悪である。

github.com

「Rome」のxcframework対応が長い間止まっていたので、さくさんはxcframework対応のために Carthage/Buildディレクトリをzipで固めてS3に置いておき、Bitriseでビルドするたびにzipファイルをダウンロードする方法で対応していた。

2021年11月26日に「Rome」がxcframeworkに対応して帰ってきた。本記事ではさくさんが個人開発しているアプリにRomeを再導入するまでの手順を書き残している。過去にCarthage /Romeを使ったことない方には不親切な内容となっているがご了承いただきたい。

はじめに

2021年11月にさくさんはM1チップ(Apple Silicon)搭載のMacBook Pro (16-inch, 2021)に移行した。このタイミングで前々より計画していたxcframework対応を一気に進めることになった。

Rosetta上でXcodeとiOSシミュレータを使えばxcframework対応をする必要はなかったが、全社的にRosettaを使わない方針でいくことになった。四苦八苦試行錯誤しながら会社で開発しているアプリのM1対応を進めた。Twitterでうめきながら愚痴りながらなんとか環境を整えることができた。副産物的に「Four Cropper」が macOS でも動くようになった。

xcframework対応にあたって可能な限りCocoaPodsの利用をやめてCarthageに移行させることになった。Carthageでxcframeworkでビルドしたい場合には以下のコマンドで実行すればよい。

carthage update --use-xcframeworks --platform ios --cache-builds

CarthageでビルドできなかったライブラリはSwiftPMかCocoaPodsを使ってインストールする。Carthageでのxcframework対応の話はこれで終わりだ。

Carthage/BuildディレクトリをどうやってBitriseへ渡す?

さくさんは会社でも個人でもアプリのビルドにはBitriseを使っていて、gitにプッシュするとBitriseでビルドしてTestFlightにアップロード・配信する流れができている。xcframework対応にあたり問題になったのが、CarthageのBuildディレクトリをどうやってBitriseで扱うかだ。

アプリにRealmが含まれているとBitriseの最大ビルド時間である1時間半を大幅に超えてしまい、ビルドが強制的にabortされてしまうため、Bitrise上ではcarthage bootstrapは使えない。さくさんは Carthage/Buildディレクトリをzipで固めてS3にアップロードしておき、Bitriseでビルドするたびにzipファイルをダウンロードする方法で対応していた。

Bitriseでは resource-archiveステップを使うと、zipファイルをダウンロードして解凍の流れを実行してくれる。bitrise.ymlでは以下のように書く。指定したBuild.zipをダウンロードして、FourCropper/Carthage/以下に解凍してくれる。

workflows:
  primary:
    steps:
〜〜省略〜〜
    - cocoapods-install@2: {}
    - resource-archive@2:
        inputs:
        - extract_to_path: FourCropper/Carthage/
        - archive_url: https://example.com/XXXXXXXXXXXX/Build.zip

Build.zipは単純に以下のスクリプトで生成したCarthage/Buildディレクトリを圧縮したファイルである。s3cmdなどのコマンドを使ってBuild.zipをS3へアップロードするだけで簡単だ。

carthage update --use-xcframeworks --platform ios --cache-builds
cd /Users/ch3cooh/works/FourCropper_ios/FourCropper/Carthage/
rm -f Build.zip
zip -r Build.zip Build/

ライブラリの少ないアプリの場合はこの方法で問題ないが、前述のRealmなどクソデカライブラリ容量の大きなライブラリが含まれているとBuild.zipは1GB近くになってしまう。Bitriseのresource-archiveステップでこのサイズのzipファイルをダウンロードするには10分ほど掛かってしまう。早期になんとかしたいと考えていたが代替案がなくそのままになっていた。

Romeがv0.24.0.65にてxcframeworksに対応したので、このCarthage/Buildディレクトリ丸ごと圧縮作戦をやめてRomeに切り替えていく。

Romeの初期設定

Carthageの設定に関しては本エントリでは扱わない。Romeを使うための準備方法を紹介していく。

Romeのインストール

RomeはHomebrewでインストールすることもできるが、Bitriseでの利用を前提にして取り回しの効くCocoaPods経由でインストールさせる。Podfileに追記しておく。

pod 'Rome'

CarthageのキャッシュをS3へアップロードするための準備

S3にxcframeworkをアップロードしたいので、ローカルPCからAWSへ接続するための準備をおこなう。過去にAWS SDKを使ったことがあれば設定は必要ないと思う。

mkdir -p ~/.aws/
touch ~/.aws/credentials
touch ~/.aws/config

~/.aws/credentials

[default]
aws_access_key_id=AKIXXXXXXXXXX
aws_secret_access_key=UvPzXXXXXXXXXXXXXXXXXXXX

~/.aws/config

[default]
region=ap-northeast-1

Romefileの書き方

基本的にRomeはCartfile.resolvedを参照してよしなにしてくれる。

しかしリポジトリ名と生成されるxcframeworkの名称が異なる場合はキャッシュ対象から除外されてしまう。たとえばSentryのリポジトリ名はsentry-cocoaだが生成されるxcframeworkの名前はSentry.xcframeworkとなっている。細かいところでは-_の違いでも対象から除外されてしまう。たとえばUITextView-Placeholder なのに生成されるのが UITextView_Placeholder.xcframework の場合などである。

Romefileの書き方には難解な部分がある。さくさんが個人開発しているFour Cropperで実際に使っているCartfileとRomefileの例を紹介する。

FourCropperのCartfileは下記の通りだ。FourCropper自体が小規模なアプリなので利用しているライブラリは少ない。

github "yhirano/LicensePlistViewController"
github "bizz84/SwiftyStoreKit"
github "TimOliver/TOCropViewController"
github "takecian/SwiftRater"
github "devxoul/UITextView-Placeholder"
github "getsentry/sentry-cocoa"

Romefileでは前述したようにリポジトリ名と生成されるxcframeworkの名称を関連付けする必要がある。FourCropperのRomefileは下記の通りだ。

cache:
  s3Bucket: XXXXXXX_S3_BUCKET_NAME

repositoryMap:
- TOCropViewController:
    - name: CropViewController
    - name: TOCropViewController
- sentry-cocoa:
    - name: Sentry
- UITextView-Placeholder:
    - name: UITextView_Placeholder

AWS SDKの場合リポジトリひとつに対して生成されるxcframeworkが20以上あるので設定がややこしくなる。iOSエンジニア各位はどのライブラリをインストールしたらなんのxcframeworkが生成されているのか理解していると思うので、おそらくこの関連付けの作業は難なくこなせるかと思う。

以上でRomeの設定はできた。

ローカルPCでのRomeの使い方

S3へCartageのキャッシュのアップロードするにはローカルPC上で下記のコマンドを実行する。毎回すべてのライブラリをアップロードするのはS3の転送量的にも所要時間的にも勿体無いので、差分のみをアップロードする方法を採るべきだろう。

export SWIFT_VERION=`xcrun swift --version | head -1 | sed 's/.*\((.*)\).*/\1/' | tr -d "()" | tr " " "-"`

./Pods/Rome/rome download --use-xcframeworks --cache-prefix $SWIFT_VERION
carthage update --use-xcframeworks --platform ios --cache-builds
./Pods/Rome/rome list --missing --use-xcframeworks --cache-prefix $SWIFT_VERION | awk '{print $1}' | xargs -I {} ./Pods/Rome/rome upload "{}" --use-xcframeworks --cache-prefix $SWIFT_VERION

ローカルでXcodeを使う際には事前に下記のコマンドを実行する。これだけでS3から必要なxcframeworkをダウンロードできる。

export SWIFT_VERION=`xcrun swift --version | head -1 | sed 's/.*\((.*)\).*/\1/' | tr -d "()" | tr " " "-"`

./Pods/Rome/rome download --use-xcframeworks --cache-prefix $SWIFT_VERION

BitriseでのRomeの使い方

BitriseでRomeを使う際には一点注意することがある。xcframeworksに対応したRome v0.24.0.65は S3からキャッシュデータをdownloadする際にかならず失敗してしまう問題がある。すでにIssueが起票されているので修正はされると思うので、それまでの回避策を含めてBitriseでのRomeの使い方を紹介する。

Install Carthage/Build ステップ が成功しても失敗しても、次のステップから普通に実行したい場合どうするのか?

f:id:ch3cooh393:20211212131403p:plain

GUI上から「発生したエラーを無視してスキップする設定」ができないので、bitrise.ymlでis_skippable: trueを付与すれば良い。

    - cocoapods-install@2: {}
    - script@1:
        title: Install Carthage/Build
        is_skippable: true 
        inputs:
        - content: |-
            #!/usr/bin/env bash
            cd FourCropper
            export SWIFT_VERION=`xcrun swift --version | head -1 | sed 's/.*\((.*)\).*/\1/' | tr -d "()" | tr " " "-"`
            ./Pods/Rome/rome download --use-xcframeworks --cache-prefix $SWIFT_VERION
    - cache-push@2:
        inputs:
        - cache_paths: |-
            $BITRISE_CACHE_DIR
            FourCropper/Carthage -> FourCropper/Cartfile.resolved
            FourCropper/Pods -> FourCropper/Podfile.lock
            FourCropper/vendor -> FourCropper/Gemfile.lock

bitrise.ymlの設定が終われば、あとはBitriseから該当のAWS S3にアクセスできるようにアクセスキーを登録しておこう。AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_REGION を忘れないで。

f:id:ch3cooh393:20211212133117p:plain

以上でBitriseでRomeを使う設定は完了だ。

xcframeworks対応のRomeを使ってみた結果ビルド速度が超高速になった!

Carthage/Buildまるごとzipで固める作戦からRomeに移行した。ビルド時間がどれだけ早くなったのかを確認していこう。

Four Cropperでのビルド時間は約12分から約9分へ短縮した。1ビルドの所要時間を約25%軽減することができた。

f:id:ch3cooh393:20211212131625p:plain f:id:ch3cooh393:20211212131619p:plain
Four Cropperでのビルド時間は約12分から約9分へ短縮した

別のアプリAでのビルド時間は約16分から約13分へ短縮した。アプリAではアプリ内DBとしてRealmを使っている。1ビルドの所要時間を約20%軽減することができた。

f:id:ch3cooh393:20211212135211p:plain f:id:ch3cooh393:20211212141204p:plain
アプリAでのビルド時間は約16分から約13分へ短縮した

別のアプリBでのビルド時間は約50分から約37分へ短縮した。このアプリではresource-archiveステップで9分〜10分かかっていた。この時間がマルッとなくなったのが嬉しい。1ビルドの所要時間を約25%軽減することができた。

f:id:ch3cooh393:20211212135402p:plain f:id:ch3cooh393:20211212143302p:plain
アプリBでのビルド時間は約50分から約37分へ短縮した

突然Romeが使えなくなった

Rome v0.24.0.65はやはり不安定なのかもしれない。とあるライブラリのインストール先を「元リポジトリ」と「フォークしたリポジトリ」とのどちらにするか実験するために、ライブラリのアップデート+Romeへのアップロードを繰り返していたところ、突然以下のエラーが発生するようになってromeが使えなくなってしまった。

rome: device /dev/random cannot be grabbed
CallStack (from HasCallStack):
  error, called at ./Crypto/Random/Entropy/Unix.hs:60:20 in cryptonite-0.26-1BsgpeCLUsqwiNhmB6AoC:Crypto.Random.Entropy.Unix

最終的には romeを一旦削除して再インストール、もう一度試したら正常に動くようになりました。今回はCocoaPods経由でインストールしているためPodsディレクトリを削除して pod install で事なきを得ました。

さいごに

この記事をみて Bitrise を使ってみたくなった方がもしいれば「https://app.bitrise.io/referral/a927d5dbff07cc1d」からBitriseのアカウントを作成してくれると嬉しい。

現在Bitriseの無料カウントでは1ビルドあたり30分までビルド時間を使うことができる。この紹介リンクからアカウントを作るとビルド時間が5分伸びるのでとても便利になる。GitHubのアカウントを持っていれば数クリックでBitriseのアカウントも作成できる。この機会にBitriseを知った方には是非利用して欲しい。