🔥

CIでSPMのキャッシュを行うのは難しい

に公開

Swift Package Managerの内部構造にはあまり詳しくなかったのですが、CI上でFirebaseのような大きなパッケージを毎回ビルドするのが非効率に感じたため、キャッシュに挑戦してみました。 しかし結論としては、うまくいきませんでした。

なぜうまくいかないのか

Bitriseには Save SPM Cache / Restore SPM Cache というステップがあります。これを見つけて「お、これでいけるのでは?」と思ってしまったのが落とし穴でした。

このステップは ~/Library/Developer/Xcode/DerivedData/**/SourcePackages を保存・復元対象にしています。

SourcePackages の中身を確認すると、artifactscheckoutsrepositories の3つのディレクトリがあります。

ざっくり言うと、Swift Package Managerは依存パッケージのソースコードを~/Library/Developer/Xcode/DerivedData/**/SourcePackagesに丸ごとチェックアウトして毎回ビルドする仕組みです(XCFrameworkは例外)

ローカルでのビルドが速く感じるのは、SPMがバイナリキャッシュを持っているからではなく、Xcodeの intermediates.noindex を使ったインクリメンタルビルドのおかげです。

つまり、そもそもSPMはCIでキャッシュできる仕様が現在ない(CarthageのバイナリだとかRomeのような仕組みがない)ので、CIでSPMのキャッシュを行うのは難しいということです。この仕様を理解していなかったため、Bitriseのステップに過剰な期待をしてしまいました。

最初に挙げた保存・復元ステップはリモートからのフェッチ速度を早めるためのもので、ビルド速度に貢献するものではありません。

それでもキャッシュしたい場合

どうしてもCIでSPMをキャッシュしたい場合、下記の記事のように DerivedData をまるごとキャッシュする方法もあります。

ただし、DerivedData は中間生成物のため、キャッシュすることでCI環境がどんどん汚れていくリスクがあります。
CIのメリットは「クリーンな環境で非同期にテストやビルドを実行できること」なので、それを犠牲にして速度を取るのは大きなトレードオフだと思います。

それでもビルド時間短縮が最優先であれば、上記のような方針を検討するのが現実的でしょう。

GitHubで編集を提案

Discussion