クラウド Mac で XCFramework を構築・配布する:複数リポジトリ向けバイナリキャッシュ活用

CI/CD ·約 9 分

クラウド Mac で XCFramework を構築・配布する:複数リポジトリ向けバイナリキャッシュ活用

先週、3つのリポジトリが同時にコミットし、CI 上では同一の社内オーディオ処理フレームワークが3回も再コンパイルされ、そのたびに約8分の archive が走りました。3回目のビルドが署名証明書の期限切れで失敗したとき、当直のメンバーがチャットで「これ昨日ビルドしたばかりじゃなかった?なんでリポジトリごとに毎回やり直してるの?」と聞いてきました。この一言は多くのチームの痛いところを突いています——共有バイナリフレームワークは本来一度だけビルドすればいいはずなのに、キャッシュ機構がないせいで、パイプラインごとに同じ作業を繰り返す羽目になっているのです。

HireVPS の専有クラウド Mac 上で、このフローを一から組み直しました。マルチスライスの XCFramework を一括でビルドし、署名・チェックサム算出まで済ませて軽量なローカルバイナリキャッシュに落とし込み、複数リポジトリはそこから直接産物を取得するだけで、各自でビルドし直す必要がなくなります。以下、具体的な手順をまとめます。

なぜ自分たちで XCFramework をキャッシュすべきか

Swift Package Manager の binaryTarget はプリコンパイル済みの XCFramework を直接参照できます。これ自体は特に新しい知識ではありませんが、これを本当に活かすための前提は「産物が信頼でき、追跡可能で、複数のチームで共有できる」ことです。各リポジトリの CI がそれぞれ archive を走らせている場合、実際に手に入るのは「理論上は同じだが実際には一致を検証していない」複数のバイナリです。ビルド環境にわずかな差異(たとえば Xcode のマイナーバージョン更新など)が生じた瞬間、原因究明が非常に厄介になります。集中ビルド・一元配布によってこそ、すべての利用先が完全に同一の産物を受け取れることが保証されます。

経験則:3つ以上のリポジトリから参照される社内フレームワークは、単独でバイナリキャッシュ化する価値があります。参照が3つ未満なら、繰り返しビルドするコストのほうがキャッシュを維持するコストより低いこともあります。

クラウド Mac のリソース優位性

このフローにはマシンに対する2つの厳しい要件があります。1つは、完全な xcodebuild archive を走らせるために専有の macOS グラフィカル/コマンドライン環境が必要なこと。もう1つは、複数アーキテクチャのスライスをビルドする際のディスク I/O 負荷が大きく、共有リソースのマシンでは互いに足を引っ張り合いやすいことです。HireVPS で日単位で M4 または M4 Pro モデルを借りれば、ノードを選んだ時点で専用の SSH 認証情報が発行され、root 権限もそのまま使えます。他のユーザーと CPU タイムスライスを争う必要がなく、数時間連続でマルチアーキテクチャのビルドを走らせても隣のタスクに割り込まれることはありません。どのモデル・どのノードが現在選択可能かは、コンソールで現行の構成を確認してください。

マルチスライス XCFramework のビルド

実機とシミュレータの両方で使える XCFramework には、少なくとも2つのスライスが必要です:ios-arm64(実機用)と ios-arm64-simulator(Apple Silicon シミュレータ用)。チーム内に Intel Mac でシミュレータを動かしているメンバーがいる場合は、ios-x86_64-simulator も追加してください。忘れると、そのマシンでテストを走らせた瞬間にアーキテクチャ不一致のエラーが出ます。

2つのスライスをそれぞれアーカイブする

xcodebuild archive \
  -scheme AudioCore \
  -destination "generic/platform=iOS" \
  -archivePath build/AudioCore-iOS.xcarchive \
  SKIP_INSTALL=NO \
  BUILD_LIBRARY_FOR_DISTRIBUTION=YES

xcodebuild archive \
  -scheme AudioCore \
  -destination "generic/platform=iOS Simulator" \
  -archivePath build/AudioCore-iOSSim.xcarchive \
  SKIP_INSTALL=NO \
  BUILD_LIBRARY_FOR_DISTRIBUTION=YES

XCFramework へ統合する

xcodebuild -create-xcframework \
  -framework build/AudioCore-iOS.xcarchive/Products/Library/Frameworks/AudioCore.framework \
  -framework build/AudioCore-iOSSim.xcarchive/Products/Library/Frameworks/AudioCore.framework \
  -output build/AudioCore.xcframework

