SwiftPM @testable build setting
01 何が問題だったのか
Swift Package Manager(SwiftPM)は、debug ビルドの際にすべてのターゲットへ一律に -enable-testing フラグを渡しており、@testable import を有効化するためのコード生成が常に行われる実装になっていました。この挙動はパッケージ作者側からコントロールする手段がなく、@testable import を一切使わないターゲットでも無条件に有効化されてしまいます。
@testable import を有効にすると、internal な宣言まで外部に露出する形でシンボルが出力されるため、次のようなコストが生じます。
- バイナリから export されるシンボルが増える。Windows の shared library では一つの DLL あたり 65,000 シンボルという上限があり、大きなライブラリではこの制限に抵触しやすくなります。
- 余計なシンボルはリンク時間を押し上げ、debug ビルドのビルド時間悪化にもつながります。
Xcode のビルドシステムではターゲットごとに testability を切り替えられるのに対し、SwiftPM にはそれに相当する設定がなく、この機能差が問題視されていました。
02 どのように解決されるのか
SwiftSetting にターゲットごとの testability を制御するための API enableTestableImport(_:_:) を追加します。Package manifest のツール版数が 6.1 以上の場合に利用できます。
// swift-tools-version: 6.1
import PackageDescription
let package = Package(
name: "MyPackage",
targets: [
.target(
name: "MyLibrary",
swiftSettings: [
// このターゲットでは @testable import を無効化する
.enableTestableImport(false),
]
),
.target(
name: "MyOtherLibrary",
swiftSettings: [
// debug ビルドでだけ有効化したい場合は condition で絞り込める
.enableTestableImport(true, .when(configuration: .debug)),
]
),
]
)
この設定を指定しない既存のターゲットは、これまで通りの挙動になります。つまり、debug 構成では @testable import が有効、release 構成では無効です。明示的に enableTestableImport(true) を release ビルドに対して指定した場合は、ビルド時に警告が出ます。
swift test のフラグの扱いの変更
swift test が持っている --enable-testable-imports / --disable-testable-imports フラグは、従来は前者がデフォルトで有効という扱いでしたが、本 Proposal では次のように変更されます。
- デフォルトは「unspecified(未指定)」となり、ターゲット側の
enableTestableImport設定がそのまま尊重されます。 - コマンドラインで
--enable-testable-importsまたは--disable-testable-importsを明示した場合は、ターゲット側の設定に関わらず、すべてのターゲットに対して testability が一律に有効化/無効化されます。
これにより、個別ターゲットの設定を通常は尊重しつつ、必要に応じて CLI から全体を上書きできる挙動になります。
今後の見通し
将来の manifest バージョンでは、debug 構成でも @testable import をデフォルト無効にする変更が検討される可能性があります。現時点ではあくまで speculative な方向性で、本 Proposal の範囲には含まれません。