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>
        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 (crate), 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.

  • 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

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