Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to openblas-build on Linux #52

Merged
merged 9 commits into from
Jan 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/openblas-src.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ jobs:
- uses: actions/checkout@v1
with:
submodules: 'recursive'
- name: Install GCC-Fortran by apt
run: |
apt update
apt install -y gfortran
- name: Install OpenBLAS by apt
run: |
apt update
Expand Down
12 changes: 12 additions & 0 deletions openblas-build/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,15 @@ impl Configure {
// - This will automatically run in parallel without `-j` flag
// - The `make` of OpenBLAS outputs 30k lines,
// which will be redirected into `out.log` and `err.log`.
// - cargo sets `TARGET` environment variable as target triple (e.g. x86_64-unknown-linux-gnu)
// while binding build.rs, but `make` read it as CPU target specification.
//
match Command::new("make")
.current_dir(out_dir)
.stdout(unsafe { Stdio::from_raw_fd(out.into_raw_fd()) }) // this works only for unix
.stderr(unsafe { Stdio::from_raw_fd(err.into_raw_fd()) })
.args(&self.make_args())
.env_remove("TARGET")
.check_call()
{
Ok(_) => {}
Expand Down Expand Up @@ -291,6 +295,14 @@ impl Configure {
mod tests {
use super::*;

#[ignore]
#[test]
fn build_default() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_build/build_default");
let opt = Configure::default();
let _detail = opt.build(path).unwrap();
}

#[ignore]
#[test]
fn build_no_shared() {
Expand Down
3 changes: 3 additions & 0 deletions openblas-src/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ libc = "0.2"

[target.'cfg(target_os="windows")'.build-dependencies]
vcpkg = "0.2"

[target.'cfg(target_os="linux")'.build-dependencies.openblas-build]
path = "../openblas-build"
189 changes: 110 additions & 79 deletions openblas-src/build.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
use std::{env, fs, path::*, process::Command};
use std::{env, path::*, process::Command};

fn feature_enabled(feature: &str) -> bool {
env::var(format!("CARGO_FEATURE_{}", feature.to_uppercase())).is_ok()
}

fn binary() -> &'static str {
if cfg!(target_pointer_width = "32") {
"32"
} else {
"64"
}
}

/// Add path where pacman (on msys2) install OpenBLAS
///
/// - `pacman -S mingw-w64-x86_64-openblas` will install
Expand Down Expand Up @@ -94,86 +86,125 @@ fn main() {
"Non-vcpkg builds are not supported on Windows. You must use the 'system' feature."
)
}
build();
}
println!("cargo:rustc-link-lib={}=openblas", link_kind);
}

let output = PathBuf::from(env::var("OUT_DIR").unwrap().replace(r"\", "/"));
let mut make = Command::new("make");
make.args(&["libs", "netlib", "shared"])
.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());
/// Build OpenBLAS using openblas-build crate
#[cfg(target_os = "linux")]
fn build() {
let output = PathBuf::from(env::var("OUT_DIR").unwrap());
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;
}
cfg.build(&output).unwrap();
println!("cargo:rustc-link-search={}", output.display());
}

/// openblas-src 0.9.0 compatible `make` runner
///
/// This cannot detect that OpenBLAS skips LAPACK build due to the absense of Fortran compiler.
/// openblas-build crate can detect it by sneaking OpenBLAS build system, but only works on Linux.
///
#[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(&["libs", "netlib", "shared"])
.arg(format!("BINARY={}", binary()))
.arg(format!(
"{}_CBLAS=1",
if feature_enabled("cblas") {
"YES"
} else {
"NO"
}
_ => (),
};
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
))
.arg(format!(
"{}_LAPACKE=1",
if feature_enabled("lapacke") {
"YES"
} else {
"NO"
}
_ => 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()))
};
));
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 = PathBuf::from(format!("{}_tmp", source.display()));
if source_tmp.exists() {
fs::remove_dir_all(&source_tmp).unwrap();
}
run(Command::new("cp").arg("-R").arg("source").arg(&source_tmp));
fs::rename(&source_tmp, &source).unwrap();
if !source.exists() {
let source_tmp = PathBuf::from(format!("{}_tmp", source.display()));
if source_tmp.exists() {
fs::remove_dir_all(&source_tmp).unwrap();
}
for name in &vec!["CC", "FC", "HOSTCC"] {
if let Ok(value) = env::var(format!("OPENBLAS_{}", name)) {
make.arg(format!("{}={}", name, value));
}
run(Command::new("cp").arg("-R").arg("source").arg(&source_tmp));
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(),
);
}
println!("cargo:rustc-link-lib={}=openblas", link_kind);
}
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 run(command: &mut Command) {
println!("Running: `{:?}`", command);
match command.status() {
Ok(status) => {
if !status.success() {
panic!("Failed: `{:?}` ({})", command, status);
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);
}
}
Err(error) => {
panic!("Failed: `{:?}` ({})", command, error);
}

fn binary() -> &'static str {
if cfg!(target_pointer_width = "32") {
"32"
} else {
"64"
}
}
}