Skip to content

Partial migration of Usage.md - topics related to dependencies and C language modules #8709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 27, 2025
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Adding dependencies to a Swift package

Use other swift packages, system libraries, or binary dependencies in your package.

## Overview

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.

A remote dependency requires a location, represented by a URL, and a requirement on the versions the package manager may use.

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.
It then uses the product `PlayingCard` as a dependency for the target `MyPackage`:

```swift
// swift-tools-version:6.1
import PackageDescription

let package = Package(
name: "MyPackage",
dependencies: [
.package(url: "https://github.com/apple/example-package-playingcard.git",
from: "3.0.4"),
],
targets: [
.target(
name: "MyPackage",
dependencies: [
.product(name: "PlayingCard",
package: "example-package-playingcard")
]
),
.testTarget(
name: "MyPackageTests",
dependencies: ["MyPackage"]
),
]
)
```

The package manager automatically resolves packages when you invoke <doc:SwiftRun> or <doc:SwiftBuild>.
You can explicitly resolve the packages with the command <doc:PackageResolve>.
For more information on resolving package versions, see <doc:ResolvingPackageVersions>.

### Constraining dependency versions

Constrain the version of a remote dependency when you when you declare the dependency.
The package manager uses git tags interpretted as semantic versions to identify eligible versions of packages.

> Note: tags for package versions should include all three components of a semantic version: major, minor, and patch.
> Tags that only include one or two of those components are not interpreted as semantic versions.

