use std::{env::current_dir, fs::create_dir_all, process::Command}; use bindgen::callbacks::ParseCallbacks; /// custom parser callbacks which implement passthrough to `CargoCallbacks` but avoid emitting `rerun-if-changed` entries for files that /// are generated by the build script, otherwise cargo will think that those files are stale every single build and will thus re-run this /// build script every single time, which is undesirable. #[derive(Debug)] struct XedBindgenParseCallbacks { xed_install_include_dir: String, cargo_callbacks: bindgen::CargoCallbacks, } impl ParseCallbacks for XedBindgenParseCallbacks { fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior { self.cargo_callbacks.will_parse_macro(name) } fn generated_name_override( &self, item_info: bindgen::callbacks::ItemInfo<'_>, ) -> Option { self.cargo_callbacks.generated_name_override(item_info) } fn generated_link_name_override( &self, item_info: bindgen::callbacks::ItemInfo<'_>, ) -> Option { self.cargo_callbacks.generated_link_name_override(item_info) } fn int_macro(&self, name: &str, value: i64) -> Option { self.cargo_callbacks.int_macro(name, value) } fn str_macro(&self, name: &str, value: &[u8]) { self.cargo_callbacks.str_macro(name, value) } fn func_macro(&self, name: &str, value: &[&[u8]]) { self.cargo_callbacks.func_macro(name, value) } fn enum_variant_behavior( &self, enum_name: Option<&str>, original_variant_name: &str, variant_value: bindgen::callbacks::EnumVariantValue, ) -> Option { self.cargo_callbacks .enum_variant_behavior(enum_name, original_variant_name, variant_value) } fn enum_variant_name( &self, enum_name: Option<&str>, original_variant_name: &str, variant_value: bindgen::callbacks::EnumVariantValue, ) -> Option { self.cargo_callbacks .enum_variant_name(enum_name, original_variant_name, variant_value) } fn item_name(&self, original_item_name: &str) -> Option { self.cargo_callbacks.item_name(original_item_name) } fn header_file(&self, filename: &str) { self.cargo_callbacks.header_file(filename) } fn include_file(&self, filename: &str) { if filename.starts_with(self.xed_install_include_dir.as_str()) { return; } self.cargo_callbacks.include_file(filename) } fn read_env_var(&self, key: &str) { self.cargo_callbacks.read_env_var(key) } fn blocklisted_type_implements_trait( &self, name: &str, derive_trait: bindgen::callbacks::DeriveTrait, ) -> Option { self.cargo_callbacks .blocklisted_type_implements_trait(name, derive_trait) } fn add_derives(&self, info: &bindgen::callbacks::DeriveInfo<'_>) -> Vec { self.cargo_callbacks.add_derives(info) } fn process_comment(&self, comment: &str) -> Option { self.cargo_callbacks.process_comment(comment) } fn field_visibility( &self, info: bindgen::callbacks::FieldInfo<'_>, ) -> Option { self.cargo_callbacks.field_visibility(info) } fn wrap_as_variadic_fn(&self, name: &str) -> Option { self.cargo_callbacks.wrap_as_variadic_fn(name) } } fn main() { let cwd_path = current_dir().unwrap(); let cwd = cwd_path.to_str().unwrap(); let out_dir = std::env::var("OUT_DIR").unwrap(); let xed_build_dir = format!("{}/xed", out_dir); let xed_install_dir = format!("{}/xed_install", out_dir); let num_jobs_str = std::env::var("NUM_JOBS").unwrap_or_else(|_| "1".to_string()); let profile = std::env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); println!("cargo:rerun-if-changed-env=OUT_DIR"); println!("cargo:rerun-if-changed-env=PROFILE"); println!("cargo:rerun-if-changed={}/xed", cwd); println!("cargo:rerun-if-changed={}/mbuild", cwd); create_dir_all(xed_build_dir.as_str()).unwrap(); let mut cmd = Command::new(format!("{}/xed/mfile.py", cwd)); cmd.arg("-j") .arg(num_jobs_str.as_str()) .arg("--static") .arg(format!("--install-dir={}", xed_install_dir)) .env("PYTHONDONTWRITEBYTECODE", "1") .current_dir(xed_build_dir.as_str()); if profile == "release" { cmd.arg("--opt=3"); } else { cmd.arg("--opt=0"); } cmd.arg("install"); let exit_code = cmd.spawn().unwrap().wait().unwrap(); assert!(exit_code.success()); println!("cargo:rustc-link-search=native={}/lib", xed_install_dir); println!("cargo:rustc-link-lib=static=xed"); let xed_include_dir = format!("{}/include", xed_install_dir); let wrapped_static_fns_file_path = format!("{}/xed_wrapped_static_fns.c", out_dir); let bindings = bindgen::Builder::default() .wrap_static_fns(true) .wrap_static_fns_path(wrapped_static_fns_file_path.as_str()) .clang_arg(format!("-I{}", xed_include_dir)) .clang_arg("-DXED_ENCODER") .clang_arg("-DXED_DECODER") .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: false, }) .use_core() .header("xed.h") .parse_callbacks(Box::new(XedBindgenParseCallbacks { xed_install_include_dir: xed_include_dir.clone(), cargo_callbacks: bindgen::CargoCallbacks::new(), })) .generate() .unwrap(); bindings .write_to_file(format!("{}/xed.rs", out_dir)) .unwrap(); cc::Build::new() .file(wrapped_static_fns_file_path.as_str()) .flag("-DXED_ENCODER") .flag("-DXED_DECODER") .flag_if_supported("-Wno-duplicate-decl-specifier") .include(xed_include_dir.as_str()) .include(cwd) .compile("xed_wrapped_static_fns"); }