fn main() {
println!("cargo:rustc-check-cfg=cfg(rlx_cpu_blas)");
println!("cargo:rustc-check-cfg=cfg(rlx_cpu_blas_accelerate)");
println!("cargo:rustc-check-cfg=cfg(rlx_cpu_blas_openblas)");
println!("cargo:rustc-check-cfg=cfg(rlx_cpu_blas_mkl)");
println!("cargo:rustc-check-cfg=cfg(rlx_cpu_amx_bnns)");
println!("cargo:rustc-check-cfg=cfg(rlx_cpu_amx_dense)");
println!("cargo:rustc-check-cfg=cfg(rlx_cpu_amx_sme)");
{
let is_apple = std::env::var("CARGO_CFG_TARGET_VENDOR").unwrap_or_default() == "apple";
for (feat, cfg) in [
("CARGO_FEATURE_AMX_BNNS", "rlx_cpu_amx_bnns"),
("CARGO_FEATURE_AMX_DENSE", "rlx_cpu_amx_dense"),
("CARGO_FEATURE_AMX_SME", "rlx_cpu_amx_sme"),
] {
println!("cargo:rerun-if-env-changed={feat}");
if is_apple && std::env::var_os(feat).is_some() {
println!("cargo:rustc-cfg={cfg}");
}
}
}
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_BLAS");
if std::env::var_os("CARGO_FEATURE_BLAS").is_none() {
return;
}
let target_vendor = std::env::var("CARGO_CFG_TARGET_VENDOR").unwrap_or_default();
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if target_arch == "wasm32" {
return;
}
if target_vendor == "apple" {
println!("cargo:rustc-link-lib=framework=Accelerate");
println!("cargo:rustc-cfg=rlx_cpu_blas");
println!("cargo:rustc-cfg=rlx_cpu_blas_accelerate");
return;
}
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_BLAS_MKL");
if std::env::var_os("CARGO_FEATURE_BLAS_MKL").is_some() {
println!("cargo:rerun-if-env-changed=MKL_ROOT");
if let Ok(root) = std::env::var("MKL_ROOT") {
println!("cargo:rustc-link-search=native={root}/lib/intel64");
println!("cargo:rustc-link-search=native={root}/lib");
println!("cargo:rustc-link-search=native={root}/redist/intel64");
}
println!("cargo:rustc-link-lib=mkl_rt");
println!("cargo:rustc-cfg=rlx_cpu_blas");
println!("cargo:rustc-cfg=rlx_cpu_blas_mkl");
return;
}
println!("cargo:rerun-if-env-changed=OPENBLAS_DIR");
println!("cargo:rerun-if-env-changed=OPENBLAS_LIB_DIR");
println!("cargo:rerun-if-env-changed=RLX_BLAS_LINK");
println!("cargo:rerun-if-env-changed=RLX_BLAS_SEARCH");
if let Some(spec) = std::env::var("RLX_BLAS_LINK")
.ok()
.filter(|s| !s.trim().is_empty())
{
for dir in [
std::env::var("RLX_BLAS_SEARCH").ok(),
std::env::var("OPENBLAS_LIB_DIR").ok(),
]
.into_iter()
.flatten()
{
println!("cargo:rustc-link-search=native={dir}");
}
for name in spec.split_whitespace() {
println!("cargo:rustc-link-lib={name}");
}
println!("cargo:rustc-cfg=rlx_cpu_blas"); return;
}
let openblas_lib_dir = std::env::var("OPENBLAS_LIB_DIR").ok();
let openblas_dir = std::env::var("OPENBLAS_DIR").ok();
let pinned = openblas_lib_dir.is_some() || openblas_dir.is_some();
let host = std::env::var("HOST").unwrap_or_default();
let target = std::env::var("TARGET").unwrap_or_default();
let native = !host.is_empty() && host == target;
let probed_lib_dir = if !pinned && target_os == "linux" && native {
openblas_search_dirs()
.into_iter()
.find(|d| std::path::Path::new(&format!("{d}/libopenblas.so")).exists())
} else {
None
};
if let Some(dir) = &probed_lib_dir {
println!("cargo:rustc-link-search=native={dir}");
} else if target_os != "windows" && !pinned && !openblas_links_by_default(native) {
println!(
"cargo:warning=rlx-cpu: no OpenBLAS for {target_arch}-{target_os}; using the \
pure-Rust fallback (correct but slower — install libopenblas-dev, or set \
RLX_BLAS_LINK=\"<libs>\" / OPENBLAS_LIB_DIR to link a tuned BLAS)"
);
return; }
let win_lib = if target_os == "windows" {
match find_windows_openblas_lib(openblas_lib_dir.as_deref(), openblas_dir.as_deref()) {
Some(hit) => Some(hit),
None if pinned => None, None => {
println!(
"cargo:warning=rlx-cpu: openblas.lib/libopenblas.lib not found; using the \
portable SIMD gemm (set OPENBLAS_LIB_DIR or install OpenBLAS under \
C:\\OpenBLAS)"
);
return;
}
}
} else {
None
};
if let Some((lib_dir, link_name)) = win_lib {
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:rustc-link-lib={link_name}");
} else if let Some(dir) = openblas_lib_dir {
println!("cargo:rustc-link-search=native={dir}");
println!("cargo:rustc-link-lib=openblas");
} else if let Some(root) = openblas_dir {
println!("cargo:rustc-link-search=native={root}/lib");
println!("cargo:rustc-link-lib=openblas");
} else {
println!("cargo:rustc-link-lib=openblas");
}
println!("cargo:rustc-cfg=rlx_cpu_blas");
println!("cargo:rustc-cfg=rlx_cpu_blas_openblas");
}
fn openblas_links_by_default(native: bool) -> bool {
if !native {
return false;
}
let Ok(out_dir) = std::env::var("OUT_DIR") else {
return false;
};
let src = std::path::Path::new(&out_dir).join("openblas_probe.c");
if std::fs::write(&src, "int main(void){return 0;}").is_err() {
return false;
}
let cc = std::env::var("CC").unwrap_or_else(|_| "cc".to_string());
println!("cargo:rerun-if-env-changed=CC");
let probe = std::process::Command::new(cc)
.arg(&src)
.arg("-lopenblas")
.arg("-o")
.arg(std::path::Path::new(&out_dir).join("openblas_probe"))
.output();
matches!(probe, Ok(o) if o.status.success())
}
fn openblas_search_dirs() -> Vec<String> {
let mut base = vec![
"/usr/lib".to_string(),
"/usr/local/lib".to_string(),
"/usr/lib64".to_string(),
"/usr/local/lib64".to_string(),
];
if let Ok(rd) = std::fs::read_dir("/usr/lib") {
for e in rd.flatten() {
let p = e.path();
if p.is_dir()
&& p.file_name()
.and_then(|s| s.to_str())
.is_some_and(|n| n.contains("-linux-"))
{
base.push(p.to_string_lossy().into_owned());
}
}
}
let mut out = Vec::with_capacity(base.len() * 4);
for d in base {
for variant in [
"",
"/openblas-pthread",
"/openblas-openmp",
"/openblas-serial",
] {
out.push(format!("{d}{variant}"));
}
}
out
}
fn find_windows_openblas_lib(
openblas_lib_dir: Option<&str>,
openblas_dir: Option<&str>,
) -> Option<(String, &'static str)> {
use std::path::PathBuf;
let mut dirs: Vec<PathBuf> = Vec::new();
if let Some(dir) = openblas_lib_dir {
dirs.push(PathBuf::from(dir));
}
if let Some(root) = openblas_dir {
dirs.push(PathBuf::from(root).join("lib"));
}
if let Ok(lib) = std::env::var("LIB") {
dirs.extend(lib.split(';').filter(|s| !s.is_empty()).map(PathBuf::from));
}
for cand in [
r"C:\OpenBLAS\lib",
r"C:\openblas\lib",
r"C:\vcpkg\installed\x64-windows\lib",
r"C:\Program Files\OpenBLAS\lib",
] {
dirs.push(PathBuf::from(cand));
}
for d in dirs {
if d.join("libopenblas.lib").is_file() {
return Some((d.to_string_lossy().into_owned(), "libopenblas"));
}
if d.join("openblas.lib").is_file() {
return Some((d.to_string_lossy().into_owned(), "openblas"));
}
}
None
}