What is the best practice to use bindgen for a C library with complex configuration options?

Hello,
I'm working for write a safe abstraction layer with Rust for a C library with variety of configuration options, those options is defined via some C macro definitions.
I understand that we can use Builder::clang_arg method to add C macro definition to the C library when generating bindings, however, the problem is that I hope the final binding crate could provide corresponding bindings for each different config macros combinations of the underlying C library, instead of giving a fixed macro definition in the bindgen invocation.
Nevertheless, it seems that each time the binding crate is built, we must re-generate the binding file with bindgen, is it an appropriate usage of bindgen? Or are there any better ways to use bindgen?
Thanks

I would usually use a separate header file for all the configs (further more, this config header may also be generated from some other data source such as environment variables or cmake configure scripts, etc), something like:

// build.rs
fn main() {
    let bindings = bindgen::builder()
        .header("config.h")
        .header("libxyz.h")
        .generate();
}
// config.h
#define CONFIG_ENABLE_FOO 1
#define CONFIG_ENABLE_BAR 0
#define CONFIG_BAZ_MSG "hello world"

a slightly variant is to use a wrapper.h header:

// wrapper.h
#include "config.h"
#include "libxyz.h"

this is totally valid usage of bindgen.

there are typically two main approach to use bindgen:

  • generate bindings at build time in cargo build script;
    • for ffi libraries with APIs that are still changing from time to time;
    • or libraries with APIs of varying configurations;
  • pre-generate bindings offline, only once (or at least once for each major version release), using the bindgen cli tool;
    • mainly for libraries providing guaranteed stable APIs
2 Likes