Package Manager Swift Language Compatibility Version
01 何が問題だったのか
Swift コンパイラには -swift-version フラグによる「言語互換バージョン(language compatibility version)」の仕組みがあり、新しいコンパイラでも過去のメジャーバージョン(Swift 3 や Swift 4 など)のソース互換モードでビルドできるようになっています。ところが、Swift Package Manager 側にはこのフラグをパッケージごとに指定する手段がありませんでした。
そのため、Swift 4 のツールチェーンがリリースされたときに、既存の Swift 3 向けパッケージを Swift 3 言語モードのままビルドしたり、あるパッケージが「Swift 3 と Swift 4 の両方で使える」ことを宣言したりする方法がなく、エコシステムが新旧コンパイラをまたいで徐々に移行していくことが難しい状況でした。
02 どのように解決されるのか
Package マニフェストに swiftLanguageVersions という新しい引数を追加し、パッケージがサポートする Swift 言語のメジャーバージョンを整数の配列で宣言できるようにします。
let package = Package(
name: "HTTP",
// ...
swiftLanguageVersions: [3, 4])
ビルド時の言語バージョン選択
パッケージマネージャは、実行中のコンパイラのメジャーバージョンを超えない範囲で、宣言された最大の言語バージョンを選び、-swift-version に渡します。互換性のあるバージョンが宣言にひとつも無い場合はエラーです。
具体的な挙動は次のとおりです。
- Swift 3 コンパイラ ×
[3]のパッケージ →-swift-version 3でビルド - Swift 3 コンパイラ ×
[4]のパッケージ → エラー(互換モードが無い) - Swift 3 コンパイラ ×
[3, 4]のパッケージ →-swift-version 3でビルド - Swift 4 コンパイラ ×
[3]のパッケージ →-swift-version 3でビルド - Swift 4 コンパイラ ×
[4]のパッケージ →-swift-version 4でビルド - Swift 4 コンパイラ ×
[3, 4]のパッケージ →-swift-version 4でビルド(ツール側のメジャーバージョンに合わせる)
なお -swift-version 3 は「Swift 3 のコードを受け入れる」モードであって「Swift 3 コンパイラが拒否するコードを必ず拒否する」モードではないため、厳密な Swift 3 互換性を検証したい場合は実際の Swift 3 コンパイラでもビルドすることが推奨されます。
省略時のデフォルト
swiftLanguageVersions を省略したパッケージは、別提案で導入される「Swift tools version」のメジャーバージョンに従います。たとえば tools version が 3.x なら Swift 3、4.x なら Swift 4 が既定になります。これにより、既存の Swift 3 向けパッケージは変更なしで Swift 4 のツールチェーンからも Swift 3 モードでビルドされ、Swift 4 へ移行したい時点で tools version を上げるか swiftLanguageVersions を明示するかを選べます。
swift package init で Swift 4 ツールから新規作成されたパッケージは、採用される tools version の関係で Swift 4 言語がデフォルトになります。
互換性上の注意
swiftLanguageVersions は新しいマニフェスト API なので、これを使うパッケージは、この API を認識しない古いパッケージマネージャではビルドできなくなります。Swift 3.1 でもこの API がサポートされる予定のため、[3, 4] のように宣言して Swift 3.1 ツール・Swift 4 ツールの双方から利用できるパッケージを作れます。
モジュール単位の指定について
言語バージョンをモジュール(ターゲット)ごとに指定したいというニーズもありますが、これは将来 Swift Package Manager が「ビルド設定(build settings)」の仕組みを持ったあとで扱う方針で、本提案ではパッケージ全体に対する指定のみを導入します。