【Carthage】Xcode14.3以降でcarthage bootstrapが失敗する💥

こんにちは。withでiOSエンジニアをしている長尾（ @zrn-ns ）です。

突然ですが、最近Xcodeを14.3以上にアップデートしたらCarthageのビルド（carthage bootstrap）が通らなくなった... って方、結構多いんじゃないでしょうか。

今回はこの問題について、弊社で行った対応についてお話します。

# どんなエラーが出るのか

今回解説する問題については、既にCarthage公式でIssueが起票されています。

しかし現状その対処方法について、公式からのアナウンスはありません。コラボレーターの方も忙しいんでしょうね...。

https://github.com/Carthage/Carthage/issues/3333

具体的な現象としては　`carthage bootstrap` を実行すると下記のようなエラーが発生します。(上記Issueから引用です)

```shell
Build Failed
	Task failed with exit code 65:
	/usr/bin/xcrun xcodebuild -workspace /Users/jadian/Git_Project/Company/tmp_vietnam_ios/Carthage/Checkouts/Masonry/Masonry.xcworkspace -scheme Masonry\ iOS -configuration Release -derivedDataPath /Users/jadian/Library/Caches/org.carthage.CarthageKit/DerivedData/14.3_14E222b/Masonry/v1.1.0 -sdk iphoneos ONLY_ACTIVE_ARCH=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES archive VALIDATE_WORKSPACE=NO -archivePath /var/folders/05/cy4ywfcd4l99__z394q_cskw0000gn/T/Masonry SKIP_INSTALL=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=NO CLANG_ENABLE_CODE_COVERAGE=NO STRIP_INSTALLED_PRODUCT=NO (launched in /Users/jadian/Git_Project/Company/tmp_vietnam_ios/Carthage/Checkouts/Masonry)

This usually indicates that project itself failed to compile. Please check the xcodebuild log for more details: /var/folders/05/cy4ywfcd4l99__z394q_cskw0000gn/T/carthage-xcodebuild.0A2id9.log
```

ほぼこのエラーに情報はないので、指示の通り詳細ログを見に行きます、すると下記のようなエラーが発生してビルドが停止していることが分かります。

```
ld: file not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphoneos.a
```

エラーの内容から、ビルド時に必要なXcodeのファイル `libarclite_iphoneos.a` が存在しない、というエラーのようです。

# なぜこのエラーが発生するのか

Issueを見ていくと、どうやらiOS11未満のOSをサポートする（Deployment Targetに含む）プロジェクトをビルドする場合に使用されるARC関連のファイル `libarclite_iphoneos.a` が、Xcode14.3からバンドルされなくなったようです。

Appleのサポートページを見ると、Xcode14.0のリリース時点でiOS11未満向けのビルドは既にできなくなっており、その後Xcode14.3のリリース時点で `libarclite_iphoneos.a` が削除されたように見えます。

https://developer.apple.com/jp/support/xcode/

これにより、iOS11未満をサポートするサードパーティ製ライブラリをCarthageでビルドしようとした場合、ビルドが通らなくなってしまったということのようです。

# 対応方針の選択肢

iOSエンジニアがとれる選択肢を列挙します。

### 案A. Carthageの開発に参加し、この問題を修正する

エンジニアとして最も正しいと思うのはこの方法です。
しかし正直ハードルは高いので今回は避けたいです。

### 案B. 限界まで何もしない

Xcodeを14.3未満を使い続けていれば、この問題は発生しません。
待っている間にCarthageのアップデートが提供され、問題が解消する可能性があります。

上に記載したIssueでも話されているように、Carthage側でDeployment Targetを任意のバージョンに変更できる機能が提供されるようになれば、問題は解消するでしょう。
しかし具体的にいつ修正が提供されるかは不明です。

### 案C. Carthageを使うのをやめる

Carthageを使うのをやめ、すべてのライブラリをCocoaPodsやSwift Package Managerに移動すれば、この問題を回避できます。
（ただしCocoaPodsに移す場合は、CocoaPods側でも同じ問題は発生するはずなので、minimum_deployment_targetをiOS11以上で上書きする必要がある気がします）

### 案D. ワークアラウンドαを行う

そもそもこの問題は、Xcode11未満をDeployment Targetに含む場合にのみ発生するので、Carthageでチェックアウトしたライブラリをビルドする前に、各ライブラリの最小サポートバージョンをiOS11以上に上書くようなスクリプトを書くことで、解消が見込まれます。

参考: 

https://github.com/Carthage/Carthage/issues/3333#issuecomment-1605757401

### 案E. ワークアラウンドβを行う

