Skip to content

Commit 3eac931

Browse files
heckjowenv
andauthored
Partial migration of Usage.md - topics related to dependencies and C language modules (#8709)
Migrating content from documentation/Usage.md into DocC: New articles: - AddingDependencies.md - CreatingCLanguageTargets.md - ResolvingPackageVersions.md - Dependencies/AddingSystemLibraryDependency.md - Dependencies/ExampleSystemLibraryPkgConfig.md - Dependencies/ExampleSystemLibraryWithoutPkgConfig.md - Dependencies/ResolvingDependencyFailures.md And updates to Documentation.md for curating them into the collection as guides. partial work against #8593 --------- Co-authored-by: Owen Voorhees <[email protected]>
1 parent 1cbb77a commit 3eac931

8 files changed

+417
-3
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Adding dependencies to a Swift package
2+
3+
Use other swift packages, system libraries, or binary dependencies in your package.
4+
5+
## Overview
6+
7+
To depend on another Swift package, define a dependency and the requirements for its version if it's remote, then add a product of that dependency to one or more of your targets.
8+
9+
A remote dependency requires a location, represented by a URL, and a requirement on the versions the package manager may use.
10+
11+
The following example illustrates a package that depends on [PlayingCard](https://github.com/apple/example-package-playingcard), using `from` to require at least version `3.0.4`, and allow any other version up to the next major version that is available at the time of dependency resolution.
12+
It then uses the product `PlayingCard` as a dependency for the target `MyPackage`:
13+
14+
```swift
15+
// swift-tools-version:6.1
16+
import PackageDescription
17+
18+
let package = Package(
19+
name: "MyPackage",
20+
dependencies: [
21+
.package(url: "https://github.com/apple/example-package-playingcard.git",
22+
from: "3.0.4"),
23+
],
24+
targets: [
25+
.target(
26+
name: "MyPackage",
27+
dependencies: [
28+
.product(name: "PlayingCard",
29+
package: "example-package-playingcard")
30+
]
31+
),
32+
.testTarget(
33+
name: "MyPackageTests",
34+
dependencies: ["MyPackage"]
35+
),
36+
]
37+
)
38+
```
39+
40+
The package manager automatically resolves packages when you invoke <doc:SwiftRun> or <doc:SwiftBuild>.
41+
You can explicitly resolve the packages with the command <doc:PackageResolve>.
42+
For more information on resolving package versions, see <doc:ResolvingPackageVersions>.
43+
44+
### Constraining dependency versions
45+
46+
Constrain the version of a remote dependency when you when you declare the dependency.
47+
The package manager uses git tags interpretted as semantic versions to identify eligible versions of packages.
48+
49+
> Note: tags for package versions should include all three components of a semantic version: major, minor, and patch.
50+
> Tags that only include one or two of those components are not interpreted as semantic versions.
51+
52+
Use the version requirement when you declare the dependency to limit what the package manager can choose.
53+
The version requirement can be a range of possible semantic versions, a specific semantic version, a branch name, or a commit hash.
54+
The API reference documentation for [Package.Dependency](https://developer.apple.com/documentation/packagedescription/package/dependency) defines the methods to use.
55+
56+
### Local Dependencies
57+
58+
To use a local package as a dependency, use either [package(name:path:)](https://developer.apple.com/documentation/packagedescription/package/dependency/package(name:path:)) or [package(path:)](https://developer.apple.com/documentation/packagedescription/package/dependency/package(path:)) to define it with the local path to the package.
59+
Local dependencies do not enforce version constraints, and instead use the version that is available at the path you provide.
60+
61+
### System Library Dependencies
62+
63+
In addition to depending on Swift packages, you can also depend on system libraries or, on Apple platforms, precompiled binary dependencies.
64+
65+
For more information on using a library provided by the system as a dependency, see <doc:AddingSystemLibraryDependency>.
66+
67+
### Precomiled Binary Targets for Apple platforms
68+
69+
To add a dependency on a precompiled binary target, specify a `.binaryTarget` in your list of targets, using either
70+
[binarytarget(name:url:checksum:)](https://developer.apple.com/documentation/packagedescription/target/binarytarget(name:url:checksum:)) for a downloadable target,
71+
or [binarytarget(name:path:)](https://developer.apple.com/documentation/packagedescription/target/binarytarget(name:path:)) for a local binary.
72+
After adding the binary target, you can add it to the list of dependencies for any other target.
73+
74+
For more information on identifying and verifying a binary target, see [Identifying binary dependencies](https://developer.apple.com/documentation/xcode/identifying-binary-dependencies).
75+
For more information on creating a binary target, see [Creating a multiplatform binary framework bundle](https://developer.apple.com/documentation/xcode/creating-a-multi-platform-binary-framework-bundle).
76+
77+
## Topics
78+
79+
- <doc:ResolvingPackageVersions>
80+
- <doc:ResolvingDependencyFailures>
81+
- <doc:AddingSystemLibraryDependency>
82+
- <doc:ExampleSystemLibraryPkgConfig>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Creating C language targets
2+
3+
Include C language code as a target in your Swift package.
4+
5+
## Overview
6+
7+
C language targets are structured similarly to Swift targets, with the additional of a directory, commonly named `include`, to hold public header files.
8+
If you use a directory other than `include` for public headers, declare it using the [publicHeadersPath parameter](https://developer.apple.com/documentation/packagedescription/target/publicheaderspath) on [target](https://developer.apple.com/documentation/packagedescription/target).
9+
10+
Swift Package manager allows only one valid C language main file for executable targets.
11+
For example, it is invalid to have `main.c` and `main.cpp` in the same target.
12+
13+
### Exposing C functions to Swift
14+
15+
Swift Package Manager automatically generates a module map for each C language library target for these use cases:
16+
17+
* If `include/Foo/Foo.h` exists, `Foo` is the only directory under the include directory, and the include directory contains no header files, then Swift package manager uses `include/Foo/Foo.h` as the umbrella header.
18+
19+
* If `include/Foo.h` exists and `include` contains no other subdirectory, then Swift package manager uses `include/Foo.h` as the umbrella header for the module map.
20+
21+
* Otherwise, Swift package manager uses the `include` directory as an umbrella directory; all headers under it are included in the module.
22+
23+
In case of complicated `include` layouts or headers that are not compatible with modules, provide a `module.modulemap` in the `include` directory.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# Adding a dependency on a system library.
2+
3+
Define the location for the library and provide module map to expose C headers to Swift.
4+
5+
## Overview
6+
7+
You can link against system libraries, using them as a dependency in your code, using the package manager.
8+
To do so, add a `target` of type [systemLibrary](https://developer.apple.com/documentation/packagedescription/target/systemlibrary(name:path:pkgconfig:providers:)), and a `module.modulemap` for each system library you're using.
9+
10+
### Using pkg-config to provide header and linker search paths
11+
12+
For Unix-like systems, Swift Package Manager can use [pkgConfig](https://en.wikipedia.org/wiki/Pkg-config) to provide the compiler with the paths for including library headers and linking to binaries.
13+
If your system doesn't provide pkgConfig, or the library doesn't include package config files, you can provide the options to the Swift compiler directly.
14+
15+
`pkgConfig` looks up libraries by name, which is the paramter that you pass to the systemLibrary target.
16+
The following two examples illustrate using `libgit2` to manually look up paths for that library:
17+
18+
```bash
19+
$ pkg-config --cflags libgit2
20+
-I/opt/homebrew/Cellar/libgit2/1.9.0/include
21+
```
22+
23+
To manually provide search paths for headers, use the `-Xcc -I/path/to/include/` as additional parameters to `swift build`.
24+
To match the above example from `pkgConfig`, the additional command line options would be:
25+
`-Xcc -I/opt/homebrew/Cellar/libgit2/1.9.0/include`
26+
27+
```bash
28+
$ pkg-config --libs-only-L libgit2
29+
-L/opt/homebrew/Cellar/libgit2/1.9.0/lib
30+
```
31+
32+
To manually provide search paths for linking to binaries, use the `-Xlinker -L/path/to/include/` as additional parameters to `swift build`.
33+
To match the above example from `pkgConfig`, the additional command line options would be:
34+
`-Xlinker -L/opt/homebrew/Cellar/libgit2/1.9.0/lib`.
35+
36+
### Declaring the system library
37+
38+
The `systemLibrary` definition informs the Swift compiler of where to find the C library.
39+
When building on Unix-like systems, the package manager can use `pkg-config` to look up where a library is installed.
40+
Specify the name of the C library you want to look up for the `pkgConfig` parameter.
41+
To use the Swift Package Manager to install the package locally, if it isn't already installed, you can specify one or more providers.
42+
43+
The following example provides a declaration for the `libgit2` library, installing the library with homebrew on macOS or apt on a Debian based Linux system:
44+
45+
```swift
46+
.systemLibrary(
47+
name: "Clibgit",
48+
pkgConfig: "libgit2",
49+
providers: [
50+
.brew(["libgit2"]),
51+
.apt(["libgit2-dev"])
52+
]
53+
)
54+
```
55+
56+
### Authoring a module map
57+
58+
The `module.modulemap` file declares the C library headers, and what parts of them, to expose as one or more clang modules that can be imported in Swift code.
59+
Each defines:
60+
61+
- A name for the module to be exposed
62+
- One or more header files to reference
63+
- A reference to the name of the C library
64+
- One or more export lines that identify what to expose to Swift
65+
66+
For example, the following module map uses the header `git2.h`, links to `libgit2`, and exports all functions defined in the header `git2.h` to Swift:
67+
68+
```
69+
module Clibgit [system] {
70+
header "git2.h"
71+
link "git2"
72+
export *
73+
}
74+
```
75+
76+
Try to reference headers that reside in the same directory or as a local path to provide the greatest flexibility.
77+
You can use an absolute path, although that makes the declaration more brittle, as different systems install system libraries in a variety of paths.
78+
79+
> Note: Not all libraries are easily made into modules. You may have to create additional shim headers to provide the Swift compiler with the references needed to fully compile and link the library.
80+
81+
For more information on the structure of module maps, see the [LLVM](https://llvm.org/) documentation: [Module Map Language](https://clang.llvm.org/docs/Modules.html#module-map-language).
82+
83+
#### Versioning Modules from system libraries
84+
85+
When creating a module map, follow the conventions of system packagers as you name the module with version information.
86+
For example, the Debian package for `python3` is called `python3`.
87+
In Debian, there is not a single package for python; the system packagers designed it to be installed side-by-side with other versions.
88+
Based on that, a recommended name for a module map for `python3` on a Debian system is `CPython3`.
89+
90+
#### System Libraries With Optional Dependencies
91+
92+
<!-- (heckj) I need to verify this is still the case for C libraries with optional dependencies - are distinct packages still needed? -->
93+
94+
To reference a system library with optional dependencies, you need to make another package to represent the optional library.
95+
96+
For example, the library `libarchive` optionally depends on `xz`, which means it can be compiled with `xz` support, but it isn't required.
97+
To provide a package that uses libarchive with xz, make a `CArchive+CXz` package that depends on `CXz` and provides `CArchive`.
98+
99+
100+
<!--#### Packages That Provide Multiple Libraries-->
101+
<!---->
102+
<!--To use a system package that provides multiple libraries, such as `.so` and `.dylib` files, add all the libraries to the `module.modulemap` file. -->
103+
<!---->
104+
<!--```-->
105+
<!--module CFoo [system] {-->
106+
<!-- header "/usr/local/include/foo/foo.h"-->
107+
<!-- link "foo"-->
108+
<!-- export *-->
109+
<!--}-->
110+
<!---->
111+
<!--module CFooBar [system] {-->
112+
<!-- header "/usr/include/foo/bar.h"-->
113+
<!-- link "foobar"-->
114+
<!-- export *-->
115+
<!--}-->
116+
<!---->
117+
<!--module CFooBaz [system] {-->
118+
<!-- header "/usr/include/foo/baz.h"-->
119+
<!-- link "foobaz"-->
120+
<!-- export *-->
121+
<!--}-->
122+
<!--```-->
123+
<!---->
124+
<!--^^ refine this out into a full example, with code included form the headers to make it possible to follow it - and drop the FOO stuff!-->
125+
<!---->
126+
<!--In the above example `foobar` and `foobaz` link to `foo`. -->
127+
<!--You don’t need to specify this information in the module map because the headers `foo/bar.h` and `foo/baz.h` both include `foo/foo.h`. -->
128+
<!--It is very important however that those headers do include their dependent headers.-->
129+
<!--Otherwise when the modules are imported into Swift the dependent modules are not imported automatically and you will receive link errors. -->
130+
<!--If link errors occur for consumers of your package, the link errors can be especially difficult to debug.-->
131+
132+
## See Also
133+
134+
- <doc:ExampleSystemLibraryPkgConfig>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Swift package example that uses system library dependency with pkg-config
2+
3+
Create an Command-line executable package that uses libgit2 as a system library dependency.
4+
5+
## Overview
6+
7+
The following example walks through creating a binary executable that depends on [libgit2](https://github.com/libgit2/libgit2).
8+
9+
### Set up the package
10+
11+
Create a directory called `example`, and initialize it as a package that builds an executable:
12+
13+
```bash
14+
$ mkdir example
15+
$ cd example
16+
example$ swift package init --type executable
17+
```
18+
19+
Edit the `Sources/example/main.swift` so it consists of this code:
20+
21+
```swift
22+
import Clibgit
23+
24+
let options = git_repository_init_options()
25+
print(options)
26+
```
27+
28+
### Add a system library target
29+
30+
Add a `systemLibrary` target to `Package.swift` that uses the `pkgConfig` parameter to look up the location of the library.
31+
32+
```swift
33+
// swift-tools-version:6.1
34+
import PackageDescription
35+
36+
let package = Package(
37+
name: "example",
38+
targets: [
39+
.systemLibrary(
40+
name: "Clibgit",
41+
pkgConfig: "libgit2",
42+
providers: [
43+
.brew(["libgit2"]),
44+
.apt(["libgit2-dev"])
45+
]
46+
)
47+
]
48+
)
49+
50+
```
51+
52+
The above example specifies two `providers` that Swift Package Manager can use to install the dependency, if needed.
53+
54+
> Note: For Windows-only packages `pkgConfig` should be omitted as `pkg-config` is not expected to be available.
55+
> If you can't or don't want to use the `pkgConfig` parameter, pass the path of a directory containing the
56+
> library using the `-L` flag in the command line when building your package instead.
57+
>
58+
> ```bash
59+
> % swift build -Xlinker -L/usr/local/lib/
60+
> ```
61+
62+
This example follows the convention of prefixing modules with `C` and using camelcase for the rest of the library, following Swift module name conventions.
63+
This allows you to create and use another module more directly named after the library that provides idiomatic Swift wrappers around the underlying C functions.
64+
65+
### Create a module map and local header
66+
67+
Create a directory `Sources/Clibgit` in your `example` project, and add a `module.modulemap` in the directory:
68+
69+
```
70+
module Clibgit [system] {
71+
header "git2.h"
72+
link "git2"
73+
export *
74+
}
75+
```
76+
77+
In the same directory, create the header file, `git2.h`, that the above module map references:
78+
79+
```c
80+
// git2.h
81+
#pragma once
82+
#include <git2.h>
83+
```
84+
85+
> Tip: Try to avoid specifying an absolute system path in the module map to the `git2.h` header provided by the library.
86+
> Doing so will break compatibility of your project between machines that use a different file system layout or install libraries to different paths.
87+
88+
The `example` directory structure should look like this now:
89+
90+
```
91+
.
92+
├── Package.swift
93+
└── Sources
94+
├── Clibgit
95+
│   ├── git2.h
96+
│   └── module.modulemap
97+
└── main.swift
98+
```
99+
100+
### Add the system library dependency to the executable target
101+
102+
With the system library target fully defined, you can now use it as a dependency in other targets.
103+
104+
For example, in `Package.swift`:
105+
106+
```swift
107+
// swift-tools-version:6.1
108+
import PackageDescription
109+
110+
let package = Package(
111+
name: "example",
112+
targets: [
113+
.executableTarget(
114+
name: "example",
115+
dependencies: ["Clibgit"],
116+
path: "Sources"
117+
),
118+
.systemLibrary(
119+
name: "Clibgit",
120+
pkgConfig: "libgit2",
121+
providers: [
122+
.brew(["libgit2"]),
123+
.apt(["libgit2-dev"])
124+
]
125+
)
126+
]
127+
)
128+
129+
```
130+
131+
### Run the example
132+
133+
Now run the command `swift run` in the example directory to create and run the executable:
134+
135+
```bash
136+
% example swift run
137+
Building for debugging...
138+
[1/1] Write swift-version-3E695E30EE234B31.txt
139+
Build of product 'example' complete! (0.10s)
140+
git_repository_init_options(version: 0, flags: 0, mode: 0, workdir_path: nil, description: nil, template_path: nil, initial_head: nil, origin_url: nil)
141+
```

Sources/PackageManagerDocs/Documentation.docc/ExploreDependencies.md renamed to Sources/PackageManagerDocs/Documentation.docc/Dependencies/ResolvingDependencyFailures.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Exploring your package's dependencies
1+
# Resolving package dependency failures
22

33
Understand dependency failure scenarios.
44

0 commit comments

Comments
 (0)