//! builds the engine archives and generates FFI bindings for femm-sys. use std::env; use std::path::PathBuf; use std::process::Command; fn main() { let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let repo_root = manifest.parent().unwrap().parent().unwrap().to_path_buf(); let ffi_dir = repo_root.join("ffi"); let build_dir = repo_root.join("build").join("ffi"); // re-run on changes to any FFI header, build script, or solver source dir. println!("cargo:rerun-if-changed={}", ffi_dir.display()); println!("cargo:rerun-if-changed={}", repo_root.join("scripts/macos/build_ffi.sh").display()); println!("cargo:rerun-if-changed={}", repo_root.join("scripts/windows/build_ffi.ps1").display()); println!("cargo:rerun-if-changed={}", repo_root.join("scripts/windows/_toolchain.ps1").display()); for engine in ["fkn", "belasolv", "csolv", "hsolv", "liblua", "compat"] { println!("cargo:rerun-if-changed={}", repo_root.join(engine).display()); } let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); let status = match target_os.as_str() { "macos" => Command::new("bash") .arg(repo_root.join("scripts/macos/build_ffi.sh")) .status() .expect("failed to invoke bash for engine build script"), "windows" => { let script = repo_root.join("scripts/windows/build_ffi.ps1"); Command::new("pwsh") .args([ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", ]) .arg(&script) .status() .expect("failed to invoke pwsh for engine build script") } other => panic!("femm-sys: no engine build script wired up for target_os={other}"), }; if !status.success() { panic!("engine build script exited with {status}"); } println!("cargo:rustc-link-search=native={}", build_dir.display()); for lib in ["femm_mag", "femm_elec", "femm_heat", "femm_curr"] { println!("cargo:rustc-link-lib=static={lib}"); } match (target_os.as_str(), target_env.as_str()) { ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"), ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"), ("windows", "gnullvm") => println!("cargo:rustc-link-lib=dylib=c++"), ("windows", "msvc") => { /* MSVC links the C++ runtime automatically */ } _ => println!("cargo:rustc-link-lib=dylib=stdc++"), } let bindings = bindgen::Builder::default() .header(manifest.join("wrapper.h").to_str().unwrap()) .clang_arg(format!("-I{}", ffi_dir.display())) .allowlist_function("femm_.*") .allowlist_type("Femm.*") .allowlist_var("FEMM_.*") .derive_default(true) .derive_copy(true) .derive_debug(true) .layout_tests(false) .generate() .expect("bindgen failed to generate FFI bindings"); let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); bindings.write_to_file(&out).expect("failed to write bindings.rs"); }