Swift PackageDescription API 解读翻译
原文链接:docs.swift.org/package-man…
本文对 Swift 官方文档中的 PackageDescription API 页面进行中文翻译与使用解读。该 API 主要用于编写 Swift Package Manager 的 Package.swift 清单文件,描述一个 Swift 包的名称、平台、产物、依赖、Target、资源和编译设置等。
1. Package
Package 表示一个 Swift 包的配置对象。
在 Package.swift 中,通常通过一个嵌套初始化语句一次性声明包的所有配置,包括包名、支持平台、产物、依赖、Target 和语言标准等。官方建议在初始化后不要再修改这些属性。
典型示例:
// swift-tools-version:5.1
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.macOS(.v10_15),
],
products: [
.library(name: "MyLibrary", targets: ["MyLibrary"]),
],
dependencies: [
.package(url: "https://url/of/another/package/named/Utility", from: "1.0.0"),
],
targets: [
.target(name: "MyLibrary", dependencies: ["Utility"]),
.testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
]
)
初始化参数:
Package(
name: String,
defaultLocalization: LanguageTag? = nil,
platforms: [SupportedPlatform]? = nil,
products: [Product] = [],
dependencies: [Package.Dependency] = [],
targets: [Target] = [],
swiftLanguageVersions: [SwiftVersion]? = nil,
cLanguageStandard: CLanguageStandard? = nil,
cxxLanguageStandard: CXXLanguageStandard? = nil
)
字段解读:
name:包名,通常也是包根目录名。defaultLocalization:默认本地化语言标签。platforms:声明最低支持平台版本。products:声明包对外暴露的构建产物,例如 library 或 executable。dependencies:声明依赖的外部包或本地包。targets:声明源码、测试、可执行程序、二进制包等构建单元。swiftLanguageVersions:指定 Swift 语言版本。cLanguageStandard/cxxLanguageStandard:指定 C / C++ 语言标准。
Swift Tools Version
每个 Package.swift 文件必须以如下声明开头:
// swift-tools-version:5.3
该版本声明决定三件事:
- 使用哪个版本的
PackageDescriptionAPI。 - 处理该 manifest 所需的最低 Swift 工具链版本。
- 当前包兼容的 Swift 语言和工具链能力。
Swift 新版本可能会扩展 PackageDescription,但旧 tools version 对应的 API 仍会保留。这样旧包无需修改 manifest,也可以继续被新工具链处理。
2. SupportedPlatform
SupportedPlatform 表示 Swift 包支持的平台及其最低部署版本。
如果不显式配置 platforms,Swift Package Manager 会为每个平台选择一个默认最低部署版本。这个默认值通常是当前已安装 SDK 支持的最早部署版本;macOS 是例外,最低从 10.10 开始。
使用示例:
platforms: [
.iOS(.v13),
.macOS(.v10_15),
]
也可以用字符串指定版本:
platforms: [
.iOS("13.0"),
.macOS("10.15.1"),
]
注意点:
platforms不能传空数组。- 同一平台不能重复声明。
- 版本字符串必须是两个或三个点分隔整数,例如
13.0或13.0.1。 - 依赖包的最低部署版本必须小于或等于顶层包的最低部署版本,否则 SPM 会报错。
支持的配置方法包括:
macOS(_:)iOS(_:)tvOS(_:)watchOS(_:)
3. Product
Product 表示包对外可见的构建产物。客户端依赖一个 Swift 包时,实际使用的是这个包导出的 product。
Product 由一个或多个 target 组装而成,主要有两类:
library:导出库,让其他包可以使用其 public API。executable:导出可执行程序,供客户端运行。
示例:
products: [
.executable(name: "tool", targets: ["tool"]),
.library(name: "Paper", targets: ["Paper"]),
.library(name: "PaperStatic", type: .static, targets: ["Paper"]),
.library(name: "PaperDynamic", type: .dynamic, targets: ["Paper"]),
]
库产物:
static func library(
name: String,
type: Product.Library.LibraryType? = nil,
targets: [String]
) -> Product
官方建议:如果没有特殊需求,不要显式指定 .static 或 .dynamic,让 Swift Package Manager 根据使用方偏好自动选择静态或动态链接。
可执行产物:
static func executable(name: String, targets: [String]) -> Product
仅当希望外部使用方能运行该工具时才需要导出 executable product。
4. Package.Dependency
Package.Dependency 表示 Swift 包依赖的其他包。
一个包依赖通常包含:
- Git URL 或本地路径。
- 版本、分支、提交或本地路径要求。
Swift Package Manager 会执行依赖解析,确定每个依赖最终使用的具体版本。解析结果记录在 Package.resolved 中。如果 Swift 包被添加到 Apple 平台 App 工程中,Package.resolved 可能位于 .xcodeproj 或 .xcworkspace 内部。
推荐写法:
.package(url: "https://example.com/example-package.git", from: "1.2.3")
该写法表示允许使用 1.2.3 到下一个 major 版本之前的版本,例如允许 1.2.4、1.3.0,但不允许 2.0.0。这是官方推荐的远程依赖声明方式,因为它能在接受 bugfix 和兼容性功能更新的同时避免自动升级到破坏性大版本。
其他依赖形式:
.package(url: "https://example.com/example-package.git", .exact("1.2.3"))
.package(url: "https://example.com/example-package.git", "1.2.3"..<"1.2.6")
.package(url: "https://example.com/example-package.git", "1.2.3"..."1.2.6")
.package(path: "../LocalPackage")
解读:
from:从某版本开始,到下一个 major 版本前,推荐用于常规依赖。exact:精确锁定版本,容易造成依赖图冲突,不推荐常规使用。- 半开区间
..<:包含下限,不包含上限。 - 闭区间
...:包含上下限。 path:依赖本地包,不访问源码控制系统,适合本地联调多个强耦合包。
5. Package.Dependency.Requirement
Package.Dependency.Requirement 表示依赖版本要求。
官方将依赖要求分为三类:
5.1 基于版本的要求
这是推荐方式。Swift 包应遵循语义化版本规范,常见选择包括:
- 升级到下一个 major 版本前。
- 升级到下一个 minor 版本前。
- 指定版本范围。
- 精确版本。
这种方式能在稳定性和功能更新之间取得平衡。
5.2 基于分支的要求
示例:
.branch("develop")
适用于多个包并行开发,或者依赖包尚未发布版本的场景。
注意:使用分支依赖的包不能被基于版本依赖的包引用。发布正式版本前,应移除分支依赖。
5.3 基于提交的要求
示例:
.revision("e74b07278b926c9ec6f9643455ea00d1ce04a021")
该方式锁定到某个提交 hash。官方不推荐常规使用,只建议用于特殊场景。它虽然稳定,但无法获得任何更新。
常见 API:
static func exact(_ version: Version) -> Package.Dependency.Requirement
static func revision(_ ref: String) -> Package.Dependency.Requirement
static func branch(_ name: String) -> Package.Dependency.Requirement
static func upToNextMajor(from version: Version) -> Package.Dependency.Requirement
static func upToNextMinor(from version: Version) -> Package.Dependency.Requirement
6. Version
Version 表示符合语义化版本规范的版本号。
Swift 包版本通常由三个整数构成:
MAJOR.MINOR.PATCH
例如:
1.0.0
语义化版本含义:
MAJOR:主版本。出现 API 破坏性变更时递增,例如重命名类型、删除方法、修改方法签名、引入不兼容行为。MINOR:次版本。以向后兼容方式新增功能时递增,例如新增类型或方法。PATCH:补丁版本。以向后兼容方式修复 bug 时递增。
依赖规则能正常工作,前提是包维护者遵守语义化版本。
7. Target
Target 是 Swift 包的基本构建单元。每个 target 包含一组源码文件,会被编译成模块、测试套件、可执行程序或二进制产物。
Target 可以依赖:
- 同一个包内的其他 target。
- 外部依赖包导出的 product。
7.1 普通 target
static func target(
name: String,
dependencies: [Target.Dependency] = [],
path: String? = nil,
exclude: [String] = [],
sources: [String]? = nil,
resources: [Resource]? = nil,
publicHeadersPath: String? = nil,
cSettings: [CSetting]? = nil,
cxxSettings: [CXXSetting]? = nil,
swiftSettings: [SwiftSetting]? = nil,
linkerSettings: [LinkerSetting]? = nil
) -> Target
普通 target 可以包含 Swift 或 C 系语言源码,但不能二者混合。默认源码路径通常是:
[PackageRoot]/Sources/[TargetName]
重要参数:
dependencies:target 依赖。path:自定义 target 路径,不能逃出包根目录,例如../Foo或/Foo无效。exclude:排除文件或目录,优先级高于sources。sources:显式指定源码文件或目录。resources:显式指定资源文件。publicHeadersPath:C 系库 target 的 public headers 目录。cSettings/cxxSettings/swiftSettings/linkerSettings:各语言和链接设置。
7.2 executableTarget
static func executableTarget(
name: String,
dependencies: [Target.Dependency] = [],
path: String? = nil,
exclude: [String] = [],
sources: [String]? = nil,
resources: [Resource]? = nil,
publicHeadersPath: String? = nil,
cSettings: [CSetting]? = nil,
cxxSettings: [CXXSetting]? = nil,
swiftSettings: [SwiftSetting]? = nil,
linkerSettings: [LinkerSetting]? = nil
) -> Target
可执行 target 会编译为可执行模块。它通常需要满足以下条件之一:
- 存在
main.swift、main.m、main.c或main.cpp。 - 某个源码文件中包含
@main。
7.3 testTarget
static func testTarget(
name: String,
dependencies: [Target.Dependency] = [],
path: String? = nil,
exclude: [String] = [],
sources: [String]? = nil,
resources: [Resource]? = nil,
cSettings: [CSetting]? = nil,
cxxSettings: [CXXSetting]? = nil,
swiftSettings: [SwiftSetting]? = nil,
linkerSettings: [LinkerSetting]? = nil
) -> Target
测试 target 使用 XCTest,通常依赖被测 target。
7.4 systemLibrary
static func systemLibrary(
name: String,
path: String? = nil,
pkgConfig: String? = nil,
providers: [SystemPackageProvider]? = nil
) -> Target
系统库 target 用于适配系统中已安装的库,使其能被 Swift 包使用。这类库通常来自 Homebrew、apt-get 等系统包管理器,并通过 modulemap 和 pkgConfig 暴露给 Swift。
7.5 binaryTarget
远程二进制产物:
static func binaryTarget(
name: String,
url: String,
checksum: String
) -> Target
本地二进制产物:
static func binaryTarget(
name: String,
path: String
) -> Target
Binary target 仅支持 Apple 平台。远程 URL 必须指向一个归档文件,归档根目录中应包含二进制 artifact。远程二进制还需要提供 checksum。
8. Target.Dependency
Target.Dependency 表示一个 target 对其他实体的依赖。
依赖同包内 target:
static func target(
name: String,
condition: TargetDependencyCondition? = nil
) -> Target.Dependency
依赖外部 package 导出的 product:
static func product(
name: String,
package: String,
condition: TargetDependencyCondition? = nil
) -> Target.Dependency
按名称依赖:
static func byName(
name: String,
condition: TargetDependencyCondition? = nil
) -> Target.Dependency
byName 会在 Swift Package Manager 加载完整包图后解析为 target 或 product。显式依赖更清晰,能减少歧义;只有在需要兼容简写或名称解析场景时才建议使用 byName。
9. TargetDependencyCondition
TargetDependencyCondition 用于限制 target 依赖的生效条件。
static func when(platforms: [Platform]? = nil) -> TargetDependencyCondition
典型用途是只在特定平台引入某个依赖:
.product(
name: "SomeAppleOnlyLibrary",
package: "SomePackage",
condition: .when(platforms: [.iOS, .macOS])
)
10. Resource
Resource 表示要打包进 Swift package 的资源文件。
Swift tools version 为 5.3 或更高时,包可以包含资源文件。资源和源码一样隶属于 target,因此资源文件应放在对应 target 目录下。例如 MyLibrary target 的资源应放在:
Sources/MyLibrary
常见组织方式:
Sources/MyLibrary/Resources
Swift Package Manager 会自动处理 Apple 平台上的常见资源类型,例如 XIB、Storyboard、Core Data 文件和 asset catalog。其他资源类型,例如图片文件,通常需要显式声明为 process 或 copy。
10.1 process
static func process(
_ path: String,
localization: Localization? = nil
) -> Resource
process 会按目标平台处理资源。比如某些图片资源可能会被平台优化;如果没有可用优化,则会复制文件。如果 path 是目录,会递归处理目录内文件。
官方建议:能用 process 时优先用 process。
10.2 copy
static func copy(_ path: String) -> Resource
copy 会原样复制资源。如果资源必须保持原始内容或目录结构,应使用 copy。当 path 是目录时,Xcode 会保留其结构。
也可以通过 target 的 exclude 参数排除资源。
11. Resource.Localization
Resource.Localization 用于声明资源本地化类型。
可选值:
case `default`
case base
含义:
default:默认国际化。base:Base Internationalization。
12. LanguageTag
LanguageTag 是 IETF language tag 的包装类型。
init(_ tag: String)
示例:
defaultLocalization: "en"
或显式构造:
defaultLocalization: LanguageTag("zh-Hans")
13. CSetting
CSetting 表示 C 语言构建设置。
头文件搜索路径:
static func headerSearchPath(
_ path: String,
_ condition: BuildSettingCondition? = nil
) -> CSetting
该路径相对于 target 目录,必须位于包内,不能使用绝对路径,也不能用它向其他 target 暴露 header。
定义宏:
static func define(
_ name: String,
to value: String? = nil,
_ condition: BuildSettingCondition? = nil
) -> CSetting
如果不指定 value,宏默认值为 1。
不安全编译参数:
static func unsafeFlags(
_ flags: [String],
_ condition: BuildSettingCondition? = nil
) -> CSetting
unsafeFlags 会传递任意命令行参数给构建工具。由于 Swift Package Manager 无法判断这些参数是否会影响构建安全性或产生副作用,使用该设置的 target 所在 product 不能被其他包作为依赖使用。应谨慎使用。
14. CXXSetting
CXXSetting 表示 C++ 构建设置。
API 与 CSetting 类似:
static func headerSearchPath(_ path: String, _ condition: BuildSettingCondition? = nil) -> CXXSetting
static func define(_ name: String, to value: String? = nil, _ condition: BuildSettingCondition? = nil) -> CXXSetting
static func unsafeFlags(_ flags: [String], _ condition: BuildSettingCondition? = nil) -> CXXSetting
解读同 CSetting:可以配置头文件路径、宏定义和不安全构建参数。unsafeFlags 同样会让包含该 target 的 product 不能被其他包依赖。
15. SwiftSetting
SwiftSetting 表示 Swift 语言构建设置。
定义编译条件:
static func define(
_ name: String,
_ condition: BuildSettingCondition? = nil
) -> SwiftSetting
使用示例:
swiftSettings: [
.define("ENABLE_SOMETHING")
]
源码中可以这样判断:
#if ENABLE_SOMETHING
// ...
#endif
Swift 编译条件不同于 C/C++ 宏,它没有关联值,只表示某个条件是否存在。
不安全 Swift 编译参数:
static func unsafeFlags(
_ flags: [String],
_ condition: BuildSettingCondition? = nil
) -> SwiftSetting
同样应谨慎使用。使用 unsafeFlags 的 product 不能作为其他包的依赖。
16. LinkerSetting
LinkerSetting 表示链接器设置。
链接系统库:
static func linkedLibrary(
_ library: String,
_ condition: BuildSettingCondition? = nil
) -> LinkerSetting
链接系统 framework:
static func linkedFramework(
_ framework: String,
_ condition: BuildSettingCondition? = nil
) -> LinkerSetting
这两个设置主要用于无法自动链接的库或 framework,例如 C++ 库、非模块化库、非模块化 framework。
不安全链接参数:
static func unsafeFlags(
_ flags: [String],
_ condition: BuildSettingCondition? = nil
) -> LinkerSetting
使用风险同前:SPM 无法验证其安全性,包含该 target 的 product 不能被其他包依赖。
17. SwiftVersion
SwiftVersion 表示用于编译包内 Swift 源码的 Swift 语言版本。
enum SwiftVersion {
case v3
case v4
case v4_2
case v5
case version(String)
}
version(String) 会把自定义字符串原样传给 Swift 编译器的 -swift-version 参数。
示例:
swiftLanguageVersions: [.v5]
18. CLanguageStandard
CLanguageStandard 表示 C 源码使用的语言标准。
官方列出的可选值包括:
case c89
case c90
case c99
case c11
case c17
case c18
case c2x
case gnu89
case gnu90
case gnu99
case gnu11
case gnu17
case gnu18
case gnu2x
case iso9899_1990 = "iso9899:1990"
case iso9899_199409 = "iso9899:199409"
case iso9899_1999 = "iso9899:1999"
case iso9899_2011 = "iso9899:2011"
case iso9899_2017 = "iso9899:2017"
case iso9899_2018 = "iso9899:2018"
使用示例:
cLanguageStandard: .c11
19. CXXLanguageStandard
CXXLanguageStandard 表示 C++ 源码使用的语言标准。
官方列出的可选值包括:
case cxx98 = "c++98"
case cxx03 = "c++03"
case cxx11 = "c++11"
case cxx14 = "c++14"
case cxx17 = "c++17"
case cxx1z = "c++1z"
case cxx20 = "c++20"
case gnucxx98 = "gnu++98"
case gnucxx03 = "gnu++03"
case gnucxx11 = "gnu++11"
case gnucxx14 = "gnu++14"
case gnucxx17 = "gnu++17"
case gnucxx1z = "gnu++1z"
case gnucxx20 = "gnu++20"
使用示例:
cxxLanguageStandard: .cxx17
20. 常见组合示例
下面是一个较完整的 Package.swift 示例,覆盖平台、产物、依赖、target、资源和编译设置:
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "ExamplePackage",
defaultLocalization: "en",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
],
products: [
.library(name: "ExamplePackage", targets: ["ExamplePackage"]),
.executable(name: "example-tool", targets: ["ExampleTool"]),
],
dependencies: [
.package(url: "https://example.com/Utility.git", from: "1.2.3"),
],
targets: [
.target(
name: "ExamplePackage",
dependencies: [
.product(name: "Utility", package: "Utility"),
],
resources: [
.process("Resources"),
],
swiftSettings: [
.define("ENABLE_LOG"),
],
linkerSettings: [
.linkedFramework("Security"),
]
),
.executableTarget(
name: "ExampleTool",
dependencies: ["ExamplePackage"]
),
.testTarget(
name: "ExamplePackageTests",
dependencies: ["ExamplePackage"]
),
],
swiftLanguageVersions: [.v5]
)
21. 使用建议总结
- 优先使用
// swift-tools-version对应的新 API,但不要无意义提高 tools version,避免降低旧工具链兼容性。 - 常规远程依赖优先使用
.package(url:from:)或.upToNextMajor(from:)。 - 避免在可发布包中使用
.branch和.revision依赖。 - 避免使用
.exact,除非明确需要锁定且知道依赖图影响。 libraryproduct 不建议默认指定.static或.dynamic,交给 SPM 和使用方决定。- target 路径必须保持在包根目录内。
- 资源能用
.process时优先用.process,只有需要原样保留结构时才用.copy。 - 尽量避免
unsafeFlags,它会影响包能否被其他包依赖。 - 显式使用
.product(name:package:)往往比byName更清晰。
22. 快速对照表
| 概念 | 作用 | 常见写法 |
|---|---|---|
Package | 描述整个 Swift 包 | Package(name:..., targets:...) |
SupportedPlatform | 声明最低平台版本 | .iOS(.v13) |
Product | 对外暴露产物 | .library(...) / .executable(...) |
Package.Dependency | 声明包依赖 | .package(url:..., from: "1.0.0") |
Target | 构建单元 | .target(...) / .testTarget(...) |
Target.Dependency | target 依赖关系 | .target(...) / .product(...) |
Resource | 打包资源 | .process("Resources") |
SwiftSetting | Swift 编译设置 | .define("FLAG") |
LinkerSetting | 链接设置 | .linkedFramework("Security") |
SwiftVersion | Swift 语言版本 | .v5 |
CLanguageStandard | C 标准 | .c11 |
CXXLanguageStandard | C++ 标准 | .cxx17 |