Integrating Automatically Generated FFI Bindings

There are a number of tools to automatically generate bindings between Rust and different foreign languages.

  1. bindgen
  2. cbindgen
  3. cxx

bindgen

bindgen is a tool to automatically generate Rust bindings from C headers. As such, integrating bindgen via a build-script works well and their doesn't seem to be a need to create CMake rules for generating the bindings.

cbindgen integration

⚠️⚠️⚠️ EXPERIMENTAL ⚠️⚠️⚠️

cbindgen is a tool that generates C/C++ headers from Rust code. When compiling C/C++ code that #includes such generated headers the buildsystem must be aware of the dependencies. Generating the headers via a build-script is possible, but Corrosion offers no guidance here.

Instead, Corrosion offers an experimental function to add CMake rules using cbindgen to generate the headers. This is not available on a stable released version yet, and the details are subject to change.

corrosion_cbindgen(
        TARGET <imported_target_name>
        CARGO_PACKAGE <cargo_package_name>
        HEADER_NAME <output_header_name>
        [MANIFEST_DIRECTORY <package_manifest_directory>]
        [CBINDGEN_VERSION <version>]
        [FLAGS <flag1> ... <flagN>]
)

A helper function which uses cbindgen to generate C/C++ bindings for a Rust crate. If cbindgen is not in PATH the helper function will automatically try to download cbindgen and place the built binary into CMAKE_BINARY_DIR. The binary is shared between multiple invocations of this function.

  • TARGET: The name of an imported Rust library target, for which bindings should be generated. If the target was not previously imported by Corrosion, because the crate only produces an rlib, you must additionally specify MANIFEST_DIRECTORY.

  • CARGO_PACKAGE: The name of the Rust cargo package that contains the library target. Note, that cbindgen expects this package name using the --crate parameter. If not specified, the TARGET will be used instead.

  • MANIFEST_DIRECTORY: Directory of the package defining the library crate bindings should be generated for. If you want to avoid specifying MANIFEST_DIRECTORY you could add a staticlib target to your package manifest as a workaround to make corrosion import the crate.

  • HEADER_NAME: The name of the generated header file. This will be the name which you include in your C/C++ code (e.g. #include "myproject/myheader.h" if you specify HEADER_NAME "myproject/myheader.h"`.

  • CBINDGEN_VERSION: Version requirement for cbindgen. Exact semantics to be specified. Currently not implemented.

  • FLAGS: Arbitrary other flags for cbindgen. Run cbindgen --help to see the possible flags.

Current limitations

  • Cbindgens (optional) macro expansion feature internally actually builds the crate / runs the build script. For this to work as expected in all cases, we probably need to set all the same environment variables as when corrosion builds the crate. However the crate is a library, so we would need to figure out which target builds it - and if there are multiple, potentially generate bindings per-target? Alternatively we could add support of setting some environment variables on rlibs, and pulling that information in when building the actual corrosion targets Alternatively we could restrict corrosions support of this feature to actual imported staticlib/cdylib targets.

Current limitations

  • The current version regenerates the bindings more often then necessary to be on the safe side, but an upstream PR is open to solve this in a future cbindgen version.

cxx integration

⚠️⚠️⚠️ EXPERIMENTAL ⚠️⚠️⚠️

cxx is a tool which generates bindings for C++/Rust interop.

corrosion_add_cxxbridge(cxx_target
        CRATE <imported_target_name>
        [FILES <file1.rs> <file2.rs>]
)

Adds build-rules to create C++ bindings using the cxx crate.

Arguments:

  • cxxtarget: Name of the C++ library target for the bindings, which corrosion will create.
  • FILES: Input Rust source file containing #[cxx::bridge].
  • CRATE: Name of an imported Rust target. Note: Parameter may be renamed before release

Currently missing arguments

The following arguments to cxxbridge currently have no way to be passed by the user:

  • --cfg
  • --cxx-impl-annotations
  • --include

The created rules approximately do the following:

  • Check which version of cxx the Rust crate specified by the CRATE argument depends on.
  • Check if the exact same version of cxxbridge-cmd is installed (available in PATH)
  • If not, create a rule to build the exact same version of cxxbridge-cmd.
  • Create rules to run cxxbridge and generate
    • The rust/cxx.h header
    • A header and source file for each of the files specified in FILES
  • The generated sources (and header include directories) are added to the cxxtarget CMake library target.

Limitations

We currently require the CRATE argument to be a target imported by Corrosion, however, Corrosion does not import rlib only libraries. As a workaround users can add staticlib to their list of crate kinds. In the future this may be solved more properly, by either adding an option to also import Rlib targets (without build rules) or by adding a MANIFEST_PATH argument to this function, specifying where the crate is.

Contributing

Specifically some more realistic test / demo projects and feedback about limitations would be welcome.