use std::borrow::Cow; use std::env; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; fn main() { let target = env::var("TARGET").expect("selinux-sys: Environment variable 'TARGET' was not defined."); match get_target_os(&target).as_deref() { None => return, // Do not build anything for bare metal architectures. Some("linux") | Some("android") | Some("androideabi") | Some("dragonfly") | Some("freebsd") | Some("netbsd") | Some("openbsd") | Some("hurd") => { // Continue, probably supported. } _ => return, // Do not build anything, probably unsupported. } let out_dir = env::var_os("OUT_DIR") .map(PathBuf::from) .expect("fts-sys: Environment variable 'OUT_DIR' was not defined."); println!("cargo:root={}", out_dir.to_str().unwrap()); let sysroot = target_env_var_os("SYSROOT", &target).map(PathBuf::from); generate_bindings(&target, sysroot.as_deref(), &out_dir) } fn target_env_var_os(name: &str, target: &str) -> Option { rerun_if_env_changed(name, target); let target_underscores = target.replace('-', "_"); env::var_os(format!("{name}_{target}")) .or_else(|| env::var_os(format!("{name}_{target_underscores}"))) .or_else(|| env::var_os(format!("TARGET_{name}"))) .or_else(|| env::var_os(name)) } fn rerun_if_env_changed(name: &str, target: &str) { let target_underscores = target.replace('-', "_"); println!("cargo:rerun-if-env-changed={name}_{target}"); println!("cargo:rerun-if-env-changed={name}_{target_underscores}"); println!("cargo:rerun-if-env-changed=TARGET_{name}"); println!("cargo:rerun-if-env-changed={name}"); } fn get_target_os(target: &str) -> Option { let components: Vec<_> = target.split('-').collect(); let os_index = match components.len() { 2 => { // e.g., aarch64-fuchsia, wasm32-wasi, x86_64-fuchsia if components[1] == "none" { return None; // Bare metal target. } 1 } 3 | 4 => { // e.g., aarch64-unknown-freebsd, aarch64-unknown-linux-gnu if components[1] == "none" || components[2] == "none" { return None; // Bare metal target. } 2 } _ => panic!("Unrecognized target triplet '{target}'"), }; Some(String::from(components[os_index])) } // See: https://github.com/rust-lang/rust-bindgen/issues/2136 fn translate_rustc_target_to_clang(rustc_target: &str) -> Cow { if let Some(suffix) = rustc_target.strip_prefix("riscv32") { let suffix = suffix.trim_start_matches(|c| c != '-'); Cow::Owned(format!("riscv32{suffix}")) } else if let Some(suffix) = rustc_target.strip_prefix("riscv64") { let suffix = suffix.trim_start_matches(|c| c != '-'); Cow::Owned(format!("riscv64{suffix}")) } else if let Some(suffix) = rustc_target.strip_prefix("aarch64-apple-") { Cow::Owned(format!("arm64-apple-{suffix}")) } else if let Some(prefix) = rustc_target.strip_suffix("-espidf") { Cow::Owned(format!("{prefix}-elf")) } else { Cow::Borrowed(rustc_target) } } fn generate_bindings(target: &str, sysroot: Option<&Path>, out_dir: &Path) { let clang_target = translate_rustc_target_to_clang(target); let mut builder = bindgen::Builder::default() .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .default_enum_style(bindgen::EnumVariation::ModuleConsts) .default_macro_constant_type(bindgen::MacroTypeVariation::Signed) .size_t_is_usize(true) .derive_debug(true) .derive_copy(true) .impl_debug(true) //.clang_arg("-D_FILE_OFFSET_BITS=64") .clang_arg(format!("--target={clang_target}")); if let Some(sysroot) = sysroot.map(Path::as_os_str).map(OsStr::to_str) { let sysroot = sysroot.expect("SYSROOT is not encoded in UTF-8"); builder = builder.clang_arg(format!("--sysroot={sysroot}")); } builder = builder .allowlist_function("fts_(open|read|children|set|close)") .allowlist_type("FTSENT") .blocklist_type("__.+") .blocklist_type("(FTS|stat|timespec|dev_t|ino_t|nlink_t)") .allowlist_var("FTS_.+") .header("src/fts-sys.h"); let bindings = builder .generate() .expect("fts-sys: Failed to generate Rust bindings for 'fts.h'."); bindings .write_to_file(out_dir.join("fts-sys.rs")) .expect("fts-sys: Failed to write 'fts-sys.rs'.") }