2026年 独占リモート Mac iOS/macOS CI:Xcode 26 Compilation Cache と sccache/ccache リモートコンパイルキャッシュはどう選ぶ?——明示的モジュール、SPM 盲区、並行 Job ディレクトリ分離と SSH ヘッドレス再現可能な意思決定マトリクス+FAQ
独占リモート Mac(SSH ヘッドレス)で iOS/macOS CI を回すとき、「コンパイルを速くする」は一つの問題に見えがちですが、Xcode 26 Compilation Cache、Clang の明示的モジュール成果物、DerivedData の階層、マシン横断の sccache/ccache はレイヤーが異なり、無効化ルールと並行境界も別物です。層を誤って積むと、ヒット率はきれいなのに再現しづらいビルドが残ります。
1. 三つのキャッシュ層を切り分ける——「速い」の一キーにしない
層 A — Xcode 26 Compilation Cache(Apple ツールチェーン内蔵): 同一ツールチェーンバージョン上でコンパイル単位を再利用します。明示的モジュールと安定したモジュールグラフに強く結びつき、「同一マシン・同一 SDK・同一 TU の再実行」向けであり、Xcode ポイントリリースや DEVELOPER_DIR が違うノード間の自動共有ではありません。
層 B — DerivedData / モジュールキャッシュディレクトリ: プロジェクト索引、プリコンパイル済みモジュール、Swift 増分状態を保持します。専用 Runner には向きますが、並行 Job ではディレクトリ分離が必須です。Job A が書き込み中に Job B が半完成の索引を読むと、不安定化します。
層 C — sccache / ccache(コンパイララッパ+リモートオブジェクトストレージ): clang / swiftc 呼び出しを横取りし、前処理済みコンパイルコマンドのハッシュを S3・MinIO・NAS 等にマップします。Runner・ブランチ横断の再利用に強い一方、サンドボックスパスや Apple 固有フラグをキーに含めないと、大量ミスまたは誤ヒットの原因になります。
レビューの合言葉:Compilation Cache = ツールチェーン内、DerivedData = プロジェクト内、sccache/ccache = 組織横断。 明示的な無効化とパス境界を決めれば三層は共存できます。そうでなければ、永続 Runner とエフェメラル Runner のトレードオフがキャッシュ汚染に埋もれます。プール境界は 2026年 独占リモート Mac 自ホスト CI:永続 Runner とエフェメラル Runner プール——キャッシュウォームアップ、キュー尾部遅延、クレデンシャル注入と SSH 観測の意思決定マトリクス+FAQを参照してください。
2. Xcode 26 Compilation Cache:「デフォルト ON」で足りる場面
Xcode 26 世代では、大規模 ObjC/Swift 混在かつ 安定した明示的モジュール、CI ノードの xcodebuild -version 固定時に Compilation Cache が最も効きます。クリーン後の二回目フルビルドでフロントエンド時間が大きく短縮されることが多いです。一方、-derivedDataPath ガバナンスやロックファイル揺れ時の SwiftPM 再解決の代替にはなりません。
Compilation Cache を主軸にする条件:
- 独占 Mac 上の永続 Runner、または Job ごと DerivedData+共有読み取り専用 Compilation Cache シード層
- CI とローカルで明示的モジュールを揃える(
SWIFT_ENABLE_EXPLICIT_MODULES等) - リリースパイプラインが「全 PR で最後のキャッシュ層を共有」より再現性を優先
分離またはバージョン付き無効化が必要な場合: 複数 Job が同一ホームを共有、Xcode マイナーを週次で更新、コンプライアンスで commit SHA+ロックファイルから出力を説明する必要がある——キャッシュルートは「無期限」ではなくバージョン付き無効化リストに載せます。
3. 明示的モジュール:Compilation Cache を意味あるものにする前提
明示的モジュールは、暗黙スキャンによる「誰が先にビルドするか」を宣言されたモジュールグラフに置き換えます。CI ではキャッシュキーが安定し、並列度が予測可能になり、Compilation Cache のヒットが意図と一致しやすくなります。ローカル OFF / CI ON(またはその逆)の典型的な「CI は緑、手元は赤」は、多くがモジュールグラフ不一致です。
Runbook チェックリスト:
xcodebuildと Xcode GUI で同じxcconfig/ ビルド設定をエクスポートする- Xcode アップグレードのたびにモジュールキャッシュ / Compilation Cache のエポックを上げる
- 並行 Job ごとに固有の
-derivedDataPath。SDK / シード層は読み取り専用共有、書き込み可能なモジュールキャッシュルートは共有しない
4. SPM 盲区:キャッシュ ON でもビルドが遅い理由
Swift Package Manager の解決・チェックアウト・バイナリ成果物は、Clang Compilation Cache とは別パイプラインです。よくある穴:
- 未コミットまたは未検証の
Package.resolved— Job ごとに依存グラフが変わる ~/Library/Caches/org.swift.swiftpmの競合 — 解決失敗や初分の遅延- バイナリパッケージ+異なる Xcode — Compilation Cache は SDK 不一致を直せない
HOME固定なしで SPM パスを sccache キーに含める — SSH ヘッドレスでホームレイアウトが違うと誤ヒット
SPM 方針は別記事で整理: 2026年 独占リモート Mac iOS/macOS CI:SwiftPM Package.resolved——再現ビルド、キャッシュウォームアップ、CI ロックドリフトと SSH パイプライン意思決定マトリクス+FAQ。 SPM グラフをロックしてから Compilation Cache と sccache を有効化しないと、ノイズをキャッシュするだけです。
5. リモート Mac CI での sccache と ccache
| 観点 | ccache | sccache |
|---|---|---|
| リモートバックエンド | プラグインまたは自前同期 | S3/Azure/GCS ネイティブ |
| Rust/LLVM 系 | 成熟 | 成熟 |
| Swift / Apple 固有フラグ | ハッシュ設計に注意 | ハッシュ設計に注意 |
| マルチテナント分離 | プレフィックス+バケットポリシー | プレフィックス+バケットポリシー |
| 運用コスト | 低(単一ホスト) | 中(オブジェクトストア+IAM) |
2026年 独占リモート Mac の目安: 永続 Runner 1 台・小規模チームなら Compilation Cache+ローカル DerivedData 階層で十分なことが多い。Mac mini プールでブランチ爆発と C/C++/Rust 共有ターゲットがあるなら sccache リモートバケットを検討し、Apple ターゲットは内蔵キャッシュに任せる。swiftc の二重ラップは切り分け手順なしに避けてください。
ccache は「単一マシン+少数ネイティブ依存」や既存 ccache 運用チーム向け。Bazel リモートキャッシュを既に持つなら、sccache でバケットポリシー・IAM・ライフサイクルを流用できます。
6. SSH ヘッドレスホストでの並行 Job ディレクトリ分離
独占 Mac ≠ 共有 DerivedData 1 つ。最低限の分離モデル:
DERIVED_DATA_ROOT/$CI_JOB_ID— Job ごとに書き込み可能COMPILATION_CACHE_ROOT/v$XCODE_BUILD— 読み取り専用マウントまたは Job ごとシードコピーSCCACHE_BUCKET_PREFIX/$REPO/$XCODE_BUILD/$BRANCH_POLICY— 組織横断共有。リリースと PR でバケット分割- Job ごとに
TMPDIRとHOMEを上書きし、CI ユーザーがキーチェーンや SPM キャッシュを汚染しない
SSH 切り分けでは GUI なしで xcodebuild -showBuildSettings、実際の -derivedDataPath、Compilation Cache パス、sccache --show-stats をエクスポートします。「82% ヒット」だけでなくパスをログに残し、ミスの理由を説明できるようにします。
7. 再現可能な意思決定マトリクス(Runbook に貼り付け可)
| 状況 | Compilation Cache | sccache/ccache |
|---|---|---|
| 専用 Mac 1 台、永続 Runner、Xcode 固定 | ✓ 主軸 | 任意 |
| Mac mini プール、PR 並行が多い | ✓ ノードごと | ✓ リモートバケット |
| エフェメラル Job、Job 間状態ゼロ厳守 | ✓ 読み取り専用シード | △ 共有書き込みに注意 |
| SwiftPM 多め、ロックファイル不安定 | △ 先に SPM ロック | △ 先に SPM ロック |
| Rust/C++ ヘルパーターゲットを含むモノレポ | ✓ Apple ターゲット | ✓ 非 Apple ターゲット |
| Xcode を週次でロールアウト | ✓ +バージョン bump | ✓ バケットにバージョン含める |
2026年のよくあるハイブリッド: Xcode 26 Compilation Cache ON+Job ごと DerivedData、sccache リモートバケットでネイティブヘルパー、SPM はロックファイルと専用キャッシュディレクトリでウォーム、リリース Job は暗黙期限ではなく明示「キャッシュ無効化」ステップ。
8. FAQ
Q1: DerivedData を消すと Compilation Cache と競合しますか?
必ずしも競合しません。DerivedData 削除はプロジェクト索引と増分状態に効き、別パスの Compilation Cache は読み取り専用シードとして残せます。1 本の rm -rf がモジュールキャッシュと Compilation Cache をまとめて消すと次はコールドスタート——Runbook に削除対象と保持対象を書いてください。
Q2: xcodebuild 全体を sccache でラップして安全ですか?
いいえ。xcodebuild ツリー全体を sccache に渡さないでください。CMake/Bazel/Rust サブターゲットの clang/swiftc だけラップし、Apple ターゲットは内蔵キャッシュと DerivedData 分離に任せます。
Q3: ヒット率は高いのに出力が再現しません
リモート共有を切り、Package.resolved と Xcode を固定し、SSH でクリーンビルドとキャッシュビルドを diff します。典型原因は SDK パス・マクロ・ブランチ名のハッシュ漏れによる誤ヒットです。リリースパイプラインには sccache バイパス(SCCACHE_DIRECT=1 等)をワンクリックで。
Q4: 並行 2 Job に Mac が 2 台必要ですか?
必須ではありませんが、書き込みディレクトリは分離が必要です。M4 Mac mini 1 台で 2 Job がぶつかるのは、読み取り専用 Compilation Cache より I/O と Simulator の争いが多いです。キュー同時実行上限の調整が、盲目的な台数増しより効くことがあります。
Q5: オブジェクトストレージ費用をどう説明しますか?
sccache バケット費用と「追加 Runner 分数削減」を 1 つの FinOps 表に並べます。リモートキャッシュはマシン横断の再コンパイル、Compilation Cache は単一マシンのフロントエンド再実行を削ります。層を誤ると、謎の緑ビルドとオンコール時間を買うだけ——マトリクスは費用と観測可能なヒットの対応表です。
三層キャッシュは SSH できる実機の上で初めて効く
Compilation Cache、sccache、DerivedData 方針は、最終的に24 時間コンパイルし SSH で切り分けできる Apple Siliconに載ります。Mac mini M4 は待機約 4W・静音筐体で机下やラックの常時稼働に向き、ネイティブ macOS+Xcode で「手元は速い、CI は遅い」ツールチェーン差を縮めやすいです。
ユニファイドメモリは大規模 iOS Job のコンパイル+テスト+Simulator 同時実行時のスワップを抑え、Gatekeeper・SIP・FileVault は無人ノードの既定セキュリティストーリーを支えます。明示的モジュール、SPM ロック、Job ごとディレクトリを Runbook に書いたなら、Mac mini はそのルールを守らせるハードウェア基準です。
Xcode 26 Compilation Cache とリモート sccache バケットを同時に展開し、ログインしやすい小さなホストで専用パイプライン全体を検証するなら、Mac mini M4 は 2026 年時点でも価格対性能の有力な出発点です——Runner 台数を増やす前に、実機でキャッシュ階層を確かめてください。