use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let vendored_src = manifest_dir.join("vendor").join("src");
let (nim_src, include_dir) = if vendored_src.is_dir() {
(vendored_src, manifest_dir.join("vendor").join("include"))
} else {
let nim_root = manifest_dir
.parent()
.expect("fasttree-sys powinno leżeć obok fasttree.nimble");
(nim_root.join("src"), nim_root.join("include"))
};
let capi_entry = nim_src.join("fasttree").join("capi.nim");
assert!(
capi_entry.exists(),
"nie znaleziono {} — uruchom scripts/vendor-nim-src.sh (przy publikacji) \
albo upewnij się, że fasttree-sys leży obok katalogów src/ i include/ w repo",
capi_entry.display()
);
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let nimcache = out_dir.join("nimcache");
println!("cargo:rerun-if-changed={}", nim_src.display());
println!("cargo:rerun-if-changed={}", include_dir.join("fasttree.h").display());
println!("cargo:rerun-if-env-changed=FASTTREE_NIM_BIN");
let use_blake3 = env::var("CARGO_FEATURE_BLAKE3").is_ok();
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".into());
let nim_bin = env::var("FASTTREE_NIM_BIN").unwrap_or_else(|_| "nim".into());
let lib_path = out_dir.join("libfasttree.a");
let mut path_arg = std::ffi::OsString::from("--path:");
path_arg.push(&nim_src);
let mut nimcache_arg = std::ffi::OsString::from("--nimcache:");
nimcache_arg.push(&nimcache);
let mut out_arg = std::ffi::OsString::from("-o:");
out_arg.push(&lib_path);
let mut cmd = Command::new(&nim_bin);
cmd.arg("c")
.arg("--app:staticlib")
.arg("--noMain")
.arg("--hints:off")
.arg(path_arg)
.arg(nimcache_arg)
.arg(out_arg);
apply_cross_compilation_flags(&mut cmd);
if !use_blake3 {
cmd.arg("-d:fasttreeNoBlake3");
}
if profile == "release" {
cmd.arg("-d:release");
}
cmd.arg(&capi_entry);
eprintln!("[fasttree-sys/build.rs] uruchamiam: {:?}", cmd);
let status = cmd
.status()
.unwrap_or_else(|e| panic!(
"nie udało się uruchomić '{}': {e}. Zainstaluj kompilator Nim (https://nim-lang.org) \
lub ustaw FASTTREE_NIM_BIN na jego ścieżkę.", nim_bin));
if !status.success() {
panic!("kompilacja Nim -> libfasttree.a nie powiodła się (kod {status})");
}
assert!(lib_path.exists(), "nim zwrócił sukces, ale {} nie istnieje", lib_path.display());
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=fasttree");
println!("cargo:rustc-link-lib=m");
if use_blake3 {
println!("cargo:rustc-link-search=native=/usr/local/lib");
println!("cargo:rustc-link-lib=static=blake3");
}
generate_bindings_if_requested(&include_dir, &out_dir);
}
#[cfg(not(feature = "bindgen"))]
fn generate_bindings_if_requested(_include_dir: &Path, _out_dir: &Path) {
}
#[cfg(feature = "bindgen")]
fn generate_bindings_if_requested(include_dir: &Path, out_dir: &Path) {
let header = include_dir.join("fasttree.h");
println!("cargo:rerun-if-changed={}", header.display());
let bindings = bindgen::Builder::default()
.header(header.to_string_lossy())
.allowlist_function("ft_.*")
.allowlist_type("Ft.*")
.generate()
.expect("bindgen: generowanie bindingów z fasttree.h nie powiodło się");
bindings
.write_to_file(out_dir.join("bindgen_bindings.rs"))
.expect("bindgen: zapis bindings.rs nie powiódł się");
println!(
"cargo:warning=wygenerowano bindgen_bindings.rs w {} — żeby ich użyć zamiast \
ręcznych bindingów, podmień `mod raw` w src/lib.rs na include!()",
out_dir.display()
);
}
fn apply_cross_compilation_flags(cmd: &mut Command) {
let host = env::var("HOST").unwrap_or_default();
let target = env::var("TARGET").unwrap_or_default();
if host == target || target.is_empty() {
return; }
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let nim_cpu = match arch.as_str() {
"x86_64" => "amd64",
"aarch64" => "arm64",
"arm" => "arm",
"x86" => "i386",
"riscv64" => "riscv64",
"powerpc64" => "powerpc64",
other => {
println!("cargo:warning=fasttree-sys: nieznana architektura '{other}' dla cross-compilation, \
pomijam mapowanie --cpu (może się nie udać)");
""
}
};
let nim_os = match os.as_str() {
"linux" => "linux",
"macos" => "macosx",
"windows" => "windows",
"android" => "android",
"ios" => "ios",
other => {
println!("cargo:warning=fasttree-sys: nieznany OS '{other}' dla cross-compilation, \
pomijam mapowanie --os (może się nie udać)");
""
}
};
if !nim_cpu.is_empty() {
cmd.arg(format!("--cpu:{nim_cpu}"));
}
if !nim_os.is_empty() {
cmd.arg(format!("--os:{nim_os}"));
}
let cross_cc = env::var("FASTTREE_NIM_CROSS_CC").unwrap_or_else(|_| format!("{target}-gcc"));
println!("cargo:rerun-if-env-changed=FASTTREE_NIM_CROSS_CC");
if which(&cross_cc) {
cmd.arg(format!("--gcc.exe:{cross_cc}"));
cmd.arg(format!("--gcc.linkerexe:{cross_cc}"));
} else {
println!("cargo:warning=fasttree-sys: cross-kompilator '{cross_cc}' nie znaleziony w PATH — \
zainstaluj go (np. `apt install gcc-aarch64-linux-gnu` dla Debiana/Ubuntu, \
dostosuj nazwę pakietu do architektury docelowej) albo ustaw \
FASTTREE_NIM_CROSS_CC na właściwą ścieżkę.");
}
}
fn which(bin: &str) -> bool {
if let Ok(path) = env::var("PATH") {
for dir in env::split_paths(&path) {
if dir.join(bin).is_file() {
return true;
}
}
}
false
}