`libarclite_iphoneos.a` 等の不足したファイルを古いXcodeから新しいXcodeにコピーすれば、この問題は解消します。

参考:

https://github.com/Carthage/Carthage/issues/3333#issuecomment-1508132105


# どの選択肢を選ぶのか

上記の案の中で、僕は **案E** を選択しました。

最初は案Bでいこうと考えていたのですが、早くXcodeをアップデートして最新の言語機能も使いたいですし、何より最新のXcodeがないと最新のiOS端末でデバッグビルドができないので、あまり長く待機し続けることはできませんでした。

また案Cについては、対応コストがまあまあ大きいですし、弊社ではCarthageのビルド済みバイナリをキャッシュすることでCIを高速化しているため、Carthageを捨てるのはなるべく避けたいと考えました。

# 具体的にどのように対応するのか

案Eの対応手順をざっくり説明すると、

1. Xcode14.3から削除されるファイルを手元に残しておく
1. 各自Xcodeを更新したら、ファイルを新しいXcodeにコピーする

以上です。

Xcode14.3で削除されたファイルは、下記のパス(arcディレクトリ)に格納されているファイル群です。
```
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc
```

プロジェクトの各メンバーは古いバージョンのXcodeをバックアップしておき、Xcode14.3をインストール後、上記ディレクトリのファイルをXcode14.3の同一階層にコピーしてあげれば、問題を解消できます。

# もっと簡単にならないの？

Xcodeをアップデートする度に毎回この手順を踏むのは面倒だという場合には、以下のようなスクリプトを組む事もできます。

```bash
#!/bin/bash

# 古いXcodeからarcディレクトリの中身をコピーして任意のパスに保存し、ここにそのパスを宣言しておく
ARC_DIR_BACKUP_PATH="/path/to/directory/arc/"

set -eu
cd `dirname $0`

if [[ "`whoami`" != "root" ]]; then
  echo 'Please execute with root user.'
  exit
fi

XCODE_PATH=`xcode-select -p`
ARC_DIR_PATH="$XCODE_PATH/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc"

# arcディレクトリがない場合だけ処理を行う（既にある場合は実行しない）
if [ -d $ARC_DIR_PATH ]; then
  echo 'arc directory exists. Workaround script skipped.'
  exit 0
fi

sudo mkdir -p "$ARC_DIR_PATH"

sudo cp "$ARC_DIR_BACKUP_PATH/*" "$ARC_DIR_PATH"
```

これは `arc` ディレクトリのバックアップ（事前に指定）を現在アクティブになっているXcodeにコピーするスクリプトです。
弊社ではCarthageやCocoaPods、Bundlerで管理しているライブラリを一括でインストールしてくれるスクリプトを用意しているので、その中で上記のスクリプトを実行するようにすることで、Xcodeのアップデート時などにも忘れることなく手軽にワークアラウンドを実行する事ができるようになりました。

なお上記スクリプトはを動かすには、事前にいくつか準備が必要です。

1. 事前に古いXcodeから `arc` ディレクトリをコピーし、任意の場所（ホームディレクトリとか）にコピーしておき、そのパスをスクリプト内の `ARC_DIR_BACKUP_PATH` に代入しておく必要がある
1. このスクリプトはrootで実行する必要がある。sudoをつけて実行するか、rootユーザで実行する
1. macOS Mojave以降では、`sudo` するためにはターミナルエミュレータに「フルディスクアクセス」を許可する必要がある。こちらの記事を参考に、事前に権限を付与する → [Macでsudoが効かないのはMojaveのせいかもしれない | eureka](https://mykii.blog/mac%E3%81%A7sudo%E3%81%8C%E5%8A%B9%E3%81%8B%E3%81%AA%E3%81%84%E3%81%AE%E3%81%AFmojave%E3%81%AE%E3%81%9B%E3%81%84%E3%81%8B%E3%82%82%E3%81%97%E3%82%8C%E3%81%AA%E3%81%84/)

# 最後に

少しでも参考になれば幸いです。
内容に間違いなどあればご指摘いただけると助かります。
「うちではこうやってる！」のような情報提供もお待ちしております。

これを期にSwift Package Managerに移行するのも良いとは思いますが、なかなか手間がかかるので、一旦ワークアラウンドで回避するのはアリだと思います。

なお上記の手順を行ったことで発生した不利益について、筆者及び弊社は一切責任を負いません。ご自身の判断でご対応ください。念のため。

# PR

withではエンジニアの採用を行っています。
興味があれば、ぜひお問い合わせください！

https://enito.co.jp/careers/