モジュールキャッシュ
- 生成されたプロジェクトが必要です。モジュールキャッシュは、
Project.swiftの背後で Tuist が保持するプロジェクトグラフを読み取り、ターゲットをハッシュ化してバイナリに置き換えます。
Tuist Module Cache は、モジュールをバイナリ(.xcframework)としてキャッシュし、さまざまな環境で共有することで、ビルド時間を最適化する強力な手段を提供します。この機能により、以前に生成されたバイナリを活用できるため、繰り返しコンパイルする必要が減り、開発プロセスが加速します。
モジュールキャッシュと Xcode キャッシュ は異なる粒度レベルで動作するため、相互補完的な関係にあります。モジュールキャッシュはビルド実行前にモジュール全体を事前ビルド済みの .xcframework に置き換え、Xcode キャッシュはビルド中にコンパイル出力を再利用します。
コンパイルキャッシュの設定はモジュールキャッシュのハッシュに含まれないため、Xcode キャッシュの有効・無効を切り替えても、すでにウォームアップ済みのバイナリは維持されます。Xcode 27 以降では、それに伴うプレフィックスマッピング設定もハッシュ化されます。モジュールキャッシュのハッシュを参照してください。
ウォームアップ#
Tuist は依存関係グラフ内の各ターゲットに対して ハッシュを効率的に利用して変更を検出します。このデータを利用して、これらのターゲットから派生したバイナリをビルドし、一意の識別子を割り当てます。グラフ生成時に、Tuist は元のターゲットを対応するバイナリ版にシームレスに置き換えます。
この操作は 「ウォームアップ」 と呼ばれ、ローカルでの使用や、Tuist を介してチームメンバーや CI 環境と共有するためのバイナリを生成します。キャッシュのウォームアッププロセスは簡単で、シンプルなコマンドで開始できます。
tuist cache
このコマンドはバイナリを再利用して処理を高速化します。
バイナリのアップロードに失敗した場合でも、コマンドは残りのアップロードを実行し、その後非ゼロのステータスで終了して、アップロードされなかったターゲットを一覧表示します。マシンのローカルキャッシュにあるバイナリはキャッシュ済みとして扱われるため、同じマシンから再度アップロードするには、ウォームアップの前に tuist clean binaries を実行してください。バイナリをローカルキャッシュにのみ保存するには、--no-upload を指定します。
デスティネーションの選択#
単一のデスティネーションに対してキャッシュ、生成、テストを行うには、--destination simulator または --destination device を使用します。
tuist cache --destination simulator
tuist generate --destination simulator
tuist test --destination simulator
tuist cache --destination device
tuist generate --destination device
tuist test --destination device -- -destination 'platform=iOS,id=DEVICE_IDENTIFIER'
デフォルトは all で、Mac Catalyst を含む、ターゲットがサポートするすべてのデスティネーションを維持します。後続の tuist cache はキャッシュ済みのスライスを再利用し、欠けている SDK だけをビルドします。通常の生成では、必要なスライスが欠けている場合、モジュールのソースを保持します。
制限付き生成では、ソースターゲットを選択したデスティネーションに限定し、マクロを含むネイティブ macOS ターゲットを維持します。いずれの制限モードにも Mac Catalyst は含まれません。すべてのデスティネーションを復元するには、--destination all で再生成してください。
tuist test --destination は選択したデスティネーションのスライスだけをダウンロードして使用するため、シミュレーターのテスト実行は、シミュレーター専用のウォームアップまたは完全なウォームアップからのバイナリを使用できます。-- の後に xcodebuild に渡されるデスティネーションが一致しない場合は失敗し、--destination device にはデバイスの -destination が必要です。選択的テストのハッシュはデスティネーションに依存しないため、異なる --destination 値での実行はテスト結果を共有します。
スライスごとのキャッシングは、標準アーキテクチャの iOS と macOS をサポートします。他のプラットフォーム、カスタムアーキテクチャ、およびサポートされていない製品は、完全なアーティファクトキャッシングを維持します。レガシーモジュールキャッシングは常に完全なアーティファクトをダウンロードし、ウォームアップ時に --destination all が必要です。
リモートアップロードの受付#
最新のモジュールキャッシングは REAPI を使用します。混雑したキャッシュが RESOURCE_EXHAUSTED でアップロードを一時的に拒否した場合、Tuist はジッター付きでバックオフし、標準の google.rpc.RetryInfo 最小遅延を尊重し、制限された待機予算内で再試行します。アップロード受付では、呼び出しごとに最大 6 回の再試行が許可されます。各 blob アップロード操作は、存在チェック、バッチアップロード、ストリーミング書き込み全体で 1 つの 60 秒の累積バックオフ予算を共有します。同時待機はそれぞれ予算を消費するため、繰り返しの拒否によってキューに入ったすべての blob にさらに 1 分間の再試行時間が与えられるわけではありません。正常な転送はこの予算を消費せず、予算が尽きた場合でもキャンセルされません。アクション結果の公開には独自の制限された予算があります。指数関数的な遅延は 1 秒から始まり、比例ジッターの前に 30 秒に向かって増加します。サーバーが必要とする遅延が残りの予算を超えた場合、Tuist は指示より早く再試行するのではなく、失敗します。キャンセルはバックオフを中断し、キャッシュ読み取りは短い再試行を維持するため、過負荷によって生成が停滞することはありません。
これらのデフォルトは、再試行ヒントのない古い Kura バージョンにも役立ちます。長い再試行は一時的な競合を吸収しますが、持続的な過負荷を吸収するものではありません。拒否が続く場合は、ウォームアップジョブの キャッシュ並行制限 を減らすか、サーバー容量を増やしてください。ポッドメモリの見積もりと受付メトリクスについては、Kura アップロードサイジング を参照してください。
構成の選択#
--configuration を指定せずにキャッシュをウォームアップする場合、Tuist は以下の順序で使用するビルド構成を選択します。
- マニフェストの
Project.Options.generationOptionsで設定されたdefaultConfiguration(存在する場合)。 - それ以外の場合、変数
debugの最初のビルド構成を名前のアルファベット順で選択します。
特定の構成に対してキャッシュをウォームアップするには、明示的に指定します。
tuist cache --configuration Release
使用方法#
デフォルトでは、Tuist コマンドがプロジェクト生成を必要とする場合、利用可能な依存関係をキャッシュからバイナリ版に自動的に置き換えます。さらに、焦点を当てるターゲットのリストを指定すると、Tuist は利用可能な場合、依存するターゲットもキャッシュされたバイナリに置き換えます。異なるアプローチを好む場合は、特定のフラグを使用してこの動作を完全にオプトアウトするオプションがあります。
tuist generate # Only dependencies
tuist generate Search # Dependencies + Search dependencies
tuist generate Search Settings # Dependencies, and Search and Settings dependencies
tuist generate --cache-profile none # No cache at all
tuist test
バイナリキャッシングは、シミュレーターやデバイスでアプリを実行したり、テストを実行したりするような開発ワークフロー向けに設計された機能です。リリースビルド用ではありません。アプリをアーカイブする際は、--cache-profile none を使用してソースを含むプロジェクトを生成してください。
キャッシュプロファイル#
Tuist は、プロジェクト生成時にターゲットをキャッシュされたバイナリにどの程度積極的に置き換えるかを制御するためのキャッシュプロファイルをサポートしています。
- 組み込み:
only-external: 外部依存関係のみを置き換える(システムのデフォルト)all-possible: 可能な限り多くのターゲットを置き換える(内部ターゲットを含む)none: キャッシュされたバイナリに決して置き換えない
tuist generate で --cache-profile を使用してプロファイルを選択します。
# Built-in profiles
tuist generate --cache-profile all-possible
# Custom profiles (defined in Tuist Config)
tuist generate --cache-profile development
# Use config default (no flag)
tuist generate
# Focus on specific targets (implies all-possible)
tuist generate MyModule AnotherTarget
# Disable binary replacement entirely
tuist generate --cache-profile none
--no-binary-cache フラグは廃止予定です。代わりに --cache-profile none を使用してください。廃止予定のフラグは後方互換性のため引き続き動作します。
有効な動作を解決する際の優先順位(高い順から低い順):
--cache-profile none- ターゲットの焦点(
generateにターゲットを渡す) → プロファイルall-possible --cache-profile <value>- 設定のデフォルト(設定されている場合)
- システムのデフォルト(
only-external)
サポートされている製品#
以下のターゲット製品のみが Tuist によってキャッシュ可能です:
- フレームワーク(静的および動的)。XCTest または Swift Testing に依存するテストサポートフレームワークを含む
- ライブラリ(静的および動的)。XCTest または Swift Testing に依存するテストサポートライブラリを含む
- バンドル
- Swift マクロ
テストバンドルはモジュールキャッシュから除外されます。つまり、unitTests および uiTests ターゲットはバイナリとしてキャッシュされませんが、テストが依存する通常のフレームワークおよびライブラリターゲットは、XCTest や Swift Testing をリンクしている場合でもキャッシュできます。
キャッシュされたライブラリの .xcframework は、生成されたプロジェクトがそれらをインポートするために必要なメタデータ(Swift モジュールや、ソースターゲットがパブリックヘッダーを宣言している場合はパブリックな C/Objective-C ヘッダーを含む)を保持します。
ターゲットがキャッシュ不可の場合、その上流のターゲットもキャッシュ不可になります。例えば、A が B に依存する依存関係グラフ A > B がある場合、B がキャッシュ不可であれば、A もキャッシュ不可になります。
アナリティクス#
プロジェクトのダッシュボードで モジュールキャッシュ → モジュール を開き、頻繁にミスが発生しているモジュールを見つけます。CI など、改善したい環境と日付範囲を選択します。概要にはキャッシュヒット、ミス、ミスのあるモジュールが表示され、モジュールを開くとその履歴と各観測に割り当てられた理由が表示されます。
ミス ドロップダウンでは、1つの理由に対するカウントと説明を選択します。チャートには4つの理由すべての分布が表示されます。モジュールのページでは、履歴の 理由 フィルターを使用して個々の発生を確認できます。理由バッジにカーソルを合わせるか、キーボードでフォーカスすると定義が表示されます。
ミスの理由#
| 理由 | 意味 |
|---|---|
| 変更 | モジュール自身の比較対象入力に変更がありました。これにはファイルハッシュ、ビルド設定、解決された構成、コンパイラ識別子が含まれます。ソースの編集はその可能な原因の1つにすぎません。 |
| 上流 | モジュール自身の比較対象入力は同じままでしたが、その依存関係または外部パッケージのハッシュが変更されました。モジュールのソースファイルは変更されていない可能性があります。 |
| コールド | 比較対象となる以前のモジュール観測が存在しないか、報告された入力がミスを説明しておらず、以前のリモート利用可能性を示す十分な証拠がありません。コールドだからといって、モジュールが一度もキャッシュされなかったことを証明するものではありません。 |
| 削除 | 正確なキャッシュキーは以前、同じプロジェクトの同じ記録されたキャッシュエンドポイントでリモートヒットしていましたが、現在はミスしています。 |
削除 とラベル付けされたミスの場合、アーティファクトは以前にリモートキャッシュからダウンロードされましたが、今回は再利用できませんでした。削除が主要原因ですが、アクセスまたはダウンロードの失敗もミスの原因となり得ます。Tuist は以前のリモートヒットが見つかった場合にのみこのラベルを割り当てます。以前のミスやローカルヒットだけでは不十分です。
例:
| シナリオ | 分類 |
|---|---|
| 以前の観測や条件を満たすリモートヒットなしでモジュールがミスする | コールド |
| A が変更; B は A に依存し、C は B に依存; 3つのキーすべてが変更 | A は変更; B と C は上流 |
| ウォームアップ後にコンパイラバージョンが変更され、ソースと設定は unchanged | 両方の観測がコンパイラ入力を報告する場合、影響を受けたミスは変更として扱われる |
| 正確なキーがリモートでダウンロードされ、その後アーティファクトが削除された後にミスする | 以前のヒットが証拠として条件を満たす場合、削除 |
| キーが繰り返しミスし、ウォームアップが一度も成功しなかった | コールドのままとなる可能性があります |
理由は観測結果を説明するものであり、キーに付与される永続的なラベルではありません。例えば、コンパイラのアップグレード後の最初のミスは「変更」となる可能性がありますが、その新しいキーに対する後のミスがリモートで利用可能であると観測されたことがなければ、「コールド」となる可能性があります。
キャッシュヒット率の向上#
- 一貫したベースラインを選択します。 CI と代表的な日付範囲から始めます。モジュールをミスの数でソートし、ヒット率、依存先、ビルドコストを検討します。頻繁にミスする高コストな依存関係を修正することは、小さなモジュールのヒット率を向上させるよりも時間を節約できる場合があります。変更を加えた後、同じ環境と類似したワークロードを比較してください。
- 「変更」ミスを調査します。 関連する実行を開き、完全なキーと報告された入力を比較します。ウォームアップジョブと消費ジョブを、意図した Xcode/コンパイラバージョン、構成、およびターゲット宛先に合わせます。意図的な変更 후에는再度ウォームアップを行います。変動しやすい生成ファイルや環境依存の設定が、 otherwise 安定しているモジュールを無効にする場合は、それらの入力を調査します。バイナリ互換性に影響する入力はハッシュに残してください。
- 「上流」ミスを追跡して変更された依存関係を見つけます。 その履歴を検査して直接の変更を探します。結果として生じるキーをウォームアップすることで再利用を回復できます。頻繁に変更される実装が高コストな多くの依存先を無効にする場合は、効率性 で説明されているように、より小さなモジュールや安定したインターフェースを検討してください。
- 「コールド」ミスのウォームアップカバレッジを確認します。 ウォームアップが必要なモジュールを選択し、消費ジョブの構成と環境を使用し、アーティファクトを正常にアップロードしていることを確認します。実際の CI ジョブで使用されている完全なキーを確認します。ウォームアップと消費で異なるキーが要求されている場合は、キャッシュが削除されたと仮定する前にハッシュ入力を検査してください。モジュールのソースが変更されていなくても、以前に正常なアップロードがない状態で繰り返しミスが発生する可能性があります。
- 「削除」ミスの証拠を活用します。 消費実行のキーとエンドポイントを確認し、消費実行のキャッシュ警告とウォームアップジョブのアップロード結果を検査します。該当する場合は保持または削除を確認します。必要なアーティファクトを再ウォームアップし、後続のヒットを確認します。それでもミスする場合は、関連する実行リンクとログをサポートチームと共有してください。
ハッシュ比較のために、ウォームアップと消費を意図した構成を使用して、各関連環境でこれを実行します:
tuist hash cache --configuration Debug --verbose
モジュールの完全なハッシュとコンポーネントブロックを比較します。同じ変更されていないマシンでコマンドを繰り返すと、通常は同じ結果が得られます。不一致を調査するために、実際のウォームアップ環境と消費環境を比較してください。どちらのコマンドもキャッシュにヒットする必要はありません。さらなる確認については ハッシュ診断 を参照してください。
効率性#
バイナリキャッシングで達成できる効率性のレベルは、グラフ構造に大きく依存します。最適な結果を得るために、以下をお勧めします:
- 非常にネストされた依存関係グラフを避けます。グラフが浅いほど良いでしょう。
- 実装ターゲットではなくプロトコル/インターフェイスタゲットで依存関係を定義し、最上位のターゲットから実装を依存性注入します。
- 頻繁に変更されるターゲットを、変更の可能性が低いより小さなターゲットに分割します。
上記の提案は モジュラーアーキテクチャ の一部であり、バイナリキャッシングだけでなく Xcode の機能の利点も最大化するためにプロジェクトを構造化する方法として提案しています。
推奨設定#
キャッシュをウォームアップするために、main ブランチのすべてのコミットで実行 される CI ジョブを用意することをお勧めします。これにより、キャッシュには常に main の変更に対するバイナリが含まれ、ローカルおよび CI ブランチビルドがそれに基づいて増分的に構築されます。
生成されたワークスペースに依存する後続のステップなしで、専用の CI ステップで tuist cache を実行します。tuist cache はキャッシュビルドの目的でワークスペースを変更するため、ワークスペースを必要とする CI ステップは、まず tuist generate を実行して新しく使用可能なワークスペースを取得する必要があります。
tuist cache コマンドは、ウォームアップを高速化するためにバイナリキャッシュも利用します。
以下は一般的なワークフローの例です:
開発者が新機能の作業を開始する#
mainから新しいブランチを作成します。tuist generateを実行します。- Tuist は
mainから最新のバイナリを取得し、それらを使用してプロジェクトを生成します。
開発者が変更を上流にプッシュする#
- CI パイプラインは
xcodebuild buildまたはtuist testを実行してプロジェクトをビルドまたはテストします。 - ワークフローは
mainから最新のバイナリを取得し、それらを使用してプロジェクトを生成します。 - その後、プロジェクトを増分的にビルドまたはテストします。
構成#
キャッシュ同時実行制限#
ダウンロードとアップロードの両方を制限するために、TUIST_CACHE_CONCURRENCY_LIMIT を正の整数に設定します。例えば、ウォームジョブでの小規模なセルフホスト型キャッシュへの負荷を軽減します:
TUIST_CACHE_CONCURRENCY_LIMIT=2 tuist cache
設定されていない場合、REAPI アップロードは 8 つの転送タスクを使用します。ダウンロードは、任意のブロブが ByteStream を必要とする場合は 8 を、バッチのみのプランの場合は 32 を使用します。レガシー HTTP キャッシュパスのデフォルトは 100 です。このデフォルトは REAPI には適用されません。none など、以前に文書化されていたものを含む無効な値は、制限を解除するのではなくデフォルトを維持します。デフォルトに戻すには unset TUIST_CACHE_CONCURRENCY_LIMIT を使用してください。
制限は転送操作ごとに適用され、操作全体や CI ジョブ全体にグローバルに適用されるわけではありません。制限が 2 の場合、3 つの同時ウォームアップで同じキャッシュに対して 6 つのアップロードがアクティブになる可能性があります。消費者ジョブは変数を未設定のままにして、デフォルトのダウンロード並列性を維持できます。
キャッシュウォームアップ用スクラッチディレクトリ#
デフォルトでは、tuist cache はビルド中間ファイルと組み立てられたキャッシュアーティファクト用の一時スクラッチディレクトリを作成し、キャッシュウォームアップが完了するとそれを削除します。これらのファイルを管理下のディレクトリに保持するには、TUIST_CACHE_WARM_SCRATCH_DIRECTORY を設定します。
パスは絶対パスでも、現在の作業ディレクトリからの相対パスでも構いません。Tuist はディレクトリが存在しない場合に作成します。すでに存在する場合は、空のディレクトリである必要があります。
この環境変数が設定されている場合、Tuist はコマンド終了後もディレクトリとその内容をそのまま残します。次のキャッシュウォームアップの前にクリーンアップするのは呼び出し側の責任です。変数が未設定の場合、Tuist は引き続き一時ディレクトリを使用し、削除します。
外部のビルドターゲットをウォームアップする必要がある場合、Tuist は呼び出し側が所有するスクラッチディレクトリを拒否します。外部のビルドスクリプトは独自の出力場所を制御するため、Tuist はそれらの出力がスクラッチディレクトリ内に留まることを保証できません。
コードカバレッジ#
モジュールキャッシュを使用したコードカバレッジは早期アクセス機能です。TUIST_FEATURE_FLAG_COVERAGE=1 が設定されている場合のみ、コマンドは以下で説明する計測済みバイナリの読み取りとウォームアップを行います。
モジュールキャッシュからのターゲットは事前構築されたバイナリであり、テスト実行はそのバイナリがカバレッジカウンター付きでビルドされた場合にのみコードを計測します。Tuist.swift でプロジェクトのカバレッジを有効にしてください:
import ProjectDescription
let tuist = Tuist(
project: .tuist(
generationOptions: .options(
coverage: .coverage(enabled: true)
)
)
)
enabled を使用すると、すべてのコマンドと環境がプロジェクト独自のターゲット向けにキャッシュの計測済みバリアントを1つ読み取るため、CIでの単一のウォームアップがCIマシンとローカルマシンの両方で利用できます:
| コマンド | プロジェクトのターゲット | 外部パッケージ |
|---|---|---|
tuist cache (Debug 構成) | 計測済みバイナリをウォームアップ | 通常バイナリをウォームアップ |
tuist cache (Release 構成) | 通常バイナリをウォームアップ | 通常バイナリをウォームアップ |
tuist hash cache | 計測済みキーを表示 | 通常キーを表示 |
tuist generate | 計測済みバイナリを読み取り | 通常バイナリを読み取り |
tuist test | 計測済みバイナリを読み取り | 通常バイナリを読み取り |
このオプションは、Xcode が Debug として扱うすべての構成に適用されます。Release ビルドは決して計測されません。Tuist はこれらの構成でプロファイルランタイムもリンクするため (CLANG_COVERAGE_MAPPING_LINKER_ARGS)、カバレッジを収集しないビルドでも計測済みバイナリがリンクされ、自動的に生成されるスキーマおよび coverage が未設定のまま定義されたスキーマのテストアクションでカバレッジが有効になります。
定義したスキーマは、自身でカバレッジを設定していない限りオプションに従います。.options(coverage: false) はオフのままにし、.options(coverage: true) はオプションの設定に関わらずオンのままにします。テストプランは独自にカバレッジを決定し、-enableCodeCoverage は Xcode と同様に実行を上書きします。カバレッジを収集せずに計測済みバイナリを読み取る実行は、通常通りビルドおよびテストを行います。実行がカバレッジを収集しているにもかかわらず、プロジェクトのターゲットが通常バイナリとしてキャッシュから提供された場合、Tuist はカバレッジが計測されていないことを警告します。
TUIST_COVERAGE_ENABLED 環境変数 (1 または 0) は coverage: .coverage(enabled:) より優先されます。キャッシュをウォームアップするコマンドと読み取るコマンドが一貫するように、単一のコマンドではなく CI ワークフローなどの環境全体に対して設定してください。
prefixMapping (Xcode キャッシュ を参照) には独自のオーバーライドがあります。TUIST_COVERAGE_PREFIX_MAPPING 環境変数 (1 または 0) は coverage: .coverage(prefixMapping:) より優先されます。
オフにオーバーライドすると通常バリアントが読み取られますが、カバレッジ有効でウォームアップされたキャッシュはこのバリアントを満たさないため、これらのターゲットはソースからビルドされます。Release ウォームアップは常に通常バイナリをウォームアップします。
enabled と prefixMapping は、プロジェクトのターゲットのビルド設定とキャッシュキー、およびテストターゲットの選択的テストハッシュを変更します。キャッシュを共有するすべてのマシンで同じ値に設定し、環境 (例えば Environment.isCI など) から導出しないでください。CI とローカルマシンで値が異なると、異なるキーが生成されます。環境変数についても同様です。TUIST_COVERAGE_ENABLED=0 で実行すると、カバレッジ有効の実行によるバイナリや選択的テストの結果を再利用しません。
留意点:
- 計測済みバイナリは大きくなります。静的アーカイブと動的ライブラリでは約 20〜30% です。Debug ビルドの実行速度は同じです。
- Xcode のカバレッジレポートは、このオプションの有無にかかわらず、キャッシュから提供されたコードを表示しません。Tuist のカバレッジアップロードはこれを計測します。Xcode でターゲットのカバレッジを確認するには、
tuist generate <target>で対象を絞り込み、ソースからビルドさせてください。
トラブルシューティング#
私のターゲットにバイナリが使用されない#
環境や実行間で ハッシュが決定的 であることを確認してください。これは、プロジェクトが絶対パスなどを通じて環境への参照を持っている場合に発生することがあります。diff コマンドを使用して、tuist generate の連続した2回の呼び出しによって生成されたプロジェクト、または異なる環境や実行間で生成されたプロジェクトを比較できます。
また、ターゲットが キャッシュ不可のターゲット に直接的または間接的に依存していないことも確認してください。
シンボルが見つからない#
ソースを使用する場合、Xcode のビルドシステムは Derived Data を通じて、明示的に宣言されていない依存関係を解決できます。しかし、バイナリキャッシュに依存する場合、依存関係は明示的に宣言する必要があります。そうしないと、シンボルが見つからない際にコンパイルエラーが発生する可能性があります。これをデバッグするには、tuist inspect dependencies --only implicit コマンドを使用し、暗黙的なリンクにおける回帰を防ぐために CI で設定することを推奨します。