Swift Digest
SE-0236 | Swift Evolution

Package Managerのプラットフォームデプロイ設定

Package Manager Platform Deployment Settings

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

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

01 何が問題だったのか

Swift Package Manager(SwiftPM)は、パッケージが対応する最小のデプロイメントターゲット(minimum deployment target)を Package.swift マニフェストで宣言する方法を持っていませんでした。代わりに、macOS については SwiftPM 自身がハードコードしたバージョンが使われ、他のプラットフォームでは SDK が規定するデフォルトに任されるだけでした。

この制限は、ハードコードされたバージョンより後に追加された API を使いたいパッケージにとって大きな摩擦になります。回避策としては次の 2 つしかありませんでした。

  • コード中で if #available(...) によるアベイラビリティチェックを書いて、新しい API の利用箇所をその都度ガードする
  • ビルド時にコマンドラインからデプロイメントターゲットを渡す

前者は本来パッケージとして「このバージョン以上のみサポートする」と宣言したい場面でも、ファイル単位・式単位で条件分岐を書かなければならず冗長です。後者はパッケージ利用者側に正しいオプションを要求することになり、パッケージが単体で自分のサポート範囲を表現できていません。

また、依存パッケージと利用側パッケージのデプロイメントターゲットが食い違っているケース(たとえば依存側が利用側よりも新しいバージョンを要求する)に対しても、SwiftPM にはこれを検出・診断する仕組みがなく、食い違いはビルド時のエラーとして初めて表面化していました。パッケージマネージャとして、この種の互換性は本来マニフェストの情報から事前に検査できるべきものです。

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

PackageDescription に、対応プラットフォームごとの最小デプロイメントターゲットを宣言するための API を追加します。Package のイニシャライザに platforms: 引数が増え、SupportedPlatform の配列で指定します。

let package = Package(
    name: "NIO",
    platforms: [
        .macOS(.v10_13), .iOS(.v12),
    ],
    products: [
        .library(name: "NIO", targets: ["NIO"]),
    ],
    targets: [
        .target(name: "NIO"),
    ]
)

この例は、macOS では 10.13、iOS では 12.0 を最小デプロイメントターゲットとし、それ以外のプラットフォームについてはデフォルトのまま、という意味になります。

デフォルトの挙動と platforms の省略

platforms を指定しない、あるいは一部のプラットフォームのみを列挙した場合、列挙されていないプラットフォームについてはあらかじめ決められたデフォルトが使われます。デフォルト値は「インストールされている SDK がサポートする最も古いデプロイメントターゲット」です。ただし macOS については、この規則の例外として 10.10 を下限とします。

したがって、上の例のように macOS と iOS のみを明示したパッケージは、tvOS や watchOS、Linux など他のプラットフォームでは引き続きデフォルトのデプロイメントターゲットでビルドされます。

バージョンの指定方法

各プラットフォームの API には、あらかじめ用意された定数を取るオーバーロードと、文字列を取るオーバーロードの 2 種類があります。

extension SupportedPlatform.MacOSVersion {
    static let v10_10: MacOSVersion
    static let v10_11: MacOSVersion
    static let v10_12: MacOSVersion
    // ...
}

// 定数での指定
.macOS(.v10_13)

// 文字列での指定(ドット区切りの細かいバージョンや、
// PackageDescription にまだ定数が用意されていない新バージョンを指定したいとき)
.macOS("10.13.4")

文字列の書式は各プラットフォームの API ドキュメントに従います。不正な値が渡された場合はマニフェスト解析時のエラーとして診断されます。空の配列、同じプラットフォームの重複宣言など、意味的に無効な入力も同様にエラーになります。

依存パッケージとの整合性チェック

SwiftPM は、依存パッケージのデプロイメントターゲットが利用側パッケージのそれ以下であることを要求します。プラットフォームごとに、「依存側 ≤ 利用側」が成り立たない場合はエラーとして報告されます。

各パッケージはそれぞれ自身が宣言したデプロイメントターゲットでコンパイルされます。原理的には、互換性が保証されている以上、グラフ全体をトップレベルパッケージのデプロイメントターゲットでコンパイルすることもできますが、そうすると依存側のコードで「より新しい OS 向けにビルドしている」ことによる警告が多数出る可能性があるため、各パッケージ固有の値が使われます。

また、デプロイメントターゲットの変更は、利用側パッケージのビルドを壊しうる変更なので、セマンティックバージョニング上はメジャーバージョンの変更として扱うべきです。

Xcode プロジェクトとの連携

swift package generate-xcodeproj で生成される Xcode プロジェクトには、宣言したプラットフォームごとのデプロイメントターゲットがそのまま反映されます。

既存パッケージへの影響

platforms を指定しない既存パッケージの挙動は、これまでと変わりません(macOS は SwiftPM が選んだデフォルト、その他は SDK のデフォルト)。この新 API は対応する Swift tools version でガードされるため、新機能を使いたいパッケージはマニフェストの // swift-tools-version:... を該当バージョン以上に引き上げる必要があります。

03 今後の見通し

この提案で導入される platforms: API は、まず Apple プラットフォームを対象に最小デプロイメントターゲットを宣言できるようにすることをスコープとしています。以下は元 Proposal で言及されている発展方向であり、いずれもこの提案では扱われず、別提案として将来的に検討される可能性のあるものです。実現を約束するものではありません。

非 Apple プラットフォームへの対応

Swift コンパイラは macOS や iOS のほかに Linux、Windows、Android などもサポートしていますが、ランタイムのアベイラビリティチェックは現状 Apple プラットフォームでしか機能しません。platforms: で宣言できるプラットフォームも、これに合わせてまずは Apple プラットフォームに限定されています。コミュニティの需要や Swift コンパイラ側のサポート状況に応じて、Windows の最小バージョンや Android の API レベルを宣言できるよう API を拡張していくことが想定されています。

サポートするプラットフォームの制限

特定のプラットフォーム専用パッケージを表現するために、対応プラットフォームそのものを絞り込みたいという要望があります。この提案ではあくまで「サポートするプラットフォームのデプロイメントターゲットを宣言する」までを扱い、「このプラットフォームではビルドさせない」という制限は別の課題として切り分けられています。

ターゲット/プロダクト/依存のプラットフォーム別指定

「この target は Linux でのみビルドしたい」「この product は特定プラットフォーム専用」「この依存パッケージは特定プラットフォームでのみ使いたい」といった、より細かい粒度のプラットフォーム別設定も今後の課題です。現状はソースコード側の #if や、target を別パッケージに切り出すといった回避策で対応するしかありません。マニフェスト側で表現できるようにするには target レベルの設定機構などが必要になるため、これも別提案として検討されます。