Swift Digest
SE-0201 | Swift Evolution

Package Manager Local Dependencies

Proposal
SE-0201
Authors
Ankit Aggarwal
Review Manager
Boris Bügling
Status
Implemented (Swift 4.2)

01 何が問題だったのか

Swift Package Manager では、パッケージ間の依存関係は Git リポジトリの URL とバージョンで宣言するのが基本でした。つまり、あるパッケージを依存先として使うためには、そのパッケージが Git リポジトリになっている必要があり、ローカルのディレクトリをそのまま依存先として指定することはできませんでした。

この制約は、次のような状況で大きな摩擦を生みます。

  • 相互に依存する複数のパッケージを新規にセットアップしたいとき、まずそれぞれを Git リポジトリ化し、依存元のパッケージで swift package edit を実行して編集モードに入れる、という手順を踏む必要があります。プロトタイピングの段階でこの手間は重くのしかかります。
  • 複数のパッケージを 1 つのリポジトリ(モノレポ)にまとめて管理したい場合があります。たとえば、まだ設計が固まっておらず個別に公開したくないパッケージ群や、緩やかに関連する複数のパッケージをとりあえず同じ場所に置いておきたい、といったケースです。こうした用途も、従来の Git URL ベースの依存だけでは扱いにくいものでした。

要するに、「隣のディレクトリにあるパッケージをそのまま依存先として使いたい」というごく自然な要求に対して、Swift Package Manager には直接の手段が用意されていなかったのです。

02 どのように解決されるのか

PackageDescription モジュールの Package.Dependency に、ディスク上のパスを指定して依存を宣言するための新しい API を追加します。

extension Package.Dependency {
    public static func package(path: String) -> Package.Dependency
}

これにより、Package.swift の中で次のように、Git URL の代わりにローカルパスで依存先を指定できます。

// swift-tools-version:4.2
import PackageDescription

let package = Package(
    name: "MyApp",
    dependencies: [
        // 相対パスでも絶対パスでもよい
        .package(path: "../MyLibrary"),
    ],
    targets: [
        .target(name: "MyApp", dependencies: ["MyLibrary"]),
    ]
)

動作ルール

この API には、ローカル依存ならではの挙動がいくつか定められています。

  • path は有効な絶対パスまたは相対パスの文字列でなければならず、指し示す先が正しい Swift パッケージである必要があります。
  • ローカルパッケージはそのまま使われ、Swift Package Manager は一切の Git 操作を行いません。つまり、バージョンタグやブランチの概念は関与せず、現在のソースがそのままビルド対象になります。
  • ローカル依存を宣言できるのは、ルートパッケージか、他のローカル依存からのみです。通常のバージョン指定された依存パッケージがさらにローカル依存を宣言している、という構成はサポートされません。
  • パッケージグラフ内で同じパッケージ名を持つ通常の依存がある場合、ローカル依存がそれを上書きします。これは swift package edit の編集モードと同様の挙動で、開発中のローカルコピーを最優先で使う、という意図です。
  • ローカル依存は Package.resolved に記録されず、上書きが発生した場合は既存のエントリが削除されます。バージョン固定の対象にならないという点でも、編集モードに近い扱いです。
  • ローカル依存に対して編集モード(swift package edit)を使うことはできません。そもそも現物のパスをそのまま見ているため、さらに編集モードで切り離す意味がないからです。

利用できるツールバージョン

この API は、マニフェストの swift-tools-version が本提案の実装された Swift バージョン(Swift 4.2)以上である場合にのみ利用できます。古いツールバージョンのマニフェストでは使えない点に注意してください。

使いどころ

この機能は、次のような場面で特に役立ちます。

  • 相互に依存する複数のパッケージを立ち上げるプロトタイピング。まだリポジトリ化する前の状態でも、隣接するディレクトリを path: で参照しあうだけで動かし始められます。
  • モノレポで複数のサブパッケージを同居させたいケース。ルートパッケージから相対パスで各サブパッケージを指すことで、1 つのリポジトリ内で緩やかに連携させられます。
  • ライブラリとアプリを一緒に開発していて、ライブラリ側の変更をすぐにアプリから試したいとき。Git の push や編集モードを挟まずに、その場のソースコードをそのまま参照できます。