extern crate bindgen; extern crate cc; use std::{ env::{self, VarError}, ffi::OsStr, iter, path::PathBuf, }; fn main() { println!("cargo:rustc-link-lib=SFCGAL"); let mut cargo_metadata = Vec::with_capacity(64); let link_libs_opt = env_var("SFCGAL_LINK_LIBS"); let link_paths_opt = env_var("SFCGAL_LINK_PATHS"); let include_paths_opt = env_var("SFCGAL_INCLUDE_PATHS"); if let Some(link_paths) = link_paths_opt { let meta = link_paths .split(',') .map(str::trim) .filter(|x| !x.is_empty()) .flat_map(|path| { let out = iter::once(format!("cargo:rustc-link-search={}", path)); #[cfg(target_os = "macos")] { out.chain(iter::once(format!( "cargo:rustc-link-search=framework={}", path ))) } #[cfg(not(target_os = "macos"))] { out } }); cargo_metadata.extend(meta); } if let Some(link_libs) = link_libs_opt { cargo_metadata.extend( process_library_list( link_libs .split(',') .map(str::trim) .filter(|x| !x.is_empty()), ) .map(|l| format!("cargo:rustc-link-lib={}", l)), ); } let include_paths: Vec<_> = match include_paths_opt { Some(include_paths) => include_paths .split(',') .map(str::trim) .filter(|x| !x.is_empty()) .map(PathBuf::from) .collect(), None => vec![PathBuf::from("/usr/include")], }; { let mut build = cc::Build::new(); build.file("src/wrapper.c"); for include_path in include_paths.iter() { build.include(include_path); } build.compile("sfcgalwrapper"); } let bindings = bindgen::Builder::default() .rust_target(bindgen::RustTarget::Stable_1_33) .header("src/wrapper.h") .clang_args( include_paths .iter() .map(|path| format!("-I{}", path.display())), ) .allowlist_type("sfcgal_.*$") .allowlist_var("sfcgal_.*$") .allowlist_function("sfcgal_.*$") .allowlist_type("w_sfcgal_.*$") .allowlist_var("w_sfcgal_.*$") .allowlist_function("w_sfcgal_.*$") .size_t_is_usize(true) .generate() .expect("Unable to generate bindings"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); for meta in cargo_metadata.into_iter() { println!("{}", meta); } } fn process_library_list( libs: impl IntoIterator>, ) -> impl Iterator { libs.into_iter().map(|x| { let mut path: PathBuf = x.into(); let extension = path.extension().and_then(OsStr::to_str).unwrap_or_default(); let is_framework = extension.eq_ignore_ascii_case("framework"); const LIB_EXTS: [&str; 7] = ["so", "a", "dll", "lib", "dylib", "framework", "tbd"]; if is_framework || LIB_EXTS.iter().any(|e| e.eq_ignore_ascii_case(extension)) { path.set_extension(""); } path.file_name() .and_then(|f| { f.to_str().map(|f| { if is_framework { format!("framework={}", f) } else { f.to_owned() } }) }) .expect("Invalid library name") }) } fn env_var>(key: K) -> Option { match env::var(key) { Ok(val) => Some(val), Err(VarError::NotPresent) => None, Err(VarError::NotUnicode(_)) => panic!("the value of environment variable is not Unicode"), } }