CarthageのキャッシュをS3で共有できる「Rome」がxcframeworksに対応して帰ってきた!!この記事はiOS Advent Calendar 2021の12日目の記事です。
- Romeとは?
- はじめに
- Carthage/Build ディレクトリを zip でまとめて Bitrise でダウンロードしていたが……
- Romeの初期設定
- xcframeworks対応のRomeを使ってみた結果ビルド速度が超高速になった!
- 追記:突然Romeが使えなくなることがある……
- さいごに
Romeとは?
ライブラリを事前にビルドしておいてiOSアプリのビルド時間を短縮するプロダクトに「Carthage」がある。Carthageの成果物をローカル・またはS3のリモートでキャッシュしておいてBitriseなどのCIサービスでもビルド時間を短縮するプロダクトに「Rome」がある。どちらもポエニ戦争に由来する名称でCarthageもRomeもググラビリティが最悪である。
「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 ディレクトリを zip でまとめて Bitrise でダウンロードしていたが……
さくさんは会社でも個人でもアプリのビルドには Bitrise を使っている。git にプッシュすると Bitrise でビルドして TestFlight にアップロード・配信する流れができている。xcframework 対応にあたり問題になったのが、Carthage の 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
ステップで1GB近いの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を使ったことがあれば設定は必要ないと思う。
AWSのIAMとバケットを作成する
AWSのIAMとバケットを作成する。
- AmazonS3FullAccess ポリシーを持つユーザー
- Carthage/Build のビルド済み .xcframework を格納する S3バケット
アクションを縛りたいがうまく行かなかったのでバケットポリシーは全許可にしている。
{ "Version": "2012-10-17", "Id": "PolicyXXXXXXXXXX", "Statement": [ { "Sid": "StmtXXXXXXXXX", "Effect": "Allow", "Principal": "*", "Action": "s3:*", "Resource": "arn:aws:s3:::XXXXXXX_S3_BUCKET_NAME" } ] }
AWSの認証情報を入力する
aws configurate
を使っても良いが、Romeの説明でコマンドラインを使ってたのでそれに倣った。
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 ステップ
が成功しても失敗しても、次のステップから普通に実行したい場合どうするのか?
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
を忘れないで。
以上でBitriseでRomeを使う設定は完了だ。
xcframeworks対応のRomeを使ってみた結果ビルド速度が超高速になった!
Carthage/Buildまるごとzipで固める作戦から Rome に移行した。ビルド時間がどれだけ早くなったのかを確認していこう。
Four Cropperでのビルド時間は約12分から約9分へ短縮した。1ビルドの所要時間を約25%軽減することができた。
別のアプリAでのビルド時間は約16分から約13分へ短縮した。アプリAではアプリ内DBとしてRealmを使っている。1ビルドの所要時間を約20%軽減することができた。
別のアプリBでのビルド時間は約50分から約37分へ短縮した。このアプリではresource-archiveステップで9分〜10分かかっていた。この時間がマルッとなくなったのが嬉しい。1ビルドの所要時間を約25%軽減することができた。
追記:突然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を知った方には是非利用して欲しい。