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").unwrap();
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() {
let link_kind = if feature_enabled("static") {
"static"
} else {
"dylib"
};
if feature_enabled("system") {
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);
}
#[cfg(target_os = "linux")]
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"),
)
}
let output = if feature_enabled("cache") {
use std::{collections::hash_map::DefaultHasher, hash::*};
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())
};
if !feature_enabled("static") {
println!(
"cargo:warning=OpenBLAS is built as a shared library. You need to set LD_LIBRARY_PATH={}",
output.display()
);
}
let source = openblas_build::download(&output).unwrap();
let deliv = cfg.build(source, &output).unwrap();
println!("cargo:rustc-link-search={}", output.display());
for search_path in &deliv.make_conf.c_extra_libs.search_paths {
println!("cargo:rustc-link-search={}", search_path.display());
}
for lib in &deliv.make_conf.c_extra_libs.libs {
println!("cargo:rustc-link-lib={}", lib);
}
for search_path in &deliv.make_conf.f_extra_libs.search_paths {
println!("cargo:rustc-link-search={}", search_path.display());
}
for lib in &deliv.make_conf.f_extra_libs.libs {
println!("cargo:rustc-link-lib={}", lib);
}
}
#[cfg(not(target_os = "linux"))]
fn build() {
use std::fs;
let output = PathBuf::from(env::var("OUT_DIR").unwrap().replace(r"\", "/"));
let mut make = Command::new("make");
make.args(&["all"])
.arg(format!("BINARY={}", binary()))
.arg(format!(
"{}_CBLAS=1",
if feature_enabled("cblas") {
"YES"
} else {
"NO"
}
))
.arg(format!(
"{}_LAPACKE=1",
if feature_enabled("lapacke") {
"YES"
} else {
"NO"
}
));
match env::var("OPENBLAS_ARGS") {
Ok(args) => {
make.args(args.split_whitespace());
}
_ => (),
};
if let Ok(num_jobs) = env::var("NUM_JOBS") {
make.arg(format!("-j{}", num_jobs));
}
let target = match env::var("OPENBLAS_TARGET") {
Ok(target) => {
make.arg(format!("TARGET={}", target));
target
}
_ => env::var("TARGET").unwrap(),
};
env::remove_var("TARGET");
let source = if feature_enabled("cache") {
PathBuf::from(format!("source_{}", target.to_lowercase()))
} else {
output.join(format!("source_{}", target.to_lowercase()))
};
if !source.exists() {
let source_tmp = openblas_build::download(&output).unwrap();
fs::rename(&source_tmp, &source).unwrap();
}
for name in &vec!["CC", "FC", "HOSTCC"] {
if let Ok(value) = env::var(format!("OPENBLAS_{}", name)) {
make.arg(format!("{}={}", name, value));
}
}
run(&mut make.current_dir(&source));
run(Command::new("make")
.arg("install")
.arg(format!("DESTDIR={}", output.display()))
.current_dir(&source));
println!(
"cargo:rustc-link-search={}",
output.join("opt/OpenBLAS/lib").display(),
);
fn binary() -> &'static str {
if cfg!(target_pointer_width = "32") {
"32"
} else {
"64"
}
}
}