76 releases

0.10.7+cargo-0.84.0 Nov 29, 2024
0.10.5+cargo-0.83.0 Oct 18, 2024
0.10.3+cargo-0.81.0 Jul 30, 2024
0.9.31+cargo-0.78.0 Mar 22, 2024
0.2.1 Jul 10, 2019

#13 in Cargo plugins

Download history 2615/week @ 2024-08-31 4492/week @ 2024-09-07 3173/week @ 2024-09-14 4376/week @ 2024-09-21 4308/week @ 2024-09-28 4483/week @ 2024-10-05 5405/week @ 2024-10-12 5285/week @ 2024-10-19 4101/week @ 2024-10-26 4088/week @ 2024-11-02 3292/week @ 2024-11-09 3846/week @ 2024-11-16 3784/week @ 2024-11-23 4222/week @ 2024-11-30 2830/week @ 2024-12-07 2141/week @ 2024-12-14

13,682 downloads per month
Used in cskk

MIT license

120KB
2.5K SLoC

Cargo C-ABI helpers

LICENSE Crates.io Build Status cargo-c chat dependency status

cargo applet to build and install C-ABI compatible dynamic and static libraries.

It produces and installs a correct pkg-config file, a static library and a dynamic library, and a C header to be used by any C (and C-compatible) software.

Installation

cargo-c may be installed from crates.io.

cargo install cargo-c

The rustc version supported is the same as the one supported by the cargo version embedded in the package version, or as set in the rust-version field.

You must have the cargo build requirements satisfied in order to build cargo-c:

  • git
  • pkg-config (on Unix, used to figure out the host-provided headers/libraries)
  • curl (on Unix)
  • OpenSSL headers (only for Unix, this is the libssl-dev package on deb-based distributions)

You may pass --features=vendored-openssl if you have problems building openssl-sys using the host-provided OpenSSL.

cargo install cargo-c --features=vendored-openssl

Usage

# build the library, create the .h header, create the .pc file
$ cargo cbuild --destdir=${D} --prefix=/usr --libdir=/usr/lib64
# build the library, create the .h header, create the .pc file, build and run the tests
$ cargo ctest
# build the library, create the .h header, create the .pc file and install all of it
$ cargo cinstall --destdir=${D} --prefix=/usr --libdir=/usr/lib64

For a more in-depth explanation of how cargo-c works and how to use it for your crates, read Building Crates so they Look Like C ABI Libraries.

The TL;DR:

This is the ideal setup for a project that wants to keep their C-API within the main crate:

  • Create a capi.rs with the C-API you want to expose and use #[cfg(cargo_c)]#[cfg(feature="capi")] to hide it when you build a normal rust library.
  • Make sure you have a lib target and if you are using a workspace the first member is the crate you want to export, that means that you might have to add a "." member at the start of the list.
  • Since Rust 1.38, also add "staticlib" to the "lib" crate-type. Do not specify the crate-type, cargo-c will add the correct library target by itself.
  • You may use the feature capi to add C-API-specific optional dependencies.

    NOTE: It must be always present in Cargo.toml

  • Remember to add a cbindgen.toml and fill it with at least the include guard and probably you want to set the language to C (it defaults to C++)
  • Once you are happy with the result update your documentation to tell the user to install cargo-c and do cargo cinstall --prefix=/usr --destdir=/tmp/some-place or something along those lines.

If you plan to keep the bindings as a separate crate and do not need to autogenerate the headers you may just populate Cargo.toml:

  • Add a capi feature, since it is used by cargo-c to identify packages that has to be built as C-libraries within a workspace.
  • Set the entry in package.metadata.capi.header.generate to false.
  • Optionally override the path to the header to a custom one instead of the default one.

Advanced

You may override various aspects of cargo-c via settings in Cargo.toml under the package.metadata.capi key

[package.metadata.capi]
# Configures the minimum required cargo-c version. Trying to run with an
# older version causes an error.
min_version = "0.6.10"

Header Generation

[package.metadata.capi.header]
# Used as header file name. By default this is equal to the crate name.
# The name can be with or without the header filename extension `.h`
name = "new_name"
# Install the header into a subdirectory with the name of the crate. This
# is enabled by default, pass `false` or "" to disable it.
subdirectory = "libfoo-2.0/foo"
# Generate the header file with `cbindgen`, or copy a pre-generated header
# from the `assets` subdirectory. By default a header is generated.
generation = true
# Can be use to disable header generation completely.
# This can be used when generating dynamic modules instead of an actual library.
enabled = true

pkg-config File Generation

