use std::process::Command;
fn main() {
if let Ok(output) = Command::new("git")
.args(["rev-parse", "--short=7", "HEAD"])
.output()
&& output.status.success()
{
let hash = String::from_utf8_lossy(&output.stdout).trim().to_string();
println!("cargo:rustc-env=GIT_HASH={}", hash);
}
println!("cargo:rerun-if-changed=.git/HEAD");
println!("cargo:rerun-if-changed=.git/refs/heads/");
if cfg!(feature = "cuda") {
check_cuda();
}
if cfg!(feature = "vulkan") {
check_vulkan();
}
if cfg!(feature = "hipblas") {
check_rocm();
}
if cfg!(feature = "openblas") {
check_openblas();
}
}
fn check_cuda() {
let output = Command::new("nvcc").arg("--version").output();
match output {
Ok(out) if out.status.success() => {
let text = String::from_utf8_lossy(&out.stdout);
let version = parse_cuda_version(&text);
println!("cargo::warning=");
println!(
"cargo::warning=╔═══════════════════════════════════════════════════════════════╗"
);
println!(
"cargo::warning=║ CUDA BUILD — SCROLL UP HERE IF BUILD FAILS ║"
);
println!(
"cargo::warning=╠═══════════════════════════════════════════════════════════════╣"
);
if let Some((major, minor)) = version {
println!(
"cargo::warning=║ Toolkit: CUDA {}.{} ║",
major, minor
);
} else {
println!(
"cargo::warning=║ Toolkit: CUDA (version unknown) ║"
);
}
if let Some(driver_cuda) = get_driver_cuda_version() {
println!(
"cargo::warning=║ Driver: supports up to CUDA {} ║",
driver_cuda
);
}
println!(
"cargo::warning=╠═══════════════════════════════════════════════════════════════╣"
);
println!(
"cargo::warning=║ If you see 'Unsupported gpu architecture': ║"
);
println!(
"cargo::warning=║ → Your GPU needs a newer CUDA toolkit ║"
);
println!(
"cargo::warning=║ → Update: https://developer.nvidia.com/cuda-downloads ║"
);
println!(
"cargo::warning=╚═══════════════════════════════════════════════════════════════╝"
);
println!("cargo::warning=");
}
_ => {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ `nvcc` not found — CUDA toolkit is not installed. ║\n\
║ ║\n\
║ Install: https://developer.nvidia.com/cuda-downloads ║\n\
║ Or build without CUDA: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
}
}
fn parse_cuda_version(text: &str) -> Option<(u32, u32)> {
let release_pos = text.find("release ")?;
let after = &text[release_pos + 8..];
let comma = after.find(',')?;
let version_str = &after[..comma];
let mut parts = version_str.split('.');
let major = parts.next()?.parse().ok()?;
let minor = parts.next()?.parse().ok()?;
Some((major, minor))
}
fn get_driver_cuda_version() -> Option<String> {
let output = Command::new("nvidia-smi")
.arg("--query-gpu=driver_version")
.arg("--format=csv,noheader")
.output()
.ok()?;
if !output.status.success() {
return None;
}
let full_output = Command::new("nvidia-smi").output().ok()?;
let text = String::from_utf8_lossy(&full_output.stdout);
let cuda_pos = text.find("CUDA Version:")?;
let after = &text[cuda_pos + 14..];
let end = after.find(|c: char| !c.is_ascii_digit() && c != '.')?;
Some(after[..end].trim().to_string())
}
fn check_vulkan() {
if Command::new("vulkaninfo")
.arg("--summary")
.output()
.is_err()
{
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ `vulkaninfo` not found — Vulkan SDK is not installed. ║\n\
║ ║\n\
║ Install: sudo apt install libvulkan-dev ║\n\
║ mesa-vulkan-drivers vulkan-tools glslc ║\n\
║ Or build without Vulkan: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
if Command::new("glslc").arg("--version").output().is_err() {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ `glslc` not found — SPIR-V shader compiler missing. ║\n\
║ ║\n\
║ Install: sudo apt install glslc ║\n\
║ Or build without Vulkan: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
check_libclang_headers();
println!("cargo::warning=Vulkan SDK detected (vulkaninfo + glslc)");
}
fn check_libclang_headers() {
if let Ok(args) = std::env::var("BINDGEN_EXTRA_CLANG_ARGS")
&& args.contains("-I")
{
return;
}
if std::env::var("CLANG_PATH").is_ok() {
return;
}
let resource_dir = find_clang_resource_dir();
match resource_dir {
Some(dir) => {
let stdbool = std::path::PathBuf::from(&dir).join("include/stdbool.h");
if !stdbool.exists() {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ clang resource dir found but stdbool.h is missing. ║\n\
║ Resource dir: {dir:<43} ║\n\
║ ║\n\
║ Install: sudo apt install libclang-dev ║\n\
║ Or build without GPU: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
}
None => {
if has_versioned_clang() {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ `clang` not in PATH (but a versioned binary exists). ║\n\
║ ║\n\
║ bindgen needs the unversioned `clang` to locate its ║\n\
║ built-in headers (stdbool.h). Without it, GPU bindings ║\n\
║ will be incomplete and compilation will fail. ║\n\
║ ║\n\
║ Fix (pick one): ║\n\
║ sudo apt install clang ║\n\
║ export CLANG_PATH=$(which clang-20) ║\n\
║ Or build without GPU: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
let headers_on_disk = std::path::Path::new("/usr/lib/clang")
.read_dir()
.ok()
.and_then(|mut entries| {
entries
.any(|e| {
e.ok()
.is_some_and(|e| e.path().join("include/stdbool.h").exists())
})
.then_some(())
})
.is_some();
if headers_on_disk {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ `clang` not found in PATH. ║\n\
║ ║\n\
║ libclang-dev headers exist on disk, but bindgen needs ║\n\
║ the `clang` binary to locate them at runtime. ║\n\
║ ║\n\
║ Fix (pick one): ║\n\
║ sudo apt install clang ║\n\
║ export CLANG_PATH=/usr/bin/clang-XX ║\n\
║ Or build without GPU: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
} else {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ libclang-dev not found. ║\n\
║ ║\n\
║ bindgen needs clang and its built-in headers to generate ║\n\
║ GPU FFI bindings. Without them, compilation will fail. ║\n\
║ ║\n\
║ Install: sudo apt install clang libclang-dev ║\n\
║ Or build without GPU: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
}
}
}
fn find_clang_resource_dir() -> Option<String> {
clang_resource_dir("clang")
}
fn has_versioned_clang() -> bool {
(10..=30)
.rev()
.any(|v| clang_resource_dir(&format!("clang-{v}")).is_some())
}
fn clang_resource_dir(name: &str) -> Option<String> {
let output = Command::new(name)
.arg("-print-resource-dir")
.output()
.ok()?;
if !output.status.success() {
return None;
}
let dir = String::from_utf8_lossy(&output.stdout).trim().to_string();
(!dir.is_empty()).then_some(dir)
}
fn check_rocm() {
if Command::new("rocminfo").output().is_err() {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ `rocminfo` not found — ROCm is not installed. ║\n\
║ ║\n\
║ Install: https://rocm.docs.amd.com/ ║\n\
║ Or build without HipBLAS: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
println!("cargo::warning=ROCm detected");
}
fn check_openblas() {
let pkg_config_ok = Command::new("pkg-config")
.args(["--exists", "openblas"])
.status()
.is_ok_and(|s| s.success());
if !pkg_config_ok {
let lib_exists = std::path::Path::new("/usr/lib/x86_64-linux-gnu/libopenblas.so").exists()
|| std::path::Path::new("/usr/lib/libopenblas.so").exists()
|| std::path::Path::new("/usr/lib64/libopenblas.so").exists();
if !lib_exists {
panic!(
"\n\n\
╔══════════════════════════════════════════════════════════╗\n\
║ OpenBLAS not found. ║\n\
║ ║\n\
║ Install: sudo apt install libopenblas-dev ║\n\
║ Or build without OpenBLAS: cargo build --release ║\n\
╚══════════════════════════════════════════════════════════╝\n",
);
}
}
println!("cargo::warning=OpenBLAS detected");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_cuda_version_standard() {
let text = "nvcc: NVIDIA (R) Cuda compiler driver\n\
Copyright (c) 2005-2024 NVIDIA Corporation\n\
Built on Thu_Mar_28_02:18:24_PDT_2024\n\
Cuda compilation tools, release 12.4, V12.4.131\n\
Build cuda_12.4.r12.4/compiler.34097967_0";
assert_eq!(parse_cuda_version(text), Some((12, 4)));
}
#[test]
fn parse_cuda_version_13() {
let text = "Cuda compilation tools, release 13.0, V13.0.76";
assert_eq!(parse_cuda_version(text), Some((13, 0)));
}
#[test]
fn parse_cuda_version_no_match() {
assert_eq!(parse_cuda_version("no version here"), None);
}
#[test]
fn parse_cuda_version_partial() {
assert_eq!(parse_cuda_version("release abc, V1"), None);
}
}