Swift Package Manager 教程
翻译自原文
是时候学习如何使用 Swift Package Manager 去处理外部依赖、在 macOS 和 linux 上创建你自己的 Swift 库和 app 了。
Swift Package Manager 基础
注意:由于 Apple 在 Swift 4 中为 Swift Package Manager 的 API 做了一个完全的重新设计(你可以阅读相关更改的内容),本教程并不适用于 Swift 3。 所以首先在我们开始之前,请在检查你的设备上的 Swift 版本。
swift --version
Apple Swift version 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38)
Target: x86_64-apple-macosx10.9
创建 apps
所有困难的工作都可以交由 swift package
命令来完成。你可以在终端中输入该命令查看可用的子命令。为了生成一个新的包,你应该使用初始化命令。如果你不提供一个类型标志,默认情况下该命令将会生成一个库,但这一次我们想要生成一个可执行的应用。
swift package init --type executable
swift build
swift run my-app
编译器可以在 swift build
命令的帮助下构建你的源文件。该可执行文件将会被放置在 .build/ 目录下的某个地方,如果你使用 swift run my-app
命令来运行刚刚创建的应用,你应该呢个看到基本的 'Hello, world!' 消息。
祝贺你成功生成了你的第一个命令行 Swift 应用程序!
现在你应当来做一些切实的编码了。通常而言你的 Swift 源文件应该在 Sources 目录下, 然而你可能想要为你的 app 创建一些可复用的部分。所以让我们通过创建一个全新的库来为这个场景做准备吧。
生成一个 Library
我们依旧从从初始化命令开始,但这次我们不指定 init 的类型了。实际上我们可以键入 swift package init --type library
但这需要键入很多单词。 😜 另外由于我们正在生成一个库, SPM 将会给我们提供一些基本的测试,让我们使用 swift test
命令来运行它们吧。
swift package init
swift test
如果你此时检查文件结构,你会在 Tests 下看到一个单元测试的例子,而不会在 source 中找到 main.swift 文件。
现在你有了一些基础了。你有一个示例程序和一个 library, 让我们在 Swift Package Manager Manifest API 的帮助下将它们链接在一起吧。
The Manifest API(清单 API) - Package.swift
每一个 SPM 包内都有一个 Package.swift 清单文件(manifest file). 在清单文件中你可以定义你的全部依赖、targets 甚至为你的工程定义指定的源文件.在本节我会教你清单文件 (manifest file) 的一些基础内容.
工具版本
首先如果你想支持新的 manifest file 的格式 (换言之, Swift 4 版本) , 你得以注释的方式在你的 manifest 文件设置 swift-tools-version.
// swift-tools-version:4.0
现在你已经准备好在全新的 manifest API 下工作了!
依赖
让我们先通过在 Package.swift 文件中创建一个新的包依赖 (package dependency) 来为主程序添加一个依赖库. 第一个参数是一条包 url 字符串,可以是本地文件路径也可以是远程 url (通常是一个 github 仓库链接). 注意, 你还应当将你的依赖添加到 targets 中. 通常而言包的特定名称 (包名) 已经在该 library 的 manifest 文件中定义了.
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "my-app",
dependencies: [
.package(url: "../my-lib", .branch("master")),
],
targets: [
.target(
name: "my-app",
dependencies: ["my-lib"]
),
]
)
如果你现在运行 swift build
你将编译源文件失败. 那是因为 SPM 仅会工作在 git 仓库下. 这就意味着你需要为你的 library 创建一个 git 仓库. 让我们移动到该目录并执行如下命令:
git init
git add .
git commit -m 'initial'
你也应当标记我们指定的包依赖的分支。你可以使用 version 号,甚至也可以用 commit hashes 。所有可用的选项都很好的被写在了 manifest api redesign proposal 文档中。
现在让我们回到应用目录并使用 swift package update
命令来更新以来。这次它就能够获取、克隆并成功搞定我们的依赖了。
你可以构建并运行,然而我们忘了设置我们 library 中结构体的访问级为 public, 所以从该 API 中看不到任何东西。
public struct my_lib {
public var text = "Hello, World!"
public init() {}
}
让我们来做一些修改并将其提交到该 library 的主分支上。
git add .
git commit -m 'access level fix'
你已经准备好在 app 中使用该 lib 了,修改 main.swift file 如下。
import my_lib
print(my_lib().text)
再一次更新依赖,这次我们来构建一个 release 构建。
swift package update
swift build -c release
swift run -c release
通过 -c 或 --configuration 标识你可以生成一个 release 构建。
Products 与 targets
默认情况下, SPM 与下列 targets 目录一起工作:
Regular targets: package root, Sources, Source, src, srcs.
Test targets: Tests, package root, Sources, Source, src, srcs.
这就表明,如果你在这些目录下创建 .swift
文件,这些源文件将根据文件路径被编译或测试。另外,生成的 mainfest 文件将仅仅包含一份构建 target (像 Xcode targets一样) , 但有时候你想要从同一个 bundle 中创建多个 apps 或 libraries. 让我们稍微修改下我们的 Package.swift, 并看看如何能创造一个全新的 target.
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "my-app",
dependencies: [
.package(url: "../my-lib", .branch("master")),
.package(url: "https://github.com/kylef/Commander", from: "0.8.0"),
],
targets: [
.target(
name: "my-app",
dependencies: ["my-lib"]
),
.target(
name: "my-cmd",
dependencies: ["Commander"],
path: "./Sources/my-cmd",
sources: ["main.swift"]
),
]
)
我们刚刚从 github 创建了一个新的依赖 和一个在 Sources/my-cmd 目录下只含有 main.swift 文件的全新的 target. 现在让我们创建这个目录并为这个新的 app 添加一些源码.
import Foundation
import Commander
let main = command { (name:String) in
print("Hello, \(name.capitalized)!")
}
main.run()
使用 swift build
编译这个工程并用一个额外的 name 参数运行这个新创建的 app. 希望你能看到的输出如下:
swift run my-cmd guest
// Hello, Guest!
因此我们刚刚创建了一个全新的可执行的 target, 但如果你想将你的 targets 暴露给其他的 packages , 你应当也将它们定义为 products. 如果你打开这个 library 的 manifest 文件, 你会看到里面有个从该 library target 定义 target 字段. 通过这种方式, 包管理器可以根据给定的 product name 链接 product 依赖.
注意: 你可以定义 static 或 dynamic 的 libraries. 但推荐使用 automatic , 这样可以让 SPM 决定合适的链接.
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "my-lib-package",
products: [
.library(name: "my-lib", targets: ["my-lib"]),
//.library(name: "my-lib", type: .static, targets: ["my-lib"]),
//.library(name: "my-lib", type: .dynamic, targets: ["my-lib"]),
],
dependencies: [
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
.target(
name: "my-lib",
dependencies: []),
.testTarget(
name: "my-libTests",
dependencies: ["my-lib"]),
]
)
部署目标(Deployment target), 其他 build flags
有时候你需要为你的包指定部署的 target. 现在 Swift Package Manager 已经能做到了 (已经有 一段时间了). 你只需要在构建阶段为编译器提供一些额外的参数就行了.
swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.12"
定义构建的 flags 也是可行的.
swift build -Xswiftc "-D" -Xswiftc "DEBUG"
现在在你的源码里面你可以检查 DEBUG 标志的存在了.
#if DEBUG
print("debug mode")
#endif
如果你想了解更多关于构建过程的内容, 只需要键入 swift build --help
你就可以看到该构建命令可用的选项.
还有一件事
你可以通过 Swift Package Manager 来生成 Xcode Projects。
swift package generate-xcodeproj
这就是 SPM 的简单介绍了。实际上我们不仅仅介绍了 Swift Package Manager 的基础知识,也稍微拓展了一些,现在你应该对 targets、products 以及大部分可用的命令比较熟悉了,但仍然还有很多需要学习。所以如果你想对这个 amazing 的工具了解更多,这里还有一些非常棒的资源等着你去浏览。 Enjoy! 😉
另外,本教程的源码可以在 github 上获取。
网友评论