ビルドが完了したら zip にまとめます。このファイルが配布すべき最終産物であり、以降は手動で変更しないでください。

署名とチェックサム

社内フレームワークでも、出所を下流で検証しやすくするために署名しておくことをお勧めします。

codesign --sign "Apple Distribution: Your Team" \
  --timestamp \
  build/AudioCore.xcframework

zip -r AudioCore-1.4.0.xcframework.zip build/AudioCore.xcframework

署名してパッケージ化した後は、SwiftPM 付属のコマンドでチェックサムを算出します。この値は下流リポジトリの Package.swift に記載する必要があります。

swift package compute-checksum AudioCore-1.4.0.xcframework.zip

チェックサム検証が失敗する最も多い原因は、パッケージ化後に zip の内容を書き換えたか、圧縮ツールを変えたことでファイル内部の順序が変わったことです。原則はシンプルです:チェックサムを算出した後は、その zip ファイルは凍結し、もう手を加えないでください。

ローカルバイナリキャッシュの構築

複雑なサービスは不要で、静的ファイルをダウンロードできるディレクトリ構成があれば十分です。

レイヤー 内容 パス例
フレームワーク名 社内フレームワークごとに1ディレクトリ /cache/AudioCore/
バージョン番号 バージョンごとに1サブディレクトリ /cache/AudioCore/1.4.0/
産物 zip パッケージ + チェックサムテキスト AudioCore-1.4.0.xcframework.zip, checksum.txt

下流リポジトリの Package.swift では以下のように参照します。

.binaryTarget(
    name: "AudioCore",
    url: "https://cache.internal.example/AudioCore/1.4.0/AudioCore-1.4.0.xcframework.zip",
    checksum: "前のステップで算出したチェックサム"
)

ストレージ設計としては、中規模チームなら中核フレームワーク3〜4種の直近10バージョンをそれぞれ保持すれば十分です。パッケージ1つあたりのサイズは通常20〜80MB程度で、総容量は数GB以内に抑え、90日間取得のない古いバージョンは定期的に削除しましょう。

マルチリポジトリ CI パイプラインへの統合

各下流リポジトリの CI では xcodebuild archive を実行する必要がなくなり、swift package resolve で指定バージョンを取得するだけで済みます。ビルドに数分かかっていた処理が、数秒のダウンロードに短縮されます。新バージョンをリリースする際は、キャッシュディレクトリに新しいバージョン番号のディレクトリを作成し、対応するリポジトリの checksum 参照を更新するだけでよく、キャッシュサーバーのコードに手を加える必要はありません。

注意点:複数のリポジトリが同時に「latest」のような可変タグを参照するのは絶対に避けてください。チェックサム機構は URL と内容が一対一で対応することを前提としています。同一バージョン番号下でファイルの上書きを許してしまうと、SwiftPM の整合性検証が意味をなさなくなります。

チェックリスト

よくある質問

実機とシミュレーター両方に対応するには XCFramework にどのスライスが必要ですか?

最低限、実機向け ios-arm64 とApple Silicon シミュレーター向け ios-arm64-simulator の2つが必要です。チームに Intel Mac でシミュレーターを動かすメンバーがいる場合は ios-x86_64-simulator も追加してください。無いとそのマシンではアーキテクチャ不一致で即座に失敗します。

binaryTarget のチェックサム検証が失敗する主な原因は?

パッケージ後に zip の内容を手動で変更したり、異なる圧縮ツールを使ってファイル順序が変わったことが多い原因です。正しい手順は、最終的に配布する zip ファイルに対して swift package compute-checksum を一度だけ実行し、その後このファイルを一切変更しないことです。

ローカルバイナリキャッシュサーバーはどれくらいのストレージを想定すべきですか?

中規模チームで主要フレームワーク3〜4本の直近10バージョンを保持する想定なら、圧縮後1件あたり通常20〜80MBで、合計数GB程度で十分です。クラウド Mac の SSD 上に専用パーティションを用意し、90日以上取得されていないバージョンは定期的に削除することを推奨します。

HireVPS(HireVPS)

今すぐ専有クラウド Mac mini を試してみましょう

日単位でレンタル可能、2分で SSH/VNC の認証情報を取得、プランはいつでもアップグレードできます。

Mac mini をレンタルする