rust-lang / rust-bindgen

Automatically generates Rust FFI bindings to C (and some C++) libraries.
https://rust-lang.github.io/rust-bindgen/
BSD 3-Clause "New" or "Revised" License
4.46k stars 694 forks source link

wrap_static_fns is missing from bindgen::Builder #2959

Open hoangbv15 opened 3 weeks ago

hoangbv15 commented 3 weeks ago

Hi, I think I'm going crazy, I have been trying to use an external C library in my Rust program without success. The C library is uhubctl, source code is here https://github.com/mvp/uhubctl/tree/master

As you can see most of the important functions are declared as static functions, for instance

static int usb_find_hubs(void)

And I need to expose them to my Rust program.

After struggling for a while, I realise that the bindings.rs file generated by Bindgen doesn't contain anything, and I also realised that I need the wrap_static_fns flag. Allegedly it should be a method in bindgen::Builder.

However, my VSCode keeps saying that the method is not found, and cargo build gives the same result.

error[E0599]: no method named `wrap_static_fns` found for struct `bindgen::Builder` in the current scope

If I run the command line version

bindgen libs/uhubctl.h -o src/bindings.rs -- -I//opt/homebrew/Cellar/libusb/1.0.27/include/libusb-1.0 -DPROGRAM_VERSION=\"2.6.0\" --wrap-static-fns

I get

panicked at /Users/hoang.bui/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-cli-0.70.1/main.rs:45:36:
Unable to generate bindings: ClangDiagnostic("error: unsupported option '--wrap-static-fns'\n")

I am also a total beginner in Rust so I'm completely lost.

I am on macOS Sonoma 14.6.1, rustc 1.82.0 bindgen 0.70.1

Here is my build.rs

/* File: build.rs */
extern crate bindgen;
extern crate cc;
use std::path::PathBuf;

fn main() {
    // Tell cargo to invalidate the built crate whenever the wrapper changes
    println!("cargo:rerun-if-changed=libs/uhubctl.c");

    let libusb_header_path_flag = "-I/opt/homebrew/Cellar/libusb/1.0.27/include/libusb-1.0";
    let uhubctl_version_flag = "-DPROGRAM_VERSION=\"2.6.0\"";

    let bindings = bindgen::Builder::default()
        // The input header we would like to generate bindings for.
        .header("libs/uhubctl.h")
        .wrap_static_fns(true)
        .clang_arg(libusb_header_path_flag)
        .clang_arg(uhubctl_version_flag)
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        // Finish the builder and generate the bindings.
        .generate()
        .expect("Unable to generate bindings");

    // Write the bindings to the src/bindings.rs file.
    let out_path = PathBuf::from("src");
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");

    // Build static library
    cc::Build::new()
        .file("libs/uhubctl.c")
        .flag(libusb_header_path_flag)
        .flag(uhubctl_version_flag)
        .compile("uhubctl.a");
}

Any help would be much appreciated!

pvdrz commented 3 weeks ago

My first bet is that you're not actually using 0.70.1 as the released crate clearly has this method: https://docs.rs/bindgen/0.70.1/bindgen/struct.Builder.html#method.wrap_static_fns

The command line version is also wrong as you're passing --wrap-static-fns after the -- which means it is being passed as a CLI argument to clang:

Usage: bindgen <FLAGS> <OPTIONS> <HEADER> -- <CLANG_ARGS>...

Arguments:
  [HEADER]         C or C++ header file
  [CLANG_ARGS]...  Arguments to be passed straight through to clang

That means that you must pass --wrap-static-fns before the --.

hoangbv15 commented 3 weeks ago

@pvdrz you are right about the command line, I changed it to

bindgen libs/uhubctl.h -o src/bindings.rs --experimental --wrap-static-fns -- -I//opt/homebrew/Cellar/libusb/1.0.27/include/libusb-1.0 -DPROGRAM_VERSION=\"2.6.0\"

and it worked.

However I'm not sure about the crate, I believe I am on 0.70.1. Here's my Cargo.toml

[package]
name = "android-charge-limiter-rust"
version = "0.1.0"
edition = "2021"

[build-dependencies]
bindgen = "0.70.1"
cc = "1.0"

[dependencies]

and my Cargo.lock also says 0.70.1

[[package]]
name = "bindgen"
version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
 "bitflags",
 "cexpr",
 "clang-sys",
 "itertools",
 "log",
 "prettyplease",
 "proc-macro2",
 "quote",
 "regex",
 "rustc-hash",
 "shlex",
 "syn",
]

I tried deleting Cargo.lock and target/ folder and rebuild, the same result as before

I shift-click on bindgen::Builder to see the underlying code, it takes me to a path in ~/.cargo/ that looks like the right version 0.70.1, but the wrap_static_fns function is nowhere to be found Image

Here is the content of the ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-0.70.1 folder if it helps bindgen-0.70.1.zip

And here is my project's source code, maybe someone can try to reproduce android-charge-limiter-rust.zip

pvdrz commented 3 weeks ago

I think that's because you're missing the experimental feature on your bindgen dependency.

hoangbv15 commented 3 weeks ago

@pvdrz could you point to an example or tutorial? The tutorial at https://rust-lang.github.io/rust-bindgen/introduction.html as well as https://github.com/rust-lang/rust-bindgen/discussions/2405 does not have anything on this

Edit: After some Googling, I followed this guide roughly to add a feature, as a shot in the dark https://doc.rust-lang.org/cargo/reference/features.html I changed the bindgen line in Cargo.toml to

bindgen = { version = "0.70.1", features = ["experimental"] }

and it worked.

I think these things need to be mentioned in the pages that talk about wrap_static_fns or anything that requires this feature

pvdrz commented 2 days ago

yeah this might have been an oversight. But the next version of bindgen won't require the experimental to use the feature.