Swift Package Manager Manifest API Redesign
The Package Manager in Swift 4 includes the redesigned Package.swift
manifest
API. The new API is easier to use and follows the design guidelines. The target
inference rules in Swift 3 Package Manager were a common source of confusion. We
revised these rules and removed most of the inference, favoring the practice of
explicitly specifying package structure in the manifest.
Swift 3 packages will continue to work because the Package Manager in Swift 4 is
backwards compatible. The manifest version is chosen by the tools version of
the package. The tools version is specified in the first line of the manifest,
using the special comment syntax: // swift-tools-version:<specifier>
.
Packages that omit this special comment will default to tools version 3.1.0.
The tools version also determines the default Swift language version used to
compile the package’s sources. Existing Swift 3 packages will compile in Swift
3 compatibility mode. You can optionally use the swiftLanguageVersions
property in both Swift 3 and Swift 4 manifests to set the language version used
to compile that package, if you don’t want the default version. This means it
is possible to upgrade a package to use the newer manifest format without
upgrading its sources to Swift 4.
Creating a New Package in Swift 4
Use the init
subcommand to create a new package in Swift 4:
$ mkdir mytool && cd mytool
$ swift package init
$ swift build
$ swift test
The Package.swift
manifest generated by the commands above is shown below.
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "mytool",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "mytool",
targets: ["mytool"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target defines a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "mytool",
dependencies: []),
.testTarget(
name: "mytoolTests",
dependencies: ["mytool"]),
]
)
There are three key differences between the Swift 4 manifest above and the previous manifest format:
- The tools version
4.0
is specified using the line// swift-tools-version:4.0
. - All targets and their dependencies must be explicitly declared.
- Public targets are vended as products using the new product API. Targets in Swift 4 packages can either depend on products of other packages, or targets of the same package.
Custom Target Layouts
The new manifest supports customizing the layout of the package. Packages are no
longer required to follow complex, convention-based layout rules. There is only
one rule: if the target path is not provided, directories Sources
, Source
,
src
, srcs
and Tests
will be searched (in order) to find the target.
Custom layouts make porting C libraries to Swift Package Manager easier. Here are manifests of two C libraries used in server side Swift community:
LibYAML
Copyright (c) 2006-2016 Kirill Simonov, licensed under MIT license (https://github.com/yaml/libyaml/blob/master/LICENSE)
// swift-tools-version:4.0
import PackageDescription
let packages = Package(
name: "LibYAML",
products: [
.library(
name: "libyaml",
targets: ["libyaml"]),
],
targets: [
.target(
name: "libyaml",
path: ".",
sources: ["src"])
]
)
Node.js http-parser
Copyright by Authors (https://github.com/nodejs/http-parser/blob/master/AUTHORS), licensed under MIT license (https://github.com/nodejs/http-parser/blob/master/LICENSE-MIT)
// swift-tools-version:4.0
import PackageDescription
let packages = Package(
name: "http-parser",
products: [
.library(
name: "httpparser",
targets: ["http-parser"]),
],
targets: [
.target(
name: "http-parser",
publicHeaders: ".",
sources: ["http_parser.c"])
]
)
Dependency Resolution
Since Swift 3 Package Manager doesn’t understand the Swift 4 manifest format, it will automatically ignore the Git tags that contain a Swift 4 manifest. So, if a package upgrades to Swift 4 manifest, Swift 3 Package Manager will pick the last tag which contains the Swift 3 manifest. However, the Package Manager in Swift 4 will pick the latest available version, regardless of manifest version.
Updating an Existing Package to the Swift 4 Manifest Format
Follow these steps to update existing package to use the Swift 4 manifest format.
-
Update the tools version of the package.
Use the
tools-version
subcommand to update the tools version of the package.
$ cd mypackage
$ swift package tools-version --set-current
- Move the
dependencies
label to precede thetargets
label and update the package dependency syntax. For example:
...
dependencies: [
- .Package(url: "https://github.com/apple/example-package-fisheryates.git", majorVersion: 2),
+ .package(url: "https://github.com/apple/example-package-fisheryates.git", from: "2.0.0"),
- .Package(url: "https://github.com/apple/example-package-playingcard.git", majorVersion: 3, minor: 3),
+ .package(url: "https://github.com/apple/example-package-playingcard.git", .upToNextMinor(from: "3.3.0")),
]
...
-
Declare all regular and test targets.
All targets and their dependencies should be explicitly declared. If there are two targets,
Foo
andFooTests
, declare both of them intargets
label. For example:
...
targets: [
.target(
name: "Foo"),
.testTarget(
name: "FooTests",
dependencies: ["Foo"]),
]
...
-
If the package is using the legacy single target layout, update the layout or provide the target path.
The recommended layout is to have one directory per target under the
Sources
i.e.Sources/<target-name>
. If a package is using this layout, the target path will be automatically detected. Otherwise, provide a target path. For example:
...
targets: [
.target(
name: "Foo",
path: "."), // The sources are located in package root.
.target(
name: "Bar",
path: "Sources") // The sources are located in directory Sources/.
]
...
-
Export public targets using the product API.
Library packages should explicitly export their public targets to allow other packages to import them. Avoid exporting targets like sample code targets, test support library etc.
...
products: [
.library(
name: "Foo",
targets: ["Foo", "Bar"]),
],
...
-
Compile in Swift 3 compatibility mode.
You can update your package manifest to the new format before updating your package’s source code to Swift 4. To do so, set the
swiftLanguageVersions
property to 3 to build your package in Swift 3 compatibility mode.
...
swiftLanguageVersions: [3]
...