[package.metadata.capi.pkg_config]
# Used as the package name in the pkg-config file and defaults to the crate name.
name = "libfoo"
# Used as the pkg-config file name and defaults to the crate name.
filename = "libfoo-2.0"
# Used as the package description in the pkg-config file and defaults to the crate description.
description = "some description"
# Used as the package version in the pkg-config file and defaults to the crate version.
version = "1.2.3"
# Used as the Requires field in the pkg-config file, if defined
requires = "gstreamer-1.0, gstreamer-base-1.0"
# Used as the Requires.private field in the pkg-config file, if defined
requires_private = "gobject-2.0, glib-2.0 >= 2.56.0, gmodule-2.0"
# Strip the include search path from the last n components, useful to support installing in a
# subdirectory but then include with the path. By default it is 0.
strip_include_path_components = 1

Library Generation

[package.metadata.capi.library]
# Used as the library name and defaults to the crate name. This might get
# prefixed with `lib` depending on the target platform.
name = "new_name"
# Used as library version and defaults to the crate version. How this is used
# depends on the target platform.
version = "1.2.3"
# Used to install the library to a subdirectory of `libdir`.
install_subdir = "gstreamer-1.0"
# Used to disable versioning links when installing the dynamic library
versioning = false
# Instead of using semver, select a fixed number of version components for your SONAME version suffix:
# Setting this to 1 with a version of 0.0.0 allows a suffix of `.so.0`
# Setting this to 3 always includes the full version in the SONAME (indicate any update is ABI breaking)
#version_suffix_components = 2
# Add `-Cpanic=abort` to the RUSTFLAGS automatically, it may be useful in case
# something might panic in the crates used by the library.
rustflags = "-Cpanic=abort"
# Used to disable the generation of additional import library file in platforms
# that have the concept such as Windows
import_library = false

Custom data install

[package.metadata.capi.install.data]
# Used to install the data to a subdirectory of `datadir`. By default it is the same as `name`
subdirectory = "foodata"
# Copy the pre-generated data files found in {root_dir}/{from} to {datadir}/{to}/{matched subdirs}
# If {from} is a single path instead of a glob, the destination is {datapath}/{to}.
# datapath is {datadir}/{subdirectory}
asset = [{from="pattern/with/or/without/**/*", to="destination"}]
# Copy the pre-generated data files found in {OUT_DIR}/{from} to {includedir}/{to}/{matched subdirs}
# If {from} is a single path instead of a glob, the destination is {datapath}/{to}.
# datapath is {datadir}/{subdirectory}
generated = [{from="pattern/with/or/without/**/*", to="destination"}]

[package.metadata.capi.install.include]
# Copy the pre-generated includes found in {root_dir}/{from} to {includedir}/{to}/{matched subdirs}
# If {from} is a single path instead of a glob, the destination is {includepath}/{to}.
# includepath is {includedir}/{header.subdirectory}
asset = [{from="pattern/with/or/without/**/*", to="destination"}]
# Copy the pre-generated includes found in {OUT_DIR}/{from} to {includedir}/{to}/{matched subdirs}
# If {from} is a single path instead of a glob, the destination is {includedpath}/{to}.
# includepath is {includedir}/{header.subdirectory}
generated = [{from="pattern/with/or/without/**/*", to="destination"}]

Notes

Do not pass RUSTFLAGS that are managed by cargo through other means, (e.g. the flags driven by [profiles] or the flags driven by [target.<>]), cargo-c effectively builds as if the target is always explicitly passed.

Users

Status

  • cli
    • build command
    • install command
    • test command
    • cargo applet support
  • build targets
    • pkg-config generation
    • header generation (cbindgen integration)
  • staticlib support
  • cdylib support
  • Generate version information in the header
    • Make it tunable
  • Extra Cargo.toml keys
  • Better status reporting

Availability

Packaging status

Troubleshooting

Shared libraries are not built on musl systems

When running on a musl-based system (e.g. Alpine Linux), it could be that using the cdylib library type results in the following error (as reported here):

Error: CliError { error: Some(cannot produce cdylib for as the target x86_64-unknown-linux-musl does not support these crate types), exit_code: 101 }

This suggests that Rust was not built with crt-static=false and it typically happens if Rust has been installed through rustup.

Shared libraries can be enabled manually in this case, by editing the file .cargo/config like so:

# .cargo/config

[target.x86_64-unknown-linux-musl]
rustflags = [
    "-C", "target-feature=-crt-static",
]

However, it is preferred to install Rust through the system package manager instead of rustup (e.g. with apk add rust), because the provided package should already handle this (see e.g. here).

On Debian-like system the libdir includes the host triplet by default

In order to accomodate Debian's multiarch approach the cargo-c default for the libdir is lib/<triplet> on such system. Either pass an explicit --libdir or pass --target to return to the common libdir=lib default.

Acknowledgements

This software has been partially developed in the scope of the H2020 project SIFIS-Home with GA n. 952652.

Dependencies

~93MB
~2M SLoC