cbindgen
cbindgen copied to clipboard
how to run cbindgen *after* rust compile?
Does anyone have recommendations for running cbindgen as post-build step? by using build.rs as recommended by the docs it makes it so when I have basic typos in my Rust code, I sometimes run into parse errors like this:
--- stderr
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseSyntaxError { crate_name: "...", src_path: "...lib.rs", error: Error("expected identifier") }', src/libcore/result.rs:1188:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
instead of pretty rust compiler errors like this:
error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `*`
--> src/lib.rs:18:14
|
18 | user_data: * mut c_void,
| ^ expected one of 8 possible tokens
there's some discussion of a postbuild.rs in cargo: https://github.com/rust-lang/cargo/issues/545 but also quite a bit of pushback on making cargo build more complex. It would be good to know if other folks have run into this and what solutions might be better than just temporarily renaming build.rs to disable cbindgen, then adding it back in once the Rust code works well.
Yes, I also gave up on the build.rs method for that reason. Instead I wrote a Makefile.
what solutions might be better than just temporarily renaming
build.rsto disable cbindgen
You can have the build.rs check an env var with std::env::var() to decide whether it should disable cbindgen or not. Make sure to also emit cargo:rerun-if-env-changed for that var.
cbindgen when ran on build.rs could potentially run rustc -Z syntax-only or something to provide a nicer error, but...
I expect the simplest solution if you want to do it from build.rs would be to create a crate that depends on the crate that you want to run cbindgen on, and then run cbindgen on that dependency, or something.
I too was hit by this. I have a workaround that I use locally that solves it:
extern crate cbindgen;
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
match cbindgen::generate(&crate_dir) {
Ok(gen) => gen,
Err(e) => match e {
// Ignore syntax errors because those will be handled later on by cargo build.
cbindgen::Error::ParseSyntaxError {
crate_name: _,
src_path: _,
error: _,
} => return,
_ => panic!("{:?}", e),
},
}.write_to_file("target/header.h");
}
It just ignores parse errors because those would be handled by the rest of the build process anyway... I have no idea if it breaks other things, but it works for me and makes development a bit less annoying. :)
Also, I have no idea why, but I wasn't able to use unwrap_or_else nor e.kind(), so we get this slightly ugly match.
Maybe it should be added to the README?
Adjusted @tasn's answer to read a bit cleaner:
match cbindgen::generate(&crate_dir) {
Ok(bindings) => bindings.write_to_file("target/header.h"),
Err(cbindgen::Error::ParseSyntaxError { .. }) => return, // ignore in favor of cargo's syntax check
Err(err) => panic!("{:?}", err)
};
It's much cleaner, thanks for sharing!
This is so much better. It should really be added to the README! It's impossible to actually write code while using cbindgen, the recommended way.