Skip to main content

Crate system_deps

Crate system_deps 

Source
Expand description

system-deps lets you write system dependencies in Cargo.toml metadata, rather than programmatically in build.rs. This makes those dependencies declarative, so other tools can read them as well.

§Usage

In your Cargo.toml:

[build-dependencies]
system-deps = "7.0"

Then, to declare a dependency on testlib >= 1.2 add the following section:

[package.metadata.system-deps]
testlib = "1.2"

Finally, in your build.rs, add:

fn main() {
    system_deps::Config::new().probe().unwrap();
}

§Version format

Versions can be expressed in the following formats

  • “1.2” or “>= 1.2”: At least version 1.2
  • “>= 1.2, < 2.0”: At least version 1.2 but less than version 2.0

In the future more complicated version expressions might be supported.

Note that these versions are not interpreted according to the semver rules, but based on the rules defined by pkg-config.

§Feature-specific dependency

You can easily declare an optional system dependency by associating it with a feature:

[package.metadata.system-deps]
testdata = { version = "4.5", feature = "use-testdata" }

system-deps will check for testdata only if the use-testdata feature has been enabled.

§Optional dependency

Another option is to use the optional setting, which can also be used using features versions:

[package.metadata.system-deps]
test-data = { version = "4.5", optional = true }
testmore = { version = "2", v3 = { version = "3.0", optional = true }}

system-deps will automatically export for each dependency a feature system_deps_have_$DEP where $DEP is the toml key defining the dependency in snake_case. This can be used to check if an optional dependency has been found or not:

#[cfg(system_deps_have_testdata)]
println!("found test-data");

§Overriding library name

toml keys cannot contain dot characters so if your library name does, you can define it using the name field:

[package.metadata.system-deps]
glib = { name = "glib-2.0", version = "2.64" }

§Fallback library names

Some libraries may be available under different names on different platforms or distributions. To allow for this, you can define fallback names to search for if the main library name does not work.

[package.metadata.system-deps]
aravis = { fallback-names = ["aravis-0.8"] }

You may also specify different fallback names for different versions:

[package.metadata.system-deps.libfoo]
version = "0.1"
fallback-names = ["libfoo-0.1"]
v1 = { version = "1.0", fallback-names = ["libfoo1"] }
v2 = { version = "2.0", fallback-names = ["libfoo2"] }

§Feature versions

-sys crates willing to support various versions of their underlying system libraries can use features to control the version of the dependency required. system-deps will pick the highest version among enabled features. Such version features must use the pattern v1_0, v1_2, etc.

[features]
v1_2 = []
v1_4 = ["v1_2"]
v1_6 = ["v1_4"]

[package.metadata.system-deps.libfoo_1_0]
name = "libfoo-1.0"
version = "1.0"
v1_2 = { version = "1.2" }
v1_4 = { version = "1.4" }
v1_6 = { version = "1.6" }

The same mechanism can be used to require a different library name depending on the version:

[package.metadata.system-deps.libfoo_gl]
name = "libfoo-gl-1.0"
version = "1.14"
v1_18 = { version = "1.18", name = "libfoo-gl-egl-1.0" }

§Target specific dependencies

You can define target specific dependencies:

[package.metadata.system-deps.'cfg(target_os = "linux")']
testdata = "1"
[package.metadata.system-deps.'cfg(not(target_os = "macos"))']
testlib = "1"
[package.metadata.system-deps.'cfg(unix)']
testanotherlib = { version = "1", optional = true }

See the Rust documentation for the exact syntax. Currently, those keys are supported:

  • target_arch
  • target_endian
  • target_env
  • target_family
  • target_os
  • target_pointer_width
  • target_vendor
  • unix and windows

§Overriding build flags

By default system-deps automatically defines the required build flags for each dependency using the information fetched from pkg-config. These flags can be overridden using environment variables if needed:

With $NAME being the upper case name of the key defining the dependency in Cargo.toml. For example SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE=/opt/lib could be used to override a dependency named testlib.

One can also define the environment variable SYSTEM_DEPS_$NAME_NO_PKG_CONFIG to fully disable pkg-config lookup for the given dependency. In this case at least SYSTEM_DEPS_$NAME_LIB or SYSTEM_DEPS_$NAME_LIB_FRAMEWORK should be defined as well.

