Swift Digest
SE-0208 | Swift Evolution

Package Manager System Library Targets

Proposal
SE-0208
Authors
Ankit Aggarwal, Daniel Dunbar
Review Manager
Boris Bügling
Status
Implemented (Swift 4.2)

01 何が問題だったのか

Swift Package Manager には、システムにインストール済みのライブラリ(zlib など OS やパッケージマネージャで提供されるネイティブライブラリ)を Swift から使えるように橋渡しする system-module package という仕組みがあります。しかし、この仕組みは パッケージ単位 でしか宣言できませんでした。

その結果、あるライブラリをラップする Swift パッケージを作りたい場合でも、

  • システムライブラリ用の独立したリポジトリ(CZLib など)
  • それを依存として使う、実際の Swift パッケージ(ZLib など)

という二つのリポジトリを用意しなければならず、一つのパッケージに必要な部品を同梱することができませんでした。

当初は「システムパッケージは独立したリポジトリとして公開してもらい、名前・URL・メンテナを標準化していこう」という意図があったのですが、実際には標準化はうまく進まず、結果として Package Manager が使いにくくなるだけになっていました。また、システム依存のためだけに別リポジトリを切る運用上のコストも無視できませんでした。

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

システムライブラリへの橋渡し情報を パッケージ単位ではなくターゲット単位 で指定できる、system library target という新しいターゲット種別が追加されます。これにより、システムライブラリのアダプタと、それを利用する Swift ライブラリを 同じパッケージ内 に同居させられるようになります。

新しいターゲットファクトリ

TargetsystemLibrary ファクトリが追加されます。

extension Target {
    public static func systemLibrary(
        name: String,
        path: String? = nil,
        pkgConfig: String? = nil,
        providers: [SystemPackageProvider]? = nil
    ) -> Target
}

pkgConfig には pkg-config 名、providers にはプラットフォーム別のインストール手段(Homebrew、apt など)を指定します。提供する情報は従来の system-module package と同じで、宣言の場所がパッケージからターゲットに変わっただけです。

使用例: システムライブラリだけを公開するパッケージ

zlib をアダプトするだけのパッケージは次のように書けます。

// swift-tools-version:4.2
import PackageDescription

let package = Package(
    name: "CZLib",
    products: [
        .library(name: "CZLib", targets: ["CZLib"]),
    ],
    targets: [
        .systemLibrary(
            name: "CZLib",
            pkgConfig: "zlib",
            providers: [
                .brew(["zlib"]),
                .apt(["zlib"]),
            ]
        )
    ]
)

使用例: Swift インタフェースとアダプタを同梱する

従来は別リポジトリに切り出さざるをえなかった「Swift で使いやすくラップしたライブラリ + システムライブラリアダプタ」の組み合わせを、一つのパッケージにまとめられます。

// swift-tools-version:4.2
import PackageDescription

let package = Package(
    name: "ZLib",
    products: [
        .library(name: "ZLib", targets: ["ZLib"]),
    ],
    targets: [
        .target(
            name: "ZLib",
            dependencies: ["CZLib"]),
        .systemLibrary(
            name: "CZLib")
    ]
)

この例では CZLib は product として公開されていないため、外部のパッケージからは見えず、ZLib の実装詳細として閉じ込められています。

依存関係の扱い

従来の system-module package はパッケージとして依存に加えるだけで、暗黙にターゲットから参照される形になっていました。system library target では、他のターゲット/product の扱いと揃えるため、利用側のターゲットから明示的に dependencies として指定する必要があります

system library target をパッケージ外に公開したい場合は、その system library target を 唯一のメンバ とする library product として export します。

旧仕組みの扱い

tools version がこの機能をサポートする Swift バージョン以降のパッケージでは、従来の system-module package は非推奨となります。依存解決時に、依存グラフに旧来の system-module package が含まれていると、ルートパッケージに対して deprecation warning が出るようになります。移行するまで動かなくなるわけではありませんが、新規に書く場合は system library target を使うことが推奨されます。