Rust bindgen
What's bindgen?
bindgen is a tool which generates Rust FFI to C/C++ libraries automatically. It's quite useful when we want to use a C/C++ library in Rust. For example, PyTorch provides C library for users which don't want to use Python. With bindgen, we can quickly create Rust binding of PyTorch C library from C header(see tch-rs)
Use bindgen
The recommended way to use bindgen is using it in build.rs
. build.rs
is a Rust file placed in the root of a package which is used to integrate third-party libraries or user customized tools to Rust compiling process. Cargo will compile and execute build.rs
first and then build the package.
Because many C/C++ headers have platform-specfic features, with using bindgen inside build.rs
, we can generate bindings for the current target on-the-fly. Other users can also use your package by generating bindings for their platform.
In the following sections, we will take onnxruntime C library as an example, generate Rust bindings using bindgen.
Add bindgen as a build dependency
First, we have to add bindgen as the build dependency. A build dependency is the dependency which is only used in building(see this section in The Cargo Book).
|
|
Create a wrapper.h Header
Because some libraries have more than one headers, we can include those headers in wrapper.h
and take wrapper.h
as an entrypoint for bindgen.
In our example, onnxruntime's header file can be found at https://github.com/microsoft/onnxruntime/blob/master/include/onnxruntime/core/session/onnxruntime_c_api.h. So we can just download it and then use it in build.rs
.
Create build.rs file
Create build.rs
at the project root, Cargo will automatically compile and execute it before compiling the rest of the project.
|
|
Run cargo build
, and then the bindings to onnxruntime are generated, you can find it at src/bindings/[os]/[arch]/bindings.rs
.
Use generated bindings
include!
macro can be used to dump the generated bindings into crate's main entry point.We can add different cfg headers for bindings of different platforms.
|
|
Because onnxruntime's symbols are defined in C, they may not follow Rust's style convention. We can suppress warnings by a bunch of #![allow(...)]
pragmas.
Test bindings
The generated code contains some tests. We can also add our test at src/lib.rs
:
|
|
Run cargo test
, cargo will execute all tests defined in bindings.rs
and lib.rs
.
test bindgen_test_layout_wait__bindgen_ty_2 ... ok
test tests::it_works ... ok
test bindgen_test_layout_wait__bindgen_ty_1 ... ok
test result: ok. 81 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
So far, we have successfully generated bindings for onnxruntime and tested them. With bindgen, we can also customize the generated bindings. For more options of bindgen, check https://rust-lang.github.io/rust-bindgen/customizing-generated-bindings.html.
More about build.rs
We just generated bindings for onnxruntime. But that's not enough if we want to use it in production, especially when you want to deploy the application using the bindings to different platforms. This is where build.rs
comes into play.
As we mentioned above, build.rs
is compiled and executed before the package is compiled. So we can add somethings like platform-specific configuration, library downloading, etc. to build.rs
. The full reference of build.rs
is here.