use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=wrapper.h");
println!("cargo:rerun-if-changed=build.rs");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let use_bundled = env::var("CARGO_FEATURE_BUNDLED").is_ok()
|| env::var("CARGO_FEATURE_STATIC").is_ok();
let is_docs_rs = env::var("DOCS_RS").is_ok();
if is_docs_rs {
println!("cargo:warning=Building on docs.rs - generating stub bindings");
println!("cargo:rustc-cfg=sdif_stub_bindings");
generate_stub_bindings(&out_dir);
return;
}
let (include_path, lib_path) = if use_bundled {
match try_build_bundled(&out_dir) {
Some(paths) => paths,
None => {
println!("cargo:warning=SDIF library not available - generating stub bindings");
println!("cargo:warning=The crate will compile but functions will not be available at runtime");
println!("cargo:rustc-cfg=sdif_stub_bindings");
generate_stub_bindings(&out_dir);
return;
}
}
} else {
match try_pkg_config() {
Some(paths) => paths,
None => {
println!("cargo:warning=pkg-config failed to find SDIF library");
match try_build_bundled(&out_dir) {
Some(paths) => {
println!("cargo:warning=Falling back to bundled build");
paths
}
None => {
println!("cargo:warning=SDIF library not available - generating stub bindings");
println!("cargo:warning=The crate will compile but functions will not be available at runtime");
println!("cargo:rustc-cfg=sdif_stub_bindings");
generate_stub_bindings(&out_dir);
return;
}
}
}
}
};
generate_bindings(&include_path, &out_dir);
if let Some(lib_path) = lib_path {
println!("cargo:rustc-link-search=native={}", lib_path.display());
}
if use_bundled || env::var("CARGO_FEATURE_STATIC").is_ok() {
println!("cargo:rustc-link-lib=static=sdif");
} else {
println!("cargo:rustc-link-lib=sdif");
}
}
fn try_pkg_config() -> Option<(PathBuf, Option<PathBuf>)> {
match pkg_config::Config::new()
.atleast_version("3.0")
.probe("sdif")
{
Ok(lib) => {
let include_path = lib.include_paths
.first()
.cloned()
.unwrap_or_else(|| PathBuf::from("/usr/include"));
let lib_path = lib.link_paths.first().cloned();
println!("cargo:info=Found SDIF via pkg-config");
Some((include_path, lib_path))
}
Err(e) => {
println!("cargo:warning=pkg-config error: {}", e);
None
}
}
}
fn try_build_bundled(out_dir: &PathBuf) -> Option<(PathBuf, Option<PathBuf>)> {
println!("cargo:info=Attempting to build SDIF from bundled source");
let sdif_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("sdif");
if !sdif_dir.exists() {
println!("cargo:warning=Bundled SDIF source not found at {:?}", sdif_dir);
return None;
}
let src_dir = sdif_dir.join("src");
let include_dir = sdif_dir.join("include");
if !src_dir.exists() || !include_dir.exists() {
println!("cargo:warning=Bundled SDIF source incomplete (missing src or include)");
return None;
}
let c_files: Vec<_> = match std::fs::read_dir(&src_dir) {
Ok(entries) => entries
.filter_map(|entry| {
let entry = entry.ok()?;
let path = entry.path();
if path.extension().map(|e| e == "c").unwrap_or(false) {
Some(path)
} else {
None
}
})
.collect(),
Err(_) => {
println!("cargo:warning=Failed to read bundled SDIF src directory");
return None;
}
};
if c_files.is_empty() {
println!("cargo:warning=No C source files found in bundled SDIF");
return None;
}
let mut build = cc::Build::new();
build
.files(&c_files)
.include(&include_dir)
.warnings(false) .opt_level(2);
if cfg!(target_os = "windows") {
build.define("WIN32", None);
}
if let Ok(types_path) = env::var("SDIFTYPES") {
build.define("SDIFTYPES_FILE", Some(types_path.as_str()));
}
build.compile("sdif");
for file in &c_files {
println!("cargo:rerun-if-changed={}", file.display());
}
Some((include_dir, Some(out_dir.clone())))
}
fn generate_bindings(include_path: &PathBuf, out_dir: &PathBuf) {
let bindings = bindgen::Builder::default()
.header("wrapper.h")
.clang_arg(format!("-I{}", include_path.display()))
.allowlist_function("Sdif.*")
.allowlist_function("_Sdif.*")
.allowlist_type("Sdif.*")
.allowlist_type("_Sdif.*")
.allowlist_type("eSdif.*")
.allowlist_var("Sdif.*")
.allowlist_var("eSdif.*")
.allowlist_type("SdifFileModeET")
.allowlist_var("eReadFile")
.allowlist_var("eWriteFile")
.allowlist_var("ePredefinedTypes")
.allowlist_var("eModeMask")
.allowlist_type("SdifDataTypeET")
.allowlist_var("eFloat4")
.allowlist_var("eFloat8")
.allowlist_var("eInt1")
.allowlist_var("eInt2")
.allowlist_var("eInt4")
.allowlist_var("eUInt1")
.allowlist_var("eUInt2")
.allowlist_var("eUInt4")
.allowlist_var("eText")
.derive_debug(true)
.derive_default(true)
.derive_copy(true)
.derive_eq(true)
.derive_hash(true)
.derive_partialeq(true)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate_comments(true)
.layout_tests(true)
.generate()
.expect("Failed to generate bindings");
let bindings_path = out_dir.join("bindings.rs");
bindings
.write_to_file(&bindings_path)
.expect("Failed to write bindings");
println!("cargo:info=Generated bindings at {:?}", bindings_path);
}
fn generate_stub_bindings(out_dir: &PathBuf) {
let stub_bindings = r#"
// Stub bindings generated because SDIF library was not available at build time.
// To use this crate, you must:
// 1. Install the SDIF library system-wide, OR
// 2. Download the SDIF source and place it in the sdif/ directory, then rebuild with --features bundled
//
// See the README.md for detailed instructions.
use std::os::raw::{c_char, c_int, c_void, c_double, c_float};
// Type aliases
pub type SdifSignature = u32;
pub type SdifFileT = *mut c_void;
pub type SdifFloat8 = c_double;
pub type SdifFloat4 = c_float;
// File mode enum
pub type SdifFileModeET = u32;
pub const SdifFileModeET_eReadFile: u32 = 1;
pub const SdifFileModeET_eWriteFile: u32 = 2;
pub const SdifFileModeET_ePredefinedTypes: u32 = 4;
pub const SdifFileModeET_eModeMask: u32 = 7;
// Data type enum
pub type SdifDataTypeET = u32;
pub const SdifDataTypeET_eFloat4: u32 = 0x0004;
pub const SdifDataTypeET_eFloat8: u32 = 0x0008;
pub const SdifDataTypeET_eInt1: u32 = 0x0001;
pub const SdifDataTypeET_eInt2: u32 = 0x0002;
pub const SdifDataTypeET_eInt4: u32 = 0x0004;
pub const SdifDataTypeET_eUInt1: u32 = 0x0101;
pub const SdifDataTypeET_eUInt2: u32 = 0x0102;
pub const SdifDataTypeET_eUInt4: u32 = 0x0104;
pub const SdifDataTypeET_eText: u32 = 0x0301;
// Stub function declarations - these will link but panic at runtime
extern "C" {
pub fn SdifGenInit(name: *const c_char) -> c_int;
pub fn SdifGenKill();
pub fn SdifFOpen(name: *const c_char, mode: SdifFileModeET) -> SdifFileT;
pub fn SdifFClose(file: SdifFileT) -> c_int;
pub fn SdifFReadGeneralHeader(file: SdifFileT) -> usize;
pub fn SdifFReadAllASCIIChunks(file: SdifFileT) -> usize;
pub fn SdifSignatureConst(a: c_char, b: c_char, c: c_char, d: c_char) -> SdifSignature;
pub fn SdifSizeofDataType(data_type: SdifDataTypeET) -> usize;
}
#[cfg(test)]
mod stub_warning {
#[test]
#[ignore]
fn warn_about_stubs() {
panic!("This is a stub build of sdif-sys. The SDIF library must be installed to run tests.");
}
}
"#;
let bindings_path = out_dir.join("bindings.rs");
std::fs::write(&bindings_path, stub_bindings)
.expect("Failed to write stub bindings");
println!("cargo:info=Generated stub bindings at {:?}", bindings_path);
}