§Internally build system libraries

-sys crates can provide support for building and statically link their underlying system library as part of their build process. Here is how to do this in your build.rs:

fn main() {
    system_deps::Config::new()
        .add_build_internal("testlib", |lib, version| {
            // Actually build the library here that fulfills the passed in version requirements
            system_deps::Library::from_internal_pkg_config("build/path-to-pc-file", lib, "1.2.4")
         })
        .probe()
        .unwrap();
}

This feature can be controlled using the SYSTEM_DEPS_$NAME_BUILD_INTERNAL environment variable which can have the following values:

  • auto: build the dependency only if the required version has not been found by pkg-config;
  • always: always build the dependency, ignoring any version which may be installed on the system;
  • never: (default) never build the dependency, system-deps will fail if the required version is not found on the system.

You can also use the SYSTEM_DEPS_BUILD_INTERNAL environment variable with the same values defining the behavior for all the dependencies which don’t have SYSTEM_DEPS_$NAME_BUILD_INTERNAL defined.

§Static linking

By default all libraries are dynamically linked, except when build internally as described above. Libraries can be statically linked by defining the environment variable SYSTEM_DEPS_$NAME_LINK=static. You can also use SYSTEM_DEPS_LINK=static to statically link all the libraries.

The libraries specified with this option can have any form readable by pkg-config, and they will inherit the main libraries’ binary paths if you are using them. If pkg-config can’t find some entry, it will print a warning but the compilation won’t fail.

§Using prebuilt binaries

Some system libraries may take too long to build or require a specific environment. system-deps allows to download and link against prebuilt library binaries specified in the crate metadata. To do so, you need to enable the binary feature and configure the library metadata.

[package.metadata.system-deps.liba]
name = "liba"
version = "1.0"
url = "https://download/liba-1.0.tar.gz"
checksum = "..."
pkg_paths = [ "lib/pkgconfig" ]

The snippet above will attempt to download the archive specified in the url field, extract it and add the relative paths from pkg_paths to the PKG_CONFIG_PATH when looking for liba. This is done automatically and dependents of the library don’t need to make any changes. It is recommended to have a feature in the crate’s Cargo.toml that enables the binary feature in system-deps, instead of hard-coding it.

[features]
binary = [ "system-deps/binary", "system-deps/gz" ]

As oppossed to the other metadata in system-deps, the metadata section can be specified anywhere in the crate tree, with entries from top level crates having priority. This allows for a crate to provide a default value for its binaries, and a dependent crate to add extra configuration.

# Crate graph: user_project -> libb -> liba

# libb/Cargo.toml
[package.metadata.system-deps.liba]
url = "https://download/custom-liba-1.0.tar.gz"

# user_project/Cargo.toml
[package.metadata.system-deps.liba]
url = "file:///tmp/liba"

In this example, libb overwrites the binaries provided by liba (for compatibility reasons, to add flags needed by libb, to use a single package for both…). However, the user project overwrites them again to point at a local file for development.

The binaries can be configured per target like other system-deps options:

[package.metadata.system-deps.liba.'cfg(target = "unix")']
url = "https://download/liba-unix-1.0.tar.gz"

[package.metadata.system-deps.liba.'cfg(target = "windows")']
url = "https://download/liba-windows-1.0.zip"

By default, a binary archive adds its paths to PKG_CONFIG_PATH only for the library it is defined for. However, sometimes you may want to share a single url for multiple libraries. While it is possible to repeat the url for every entry, a more concise approach is to use follows to copy the configuration from another library.

[package.metadata.system-deps.libb]
follows = "liba" # This name corresponds to the key of the metadata table

Structs§

Config
Structure used to configure metadata before starting to probe for dependencies
Dependencies
All the system dependencies retrieved by Config::probe.
InternalLib
Internal library name and if a static library is available on the system
Library
A system dependency

Enums§

BuildInternalClosureError
Error used in return value of Config::add_build_internal closures
Error
system-deps errors
Source
From where the library settings have been retrieved

Traits§

PathOrList
A trait that can represent both a reference to a Path like object or a list of paths. Used in Library::wrap_pkg_config and Library::from_internal_pkg_config to specify the list of pkg-config paths that should take priority.