|
| 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> |
0 commit comments