Swift Digest
SE-0209 | Swift Evolution

Package Manager Swift Language Version API Update

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

01 何が問題だったのか

Swift Package Manager では、SE-0151 で導入された swiftLanguageVersions プロパティによって、パッケージが対応する Swift の言語互換バージョンを Package.swift で宣言できるようになっていました。ただしこのプロパティの型は [Int] で、Swift 3 や Swift 4 のようにメジャー番号のみで表現できるバージョンしか扱えませんでした。

一方、Swift 4.2 からはコンパイラの -swift-version フラグが 4.2 のようなマイナー番号付きの値も受け付けるようになります。[Int] のままでは 4.2 を書き下す方法がなく、パッケージから「Swift 4.2 の言語モードに対応している」と宣言する手段がありません。

また、マニフェスト API は Swift tools version ごとにバージョニングされているため、将来さらに新しい言語バージョン(5 など)が登場するたびにプロパティの型側に手を入れる必要があり、古い tools version のパッケージからでも新しい言語バージョンを指定できる 仕組みがないと、言語バージョンと tools version を独立に上げていくのが難しいという課題もありました。

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

tools version 4.2 以降のマニフェスト API では、swiftLanguageVersions の型が [Int] から新しい SwiftVersion 列挙型の配列 [SwiftVersion] に変更されます。既知の言語バージョンは case として提供され、さらに任意のバージョン文字列を表現できる version(String) case も用意されます。

/// Represents the version of the Swift language that should be used for
/// compiling Swift sources in the package.
public enum SwiftVersion {
    case v3
    case v4
    case v4_2

    /// User-defined value of Swift version.
    ///
    /// The value is passed as-is to Swift compiler's -swift-version flag.
    case version(String)
}

version(String) に渡した文字列は、そのまま -swift-version フラグの値としてコンパイラに渡されます。これにより、現在の tools version のマニフェスト API がまだ知らない将来の言語バージョン(たとえば "5")についても、古い tools version を保ったまま宣言できます。パッケージが新しい言語バージョンへの対応を追加しつつ、古いツールチェーンとの互換も維持したいケースに向いた仕組みです。

バージョンの選択ルール

swiftLanguageVersions に複数の値が列挙されている場合、パッケージマネージャは 使用中の Swift コンパイラが受け付ける言語バージョンを超えない範囲で、もっとも新しい値 を選択します。バージョン番号の比較は標準的な数値ルールで、たとえば 5 > 4.2.1 > 4.2 > 4 となります。

swiftLanguageVersions が指定されていない場合は、マニフェストの tools version が言語バージョンとして使われます。

使用例

既存の [Int] スタイル(tools version 4.0 のマニフェスト)はそのまま使えます。

// swift-tools-version:4.0

import PackageDescription

let package = Package(
    name: "HTTPClient",
    // ...
    swiftLanguageVersions: [4]
)

tools version を 4.2 に上げると、新しい列挙型による記述に移行します。

// swift-tools-version:4.2

import PackageDescription

let package = Package(
    name: "HTTPClient",
    // ...
    swiftLanguageVersions: [.v4, .v4_2]
)

tools version 4.2 のマニフェストでは、将来の言語バージョンをカスタム値として併記することもできます。下の例でコンパイラが Swift 4.2 までしか受け付けない場合、"5" は無視され .v4 が選ばれます。

// swift-tools-version:4.2

import PackageDescription

let package = Package(
    name: "HTTPClient",
    // ...
    swiftLanguageVersions: [.v4, .version("5")]
)

なお、tools version 4.0 のマニフェストでは SwiftVersion 型そのものが存在しないため、.v4_2.version("5") を書くとパッケージマネージャはエラーを出します。これらの API を使うにはマニフェスト冒頭の // swift-tools-version: を 4.2 以上に上げる必要があります。

既存パッケージへの影響

tools version を 4.2 に上げない限り、既存パッケージに影響はありません。ただし tools version を 4.2 に更新したタイミングで swiftLanguageVersions を使っているパッケージは、[Int] から [SwiftVersion] への書き換えが必要になります。また、tools version を 4.2 に上げない限り、パッケージから Swift 4.2 言語モードを宣言することはできません。