Use the version requirement when you declare the dependency to limit what the package manager can choose.
The version requirement can be a range of possible semantic versions, a specific semantic version, a branch name, or a commit hash.
The API reference documentation for [Package.Dependency](https://developer.apple.com/documentation/packagedescription/package/dependency) defines the methods to use.

### Local Dependencies

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.
Local dependencies do not enforce version constraints, and instead use the version that is available at the path you provide.

### System Library Dependencies

In addition to depending on Swift packages, you can also depend on system libraries or, on Apple platforms, precompiled binary dependencies.

For more information on using a library provided by the system as a dependency, see <doc:AddingSystemLibraryDependency>.

### Precomiled Binary Targets for Apple platforms

To add a dependency on a precompiled binary target, specify a `.binaryTarget` in your list of targets, using either
[binarytarget(name:url:checksum:)](https://developer.apple.com/documentation/packagedescription/target/binarytarget(name:url:checksum:)) for a downloadable target,
or [binarytarget(name:path:)](https://developer.apple.com/documentation/packagedescription/target/binarytarget(name:path:)) for a local binary.
After adding the binary target, you can add it to the list of dependencies for any other target.

For more information on identifying and verifying a binary target, see [Identifying binary dependencies](https://developer.apple.com/documentation/xcode/identifying-binary-dependencies).
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).

## Topics

- <doc:ResolvingPackageVersions>
- <doc:ResolvingDependencyFailures>
- <doc:AddingSystemLibraryDependency>
- <doc:ExampleSystemLibraryPkgConfig>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Creating C language targets

Include C language code as a target in your Swift package.

## Overview

C language targets are structured similarly to Swift targets, with the additional of a directory, commonly named `include`, to hold public header files.
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).

Swift Package manager allows only one valid C language main file for executable targets.
For example, it is invalid to have `main.c` and `main.cpp` in the same target.

### Exposing C functions to Swift

Swift Package Manager automatically generates a module map for each C language library target for these use cases:

* 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.

* 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.

* Otherwise, Swift package manager uses the `include` directory as an umbrella directory; all headers under it are included in the module.

In case of complicated `include` layouts or headers that are not compatible with modules, provide a `module.modulemap` in the `include` directory.
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Adding a dependency on a system library.

Define the location for the library and provide module map to expose C headers to Swift.

## Overview

You can link against system libraries, using them as a dependency in your code, using the package manager.
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.

### Using pkg-config to provide header and linker search paths

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.
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.

`pkgConfig` looks up libraries by name, which is the paramter that you pass to the systemLibrary target.
The following two examples illustrate using `libgit2` to manually look up paths for that library:

```bash
$ pkg-config --cflags libgit2
-I/opt/homebrew/Cellar/libgit2/1.9.0/include
```

To manually provide search paths for headers, use the `-Xcc -I/path/to/include/` as additional parameters to `swift build`.
To match the above example from `pkgConfig`, the additional command line options would be:
`-Xcc -I/opt/homebrew/Cellar/libgit2/1.9.0/include`

```bash
$ pkg-config --libs-only-L libgit2
-L/opt/homebrew/Cellar/libgit2/1.9.0/lib
```

To manually provide search paths for linking to binaries, use the `-Xlinker -L/path/to/include/` as additional parameters to `swift build`.
To match the above example from `pkgConfig`, the additional command line options would be:
`-Xlinker -L/opt/homebrew/Cellar/libgit2/1.9.0/lib`.

### Declaring the system library

The `systemLibrary` definition informs the Swift compiler of where to find the C library.
When building on Unix-like systems, the package manager can use `pkg-config` to look up where a library is installed.
Specify the name of the C library you want to look up for the `pkgConfig` parameter.
To use the Swift Package Manager to install the package locally, if it isn't already installed, you can specify one or more providers.

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:

```swift
.systemLibrary(
name: "Clibgit",
pkgConfig: "libgit2",
providers: [
.brew(["libgit2"]),
.apt(["libgit2-dev"])
]
)
```

### Authoring a module map

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.
Each defines:

- A name for the module to be exposed
- One or more header files to reference
- A reference to the name of the C library
- One or more export lines that identify what to expose to Swift

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:

```
module Clibgit [system] {
header "git2.h"
link "git2"
export *
}
```

Try to reference headers that reside in the same directory or as a local path to provide the greatest flexibility.
You can use an absolute path, although that makes the declaration more brittle, as different systems install system libraries in a variety of paths.

> 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.

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).

#### Versioning Modules from system libraries

When creating a module map, follow the conventions of system packagers as you name the module with version information.
For example, the Debian package for `python3` is called `python3`.
In Debian, there is not a single package for python; the system packagers designed it to be installed side-by-side with other versions.
Based on that, a recommended name for a module map for `python3` on a Debian system is `CPython3`.

#### System Libraries With Optional Dependencies

<!-- (heckj) I need to verify this is still the case for C libraries with optional dependencies - are distinct packages still needed? -->

To reference a system library with optional dependencies, you need to make another package to represent the optional library.

For example, the library `libarchive` optionally depends on `xz`, which means it can be compiled with `xz` support, but it isn't required.
To provide a package that uses libarchive with xz, make a `CArchive+CXz` package that depends on `CXz` and provides `CArchive`.


<!--#### Packages That Provide Multiple Libraries-->
<!---->
<!--To use a system package that provides multiple libraries, such as `.so` and `.dylib` files, add all the libraries to the `module.modulemap` file. -->
<!---->
<!--```-->
<!--module CFoo [system] {-->
<!-- header "/usr/local/include/foo/foo.h"-->
<!-- link "foo"-->
<!-- export *-->
<!--}-->
<!---->
<!--module CFooBar [system] {-->
<!-- header "/usr/include/foo/bar.h"-->
<!-- link "foobar"-->
<!-- export *-->
<!--}-->
<!---->
<!--module CFooBaz [system] {-->
<!-- header "/usr/include/foo/baz.h"-->
<!-- link "foobaz"-->
<!-- export *-->
<!--}-->
<!--```-->
<!---->
<!--^^ 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!-->
<!---->
<!--In the above example `foobar` and `foobaz` link to `foo`. -->
<!--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`. -->
<!--It is very important however that those headers do include their dependent headers.-->
<!--Otherwise when the modules are imported into Swift the dependent modules are not imported automatically and you will receive link errors. -->
<!--If link errors occur for consumers of your package, the link errors can be especially difficult to debug.-->

## See Also

- <doc:ExampleSystemLibraryPkgConfig>
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Swift package example that uses system library dependency with pkg-config

Create an Command-line executable package that uses libgit2 as a system library dependency.

## Overview

The following example walks through creating a binary executable that depends on [libgit2](https://github.com/libgit2/libgit2).

### Set up the package

Create a directory called `example`, and initialize it as a package that builds an executable:

```bash
$ mkdir example
$ cd example
example$ swift package init --type executable
```

Edit the `Sources/example/main.swift` so it consists of this code:

```swift
import Clibgit

let options = git_repository_init_options()
print(options)
```

### Add a system library target

Add a `systemLibrary` target to `Package.swift` that uses the `pkgConfig` parameter to look up the location of the library.

```swift
// swift-tools-version:6.1
import PackageDescription

let package = Package(
name: "example",
targets: [
.systemLibrary(
name: "Clibgit",
pkgConfig: "libgit2",
providers: [
.brew(["libgit2"]),
.apt(["libgit2-dev"])
]
)
]
)

```

The above example specifies two `providers` that Swift Package Manager can use to install the dependency, if needed.

> Note: For Windows-only packages `pkgConfig` should be omitted as `pkg-config` is not expected to be available.
> If you can't or don't want to use the `pkgConfig` parameter, pass the path of a directory containing the
> library using the `-L` flag in the command line when building your package instead.
>
> ```bash
> % swift build -Xlinker -L/usr/local/lib/
> ```

This example follows the convention of prefixing modules with `C` and using camelcase for the rest of the library, following Swift module name conventions.
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.

### Create a module map and local header

Create a directory `Sources/Clibgit` in your `example` project, and add a `module.modulemap` in the directory:

```
module Clibgit [system] {
header "git2.h"
link "git2"
export *
}
```

In the same directory, create the header file, `git2.h`, that the above module map references:

```c
// git2.h
#pragma once
#include <git2.h>
```

> Tip: Try to avoid specifying an absolute system path in the module map to the `git2.h` header provided by the library.
> Doing so will break compatibility of your project between machines that use a different file system layout or install libraries to different paths.

The `example` directory structure should look like this now:

```
.
├── Package.swift
└── Sources
├── Clibgit
│   ├── git2.h
│   └── module.modulemap
└── main.swift
```

### Add the system library dependency to the executable target

With the system library target fully defined, you can now use it as a dependency in other targets.

For example, in `Package.swift`:

```swift
// swift-tools-version:6.1
import PackageDescription

let package = Package(
name: "example",
targets: [
.executableTarget(
name: "example",
dependencies: ["Clibgit"],
path: "Sources"
),
.systemLibrary(
name: "Clibgit",
pkgConfig: "libgit2",
providers: [
.brew(["libgit2"]),
.apt(["libgit2-dev"])
]
)
]
)

```

### Run the example

Now run the command `swift run` in the example directory to create and run the executable:

```bash
% example swift run
Building for debugging...
[1/1] Write swift-version-3E695E30EE234B31.txt
Build of product 'example' complete! (0.10s)
git_repository_init_options(version: 0, flags: 0, mode: 0, workdir_path: nil, description: nil, template_path: nil, initial_head: nil, origin_url: nil)
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Exploring your package's dependencies
# Resolving package dependency failures

Understand dependency failure scenarios.

Expand Down
Loading