use std::{env, path::*, process::Command};
#[allow(unused)]
fn run(command: &mut Command) {
println!("Running: `{:?}`", command);
match command.status() {
Ok(status) => {
if !status.success() {
panic!("Failed: `{:?}` ({})", command, status);
}
}
Err(error) => {
panic!("Failed: `{:?}` ({})", command, error);
}
}
}
fn feature_enabled(feature: &str) -> bool {
env::var(format!("CARGO_FEATURE_{}", feature.to_uppercase())).is_ok()
}
fn windows_gnu_system() {
let lib_path = String::from_utf8(
Command::new("cygpath")
.arg("-w")
.arg(if feature_enabled("static") {
"/mingw64/bin"
} else {
"/mingw64/lib"
})
.output()
.expect("Failed to exec cygpath")
.stdout,
)
.expect("cygpath output includes non UTF-8 string");
println!("cargo:rustc-link-search={}", lib_path);
}
fn windows_msvc_system() {
if !feature_enabled("static") {
env::set_var("VCPKGRS_DYNAMIC", "1");
}
#[cfg(target_env = "msvc")]
vcpkg::find_package("openblas").expect(
"vcpkg failed to find OpenBLAS package , Try to install it using `vcpkg install openblas:$(ARCH)-windows(-static)(-md)`"
);
if !cfg!(target_env = "msvc") {
unreachable!();
}
}
fn macos_system() {
fn brew_prefix(target: &str) -> PathBuf {
let out = Command::new("brew")
.arg("--prefix")
.arg(target)
.output()
.expect("brew not installed");
assert!(out.status.success(), "`brew --prefix` failed");
let path = String::from_utf8(out.stdout).expect("Non-UTF8 path by `brew --prefix`");
PathBuf::from(path.trim())
}
let openblas = brew_prefix("openblas");
let libomp = brew_prefix("libomp");
println!("cargo:rustc-link-search={}/lib", openblas.display());
println!("cargo:rustc-link-search={}/lib", libomp.display());
}
fn main() {
if env::var("DOCS_RS").is_ok() {
return;
}
let link_kind = if feature_enabled("static") {
"static"
} else {
"dylib"
};
if feature_enabled("system") {
if pkg_config::Config::new()
.statik(feature_enabled("static"))
.probe("openblas")
.is_ok()
{
return;
}
if cfg!(target_os = "windows") {
if cfg!(target_env = "gnu") {
windows_gnu_system();
} else if cfg!(target_env = "msvc") {
windows_msvc_system();
} else {
panic!(
"Unsupported ABI for Windows: {}",
env::var("CARGO_CFG_TARGET_ENV").unwrap()
);
}
}
if cfg!(target_os = "macos") {
macos_system();
}
println!("cargo:rustc-link-lib={}=openblas", link_kind);
} else {
if cfg!(target_env = "msvc") {
panic!(
"Non-vcpkg builds are not supported on Windows. You must use the 'system' feature."
)
}
build();
}
println!("cargo:rustc-link-lib={}=openblas", link_kind);
}
fn build() {
println!("cargo:rerun-if-env-changed=OPENBLAS_TARGET");
println!("cargo:rerun-if-env-changed=OPENBLAS_CC");
println!("cargo:rerun-if-env-changed=OPENBLAS_HOSTCC");
println!("cargo:rerun-if-env-changed=OPENBLAS_FC");
println!("cargo:rerun-if-env-changed=OPENBLAS_RANLIB");
let mut cfg = openblas_build::Configure::default();
if !feature_enabled("cblas") {
cfg.no_cblas = true;
}
if !feature_enabled("lapacke") {
cfg.no_lapacke = true;
}
if feature_enabled("static") {
cfg.no_shared = true;
} else {
cfg.no_static = true;
}
if let Ok(target) = env::var("OPENBLAS_TARGET") {
cfg.target = Some(
target
.parse()
.expect("Unsupported target is specified by $OPENBLAS_TARGET"),
)
}
cfg.compilers.cc = env::var("OPENBLAS_CC").ok();
cfg.compilers.hostcc = env::var("OPENBLAS_HOSTCC").ok();
cfg.compilers.fc = env::var("OPENBLAS_FC").ok();
cfg.compilers.ranlib = env::var("OPENBLAS_RANLIB").ok();
let output = if feature_enabled("cache") {
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
let mut hasher = DefaultHasher::new();
cfg.hash(&mut hasher);
dirs::data_dir()
.expect("Cannot get user's data directory")
.join("openblas_build")
.join(format!("{:x}", hasher.finish()))
} else {
PathBuf::from(env::var("OUT_DIR").unwrap())
};
let source = openblas_build::download(&output).unwrap();
if !feature_enabled("static") {
let ld_name = if cfg!(target_os = "macos") {
"DYLD_LIBRARY_PATH"
} else {
"LD_LIBRARY_PATH"
};
println!(
"cargo:warning=OpenBLAS is built as a shared library. You need to set {}={}",
ld_name,
source.display()
);
}
let build_result = cfg.build(&source);
let make_conf = match build_result {
Ok(c) => c,
Err(openblas_build::error::Error::MissingCrossCompileInfo { info }) => {
panic!(
"Cross compile information is missing and cannot be inferred: OPENBLAS_{}",
info
);
}
Err(e) => {
panic!("OpenBLAS build failed: {}", e);
}
};
println!("cargo:rustc-link-search={}", source.display());
for search_path in &make_conf.c_extra_libs.search_paths {
println!("cargo:rustc-link-search={}", search_path.display());
}
for lib in &make_conf.c_extra_libs.libs {
println!("cargo:rustc-link-lib={}", lib);
}
for search_path in &make_conf.f_extra_libs.search_paths {
println!("cargo:rustc-link-search={}", search_path.display());
}
for lib in &make_conf.f_extra_libs.libs {
println!("cargo:rustc-link-lib={}", lib);
}
}