Xcode 12になって悲しいことにCarthageが使えなくなりました。説明するまでもありませんが、CarthageはiOSアプリ開発の主要なパッケージマネージャーのひとつです。
開発を止められないお仕事アプリは即日ですべてCocoaPodsへ移行しました。CocoaPodsは導入が簡単なので大好きなのですが、ビルド時間が長くなる傾向にあります。デバッグ実行のたびにつらみが増します。過去に「Bitriseで iOSアプリのビルド速度を cocoapods-binary を使って高速化する - 酢ろぐ!」で紹介したように、CI上ではcocoapods-binaryを使ってビルド時間の短縮を狙うことが可能です。
Apple公式であるSwiftPMへの移行も検討しましたが、Bitriseでキャッシュが効かずCIでのビルド時間が増えてしまうので不採用になりました。
Xcode 12の劇的なリリースから1ヶ月経って状況も落ち着いてきたので、Bitrise+Xcode12でCarthageを使ってiOSアプリをビルドするように戻しました。
- 実行環境
- carthage.shを準備する
- 本プロジェクトでのディレクトリ構成
- Bitriseでの設定
- トラブルシューティング:Command PhaseScriptExecution failedエラーが発生する
- トラブルシューティング:10/23のNYTPhotoViewerアップデートでCarthageビルドに失敗するようになった
- まとめ
実行環境
- macOS 10.15.7 (19H2)
- Xcode 12.0.1
- Carthage v0.36.0
古いバージョンのCarthageを使っている場合には、Mac HomebrewでCarthageをアップデートしておくと良いかもしれません。
brew upgrade Carthage
Carthageは頻繁にアップデートがおこなわれないのでみんな最新のものを使っているかと思います。
carthage.shを準備する
この項目はまるっと 「Carthage/Xcode12Workaround.md at master · Carthage/Carthage · GitHub」 からの引用が多いです。WorkaroundスクリプトはXcodeのバージョンによって変わってしまうかもしれませんので、随時適切なスクリプトに読み替えてください。本記事では2020/10/16時点のスクリプトを利用しています。
carthage
のコマンドの代わりに carthage.sh
スクリプトを使います。
mkdir -p bin touch bin/carthage.sh
「Carthage/Xcode12Workaround.md at master · Carthage/Carthage · GitHub」 から、スクリプトをまるっとコピーします。
# carthage.sh # Usage example: ./carthage.sh build --platform iOS set -euo pipefail xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) trap 'rm -f "$xcconfig"' INT TERM HUP EXIT # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise # the build will fail on lipo due to duplicate architectures. CURRENT_XCODE_VERSION=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3) echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$CURRENT_XCODE_VERSION = arm64 arm64e armv7 armv7s armv6 armv8" >> $xcconfig echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' >> $xcconfig echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig export XCODE_XCCONFIG_FILE="$xcconfig" carthage "$@"
本プロジェクトでのディレクトリ構成
なお、本記事での紹介に使っているプロジェクトのディレクトリ構成は下図のようになっています。ルートディレクトリにプロジェクトファイルは置いていません。
BitriseのScriptステップでこの carthage.sh
を実行しますので、本記事を読んで「Bitriseで動かないよ〜」という方がいれば適宜読み替えてください。
Bitriseでの設定
ローカルPC上でCarthage経由でライブラリをインストールするのは以下のコマンドを実行すればよいです。
chmod +x TweetClips/bin/carthage.sh TweetClips/bin/carthage.sh bootstrap --platform iOS --cache-builds --project-directory TweetClips
このコマンドをBitrise上で再現する場合にはCarthageステップを使わずにScriptステップを使います。Bitrise上でのステップは以下のように書きました。
workflows: primary: steps: {{ 〜〜〜中略〜〜〜 }} - cocoapods-install@1.11.0: {} - script@1: title: install carthage inputs: - content: |- #!/usr/bin/env bash chmod +x TweetClips/bin/carthage.sh #export GITHUB_ACCESS_TOKEN="$GITHUB_ACCESS_TOKEN" TweetClips/bin/carthage.sh bootstrap --platform iOS --cache-builds --project-directory TweetClips
BitriseでCarthageを使っている方はご存知かと思いますが、Cartfileがルートディレクトリに存在しない場合エラーを吐くので、--project-directory
オプションでCartfileを置いている場所を指定しています。詳しくは「Bitriseでトラブル発生!Cartfile.resolved がルートディレクトリにないリポジトリのビルドが通らない - 酢ろぐ!」をご覧ください。
export GITHUB_ACCESS_TOKEN〜
の部分はあってもなくても良いですが、CIサービス上でのビルド時間を短縮することができるので調べてみてください。具体的には下記のエラーを防ぐことができます。
API rate limit exceeded for xxx.xxx.xxx.xxx. (But here’s the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)
トラブル:Carthageディレクトリがキャッシュされない
Carthageステップを利用しているとBitrise側で自動的にキャッシュをよしなにしてくれます。
今回はScriptステップでcarthage.sh
を実行しているため、自動的にはキャッシュをプッシュしてもらえません。悲しい。cache-pushステップでキャッシュするディレクトリを手動で追加をする必要があります。
- cache-push@2: inputs: - cache_paths: |- $BITRISE_CACHE_DIR TweetClips/Carthage -> TweetClips/Cartfile.resolved
GUI側で設定するのであればこの部分を編集してください。
トラブルシューティング:Command PhaseScriptExecution failedエラーが発生する
bin/carthage.sh update --platform ios --cache-builds
実行時、ある日何もしていないのに下記のエラーが発生する。
The file couldn’t be saved.
Command PhaseScriptExecution failed with a nonzero exit code
macOSの再起動で発生しなくなります。
または、carthage copy-frameworks produces "The file couldn’t be saved." error · Issue #3056 · Carthage/Carthage · GitHubによれば、macOSのシステム設定でXcodeにフルディスクアクセス権限を付与した状態で、Run Scriptを以下のように書き換えることで発生しなくなります。
rm -rf ${TMPDIR}/TemporaryItems/*carthage* /usr/local/bin/carthage copy-frameworks
Xcodeにフルディスクアクセス権限を付与してもエラーが消えないようでしたら、ターミナルで以下のコマンドを実行して、直接手動でtempファイルを削除する方法もアリかと思います。
open $TMPDIR/TemporaryItems
トラブルシューティング:10/23のNYTPhotoViewerアップデートでCarthageビルドに失敗するようになった
Carthageでビルドせずに CocoaPodsでビルドするようにしました。CocoaPodsはCocoaPodsで最新版のv1.10でビルドするとエラーが発生するかもしれないのでこちらのエントリをご覧ください。
まとめ
以上でXcode12+CarthageでiOSアプリをビルドできるようになりました。
そもそもとしてcocoapods-binaryを使ってキャッシュを利用するようにしているので、Carthageを使うようにしてもCI上でのビルド時間自体はそんなに変わりませんが、ローカルPCでのデバッグ実行時のビルドが軽くなった印象はあります。
なお、FirebaseをCarthage経由でインストールする場合にはいくつか注意する点があるのであわせて関連記事もお読みください。
ちなみに新規でBitriseを使ってみようとお考えの方はこちらのリンクからアカウントを作成していただけると、通常10分のビルド時間が増えるのでよければこちらからどうぞ!