Swift-PackageDescription-API解读翻译

6 阅读13分钟

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

该版本声明决定三件事:

  • 使用哪个版本的 PackageDescription API。
  • 处理该 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.013.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.41.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.swiftmain.mmain.cmain.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 等系统包管理器,并通过 modulemappkgConfig 暴露给 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。其他资源类型,例如图片文件,通常需要显式声明为 processcopy

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,除非明确需要锁定且知道依赖图影响。
  • library product 不建议默认指定 .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.Dependencytarget 依赖关系.target(...) / .product(...)
Resource打包资源.process("Resources")
SwiftSettingSwift 编译设置.define("FLAG")
LinkerSetting链接设置.linkedFramework("Security")
SwiftVersionSwift 语言版本.v5
CLanguageStandardC 标准.c11
CXXLanguageStandardC++ 标准.cxx17