use std::{
env,
path::{Path, PathBuf},
process::Command,
};
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=cbindgen.toml");
let fskit_enabled = env::var_os("CARGO_FEATURE_FSKIT").is_some();
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if !fskit_enabled {
return;
}
if target_os != "macos" {
return;
}
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let swift_dir = manifest_dir.join("swift").join("HeddleFSKit");
let swift_src = swift_dir.join("HeddleFSKit.swift");
let bridging = swift_dir.join("HeddleFSKit-Bridging.h");
let c_abi_src = manifest_dir.join("src").join("fskit").join("c_abi.rs");
println!("cargo:rerun-if-changed={}", swift_src.display());
println!("cargo:rerun-if-changed={}", c_abi_src.display());
regenerate_bridging_header(&manifest_dir, &bridging);
println!("cargo:rerun-if-changed={}", bridging.display());
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let static_lib = out_dir.join("libHeddleFSKit.a");
let object = out_dir.join("HeddleFSKit.o");
let target = swift_target_triple();
let swiftc = env::var("HEDDLE_SWIFTC").unwrap_or_else(|_| "swiftc".into());
let status = Command::new(&swiftc)
.args([
"-parse-as-library",
"-emit-object",
"-static",
"-O",
"-target",
&target,
"-module-name",
"HeddleFSKit",
"-import-objc-header",
])
.arg(&bridging)
.args(["-o"])
.arg(&object)
.arg(&swift_src)
.status()
.expect("failed to invoke swiftc; install Xcode command line tools");
assert!(
status.success(),
"swiftc failed for HeddleFSKit.swift (status: {status})"
);
let status = Command::new("ar")
.arg("crus")
.arg(&static_lib)
.arg(&object)
.status()
.expect("failed to invoke ar");
assert!(status.success(), "ar failed assembling libHeddleFSKit.a");
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=HeddleFSKit");
let toolchain = swift_runtime_path();
println!("cargo:rustc-link-search=native={toolchain}");
println!("cargo:rustc-link-arg=-Wl,-rpath,{toolchain}");
println!("cargo:rustc-link-arg=-weak_framework");
println!("cargo:rustc-link-arg=FSKit");
println!("cargo:rustc-link-arg=-framework");
println!("cargo:rustc-link-arg=Foundation");
}
#[cfg(feature = "fskit")]
fn regenerate_bridging_header(manifest_dir: &Path, output: &Path) {
let config = cbindgen::Config::from_file(manifest_dir.join("cbindgen.toml"))
.expect("failed to load cbindgen.toml");
let c_abi = manifest_dir.join("src").join("fskit").join("c_abi.rs");
let bindings = cbindgen::Builder::new()
.with_src(&c_abi)
.with_config(config)
.generate()
.expect("cbindgen failed to generate FSKit bridging header");
bindings.write_to_file(output);
}
#[cfg(not(feature = "fskit"))]
fn regenerate_bridging_header(_: &Path, _: &Path) {
unreachable!("regenerate_bridging_header invoked without fskit feature");
}
fn swift_target_triple() -> String {
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "aarch64".into());
let normalized = match arch.as_str() {
"aarch64" => "arm64",
"x86_64" => "x86_64",
other => other,
};
format!("{normalized}-apple-macos14.0")
}
fn swift_runtime_path() -> String {
env::var("HEDDLE_SWIFT_RUNTIME").unwrap_or_else(|_| {
"/Applications/Xcode.app/Contents/Developer/Toolchains/\
XcodeDefault.xctoolchain/usr/lib/swift/macosx"
.into()
})
}