Swift Digest
Blog | Swift.org Blog

Swift Package Manager のマニフェスト API 再設計

Swift Package Manager Manifest API Redesign

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

この記事の要点

背景: Swift 3 の推論ルールは分かりにくかった

Swift 3 の Package Manager は、規約ベースの複雑なレイアウトルールと、ターゲットやその依存関係を自動推論する仕組みを持っていました。これは便利な反面、「どのディレクトリがどのターゲットになり、何に依存するのか」が暗黙的に決まるため、混乱の原因になっていました。

Swift 4 ではこの推論の大部分を取り除き、パッケージの構成をマニフェストに明示的に記述する方針へ切り替えました。あわせて、外部に公開するターゲットを product として明示的に書き出す API も導入されています。

tools version とマニフェストの選択

Swift 4 の Package Manager がどの形式のマニフェストとして解釈するかは、マニフェスト先頭の特別なコメント行で指定する tools version で決まります。

// swift-tools-version:4.0

依存解決の面でも互換性が保たれます。Swift 3 の Package Manager は Swift 4 形式のマニフェストを理解できないため、Swift 4 マニフェストを含む Git タグを自動的に無視し、Swift 3 マニフェストを含む最後のタグを選びます。一方、Swift 4 の Package Manager はマニフェスト形式に関わらず、利用可能な最新バージョンを選びます。

Swift 4 で新しいパッケージを作る

swift package init で新しいパッケージを作成します。

$ mkdir mytool && cd mytool
$ swift package init
$ swift build
$ swift test

生成される Package.swift は次のような形です。

// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "mytool",
    products: [
        .library(
            name: "mytool",
            targets: ["mytool"]),
    ],
    dependencies: [
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        .target(
            name: "mytool",
            dependencies: []),
        .testTarget(
            name: "mytoolTests",
            dependencies: ["mytool"]),
    ]
)

旧形式との主な違いは次の 3 点です。

  1. tools version を // swift-tools-version:4.0 の行で指定します。
  2. すべてのターゲットとその依存関係を 明示的に宣言 します。
  3. 公開するターゲットを、新しい product API で product として書き出します。Swift 4 のパッケージのターゲットは、他パッケージの product か、同じパッケージのターゲットに依存できます。

カスタムなターゲットレイアウト

新しいマニフェストでは、パッケージのレイアウトを自由に指定できます。複雑な規約ベースのレイアウトに従う必要はなくなり、ルールは 1 つだけです。ターゲットのパスを指定しない場合、SourcesSourcesrcsrcsTests の各ディレクトリがこの順で探索されてターゲットが見つけられます。

カスタムレイアウトのおかげで、C ライブラリを Swift Package Manager に取り込むのも容易になりました。たとえばソースが src ディレクトリに置かれている C ライブラリは、pathsources を指定して次のように書けます。

// swift-tools-version:4.0

import PackageDescription

let package = Package(
    name: "LibYAML",
    products: [
        .library(
            name: "libyaml",
            targets: ["libyaml"]),
    ],
    targets: [
        .target(
            name: "libyaml",
            path: ".",
            sources: ["src"])
    ]
)

特定のソースファイルだけを対象にしたり、公開ヘッダの場所を publicHeaders で指定したりもできます。

let package = Package(
    name: "http-parser",
    products: [
        .library(
            name: "httpparser",
            targets: ["http-parser"]),
    ],
    targets: [
        .target(
            name: "http-parser",
            publicHeaders: ".",
            sources: ["http_parser.c"])
    ]
)

既存パッケージを Swift 4 形式に更新する

既存のパッケージを Swift 4 マニフェスト形式へ移行する手順は次のとおりです。

tools version を更新する

tools-version サブコマンドで tools version を現在の値に設定します。

$ cd mypackage
$ swift package tools-version --set-current

依存関係の宣言を更新する

dependenciestargets より前に置き、依存関係の構文を新しい形式に直します。バージョン指定が majorVersion: から from:.upToNextMinor(from:) などに変わっています。

    dependencies: [
-    .Package(url: "https://github.com/apple/example-package-fisheryates.git", majorVersion: 2),
+    .package(url: "https://github.com/apple/example-package-fisheryates.git", from: "2.0.0"),

-    .Package(url: "https://github.com/apple/example-package-playingcard.git", majorVersion: 3, minor: 3),
+    .package(url: "https://github.com/apple/example-package-playingcard.git", .upToNextMinor(from: "3.3.0")),
    ]

すべてのターゲットを宣言する

通常のターゲットとテストターゲットの両方を、依存関係も含めて明示的に宣言します。

    targets: [
        .target(
            name: "Foo"),
        .testTarget(
            name: "FooTests",
            dependencies: ["Foo"]),
    ]

必要ならターゲットのパスを指定する

推奨レイアウトは、Sources の下にターゲットごとのディレクトリを置く(Sources/<ターゲット名>)形です。このレイアウトならパスは自動検出されます。それ以外のレイアウトでは path を指定します。

    targets: [
        .target(
            name: "Foo",
            path: "."), // ソースはパッケージのルートにある
        .target(
            name: "Bar",
            path: "Sources") // ソースは Sources/ にある
    ]

公開ターゲットを product として書き出す

ライブラリパッケージは、他パッケージから import できるよう公開ターゲットを明示的に書き出します。サンプルコード用ターゲットやテスト補助ライブラリなど、外部に見せる必要のないものは書き出さないようにします。

    products: [
        .library(
            name: "Foo",
            targets: ["Foo", "Bar"]),
    ],

Swift 3 互換モードでコンパイルする

ソースを Swift 4 へ書き換える前にマニフェストだけを新形式に更新したい場合は、swiftLanguageVersions3 に設定して Swift 3 互換モードでビルドします。

    swiftLanguageVersions: [3]

関連リンク