use cp2k_build_utils as bu;
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed=CP2K_TARGET_ARCH");
println!("cargo:rerun-if-env-changed=CP2K_CUDA_ARCH");
println!("cargo:rerun-if-env-changed=CP2K_HIP_ARCH");
let arch = bu::target_arch();
let stable_dir = bu::stable_dir("libxc-v7.1");
let src_dir = stable_dir.join("src");
let build_dir = stable_dir.join("build");
let install_dir = stable_dir.join("install");
let ver = "7.1.2";
let sha = "3915fac94416e4c415534223ea492ad2663f928acf27e98662c861b094a6c306";
let cuda = cfg!(feature = "cuda");
let recipe_ok = (!cuda || install_dir.join(".dlink").exists())
&& !bu::dep_marker_stale(&stable_dir, ".version", ver);
if install_dir.join("lib").join("libxc.a").exists() && recipe_ok {
println!(
"cargo:warning=Libxc already installed at {}",
install_dir.display()
);
bu::emit_root(&install_dir);
return;
}
if install_dir.join("lib").join("libxc.a").exists() {
println!(
"cargo:warning=Cached Libxc is stale (version bump or device-link recipe changed) – rebuilding..."
);
let _ = std::fs::remove_dir_all(&install_dir);
let _ = std::fs::remove_dir_all(&build_dir);
let _ = std::fs::remove_dir_all(&src_dir);
}
if !src_dir.exists() {
println!("cargo:warning=Downloading Libxc {ver}...");
let tarball = stable_dir.join(format!("libxc-{ver}.tar.bz2"));
bu::download(
&format!("https://gitlab.com/libxc/libxc/-/archive/{ver}/libxc-{ver}.tar.bz2"),
&tarball,
);
bu::verify_sha256(&tarball, sha);
std::fs::create_dir_all(&src_dir).expect("Failed to create libxc src dir");
bu::run(
Command::new("tar").args([
"-xjf",
tarball.to_str().unwrap(),
"--strip-components=1",
"-C",
src_dir.to_str().unwrap(),
]),
"Libxc extract",
);
}
std::fs::create_dir_all(&build_dir).expect("Failed to create libxc build dir");
let num_jobs = bu::num_jobs();
println!("cargo:warning=Configuring Libxc with CMake...");
let mut args: Vec<String> = vec![
"-S".into(),
src_dir.to_string_lossy().into_owned(),
"-B".into(),
build_dir.to_string_lossy().into_owned(),
"-DCMAKE_BUILD_TYPE=Release".into(),
"-DBUILD_SHARED_LIBS=OFF".into(),
"-DCMAKE_POSITION_INDEPENDENT_CODE=ON".into(),
"-DBUILD_FPIC=ON".into(),
"-DCMAKE_INSTALL_LIBDIR=lib".into(),
"-DENABLE_FORTRAN=ON".into(),
"-DENABLE_PYTHON=OFF".into(),
"-DBUILD_TESTING=OFF".into(),
"-DDISABLE_KXC=OFF".into(),
format!("-DCMAKE_INSTALL_PREFIX={}", install_dir.display()),
format!("-DCMAKE_C_FLAGS={}", bu::march_flag(&arch)),
format!("-DCMAKE_Fortran_FLAGS={}", bu::march_flag(&arch)),
format!("-DCMAKE_BUILD_PARALLEL_LEVEL={num_jobs}"),
];
let cuda_arch = std::env::var("CP2K_CUDA_ARCH").unwrap_or_else(|_| "80".into());
if cuda {
args.push("-DENABLE_CUDA=ON".into());
args.push(format!("-DCMAKE_CUDA_ARCHITECTURES={cuda_arch}"));
}
bu::run(Command::new("cmake").args(&args), "Libxc configure");
println!("cargo:warning=Building Libxc...");
bu::run(
Command::new("cmake").args(["--build", build_dir.to_str().unwrap(), "-j", &num_jobs]),
"Libxc build",
);
bu::run(
Command::new("cmake").args(["--install", build_dir.to_str().unwrap()]),
"Libxc install",
);
if cuda {
let libxc_a = install_dir.join("lib").join("libxc.a");
let dlink_o = install_dir.join("lib").join("libxc_dlink.o");
let nvcc = std::env::var("CUDA_PATH")
.map(|p| format!("{p}/bin/nvcc"))
.unwrap_or_else(|_| "nvcc".into());
let mut dlink = Command::new(&nvcc);
dlink.arg("--device-link");
for a in cuda_arch.split(';').filter(|s| !s.is_empty()) {
dlink.arg(format!("-gencode=arch=compute_{a},code=sm_{a}"));
}
dlink
.arg(&libxc_a)
.arg("-o")
.arg(&dlink_o)
.arg("-Xcompiler")
.arg("-fPIC");
println!("cargo:warning=Device-linking libxc CUDA objects (nvcc --device-link)...");
bu::run(&mut dlink, "Libxc CUDA device-link");
std::fs::write(install_dir.join(".dlink"), "1\n").ok();
}
println!("cargo:warning=Libxc installed to {}", install_dir.display());
bu::write_dep_marker(&stable_dir, ".version", ver);
bu::emit_root(&install_dir);
}