diff --git a/.github/workflows/intel-mkl-sys.yml b/.github/workflows/intel-mkl-sys.yml new file mode 100644 index 00000000..44479be8 --- /dev/null +++ b/.github/workflows/intel-mkl-sys.yml @@ -0,0 +1,70 @@ +name: intel-mkl-sys + +on: + push: + branches: + - master + pull_request: {} + +jobs: + windows: + strategy: + fail-fast: false + matrix: + feature: + - mkl-dynamic-lp64-seq + - mkl-dynamic-ilp64-seq + runs-on: windows-2019 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/cargo@v1 + with: + command: test + args: > + --manifest-path=intel-mkl-sys/Cargo.toml + --no-default-features + --features=${{ matrix.feature }},download + + macos: + strategy: + fail-fast: false + matrix: + feature: + - mkl-dynamic-lp64-iomp + - mkl-dynamic-lp64-seq + - mkl-dynamic-ilp64-iomp + - mkl-dynamic-ilp64-seq + runs-on: macos-10.15 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/cargo@v1 + with: + command: test + args: > + --manifest-path=intel-mkl-sys/Cargo.toml + --no-default-features + --features=${{ matrix.feature }},download + + linux: + strategy: + fail-fast: false + matrix: + feature: + - mkl-static-lp64-iomp + - mkl-static-lp64-seq + - mkl-static-ilp64-iomp + - mkl-static-ilp64-seq + - mkl-dynamic-lp64-iomp + - mkl-dynamic-lp64-seq + - mkl-dynamic-ilp64-iomp + - mkl-dynamic-ilp64-seq + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/cargo@v1 + with: + command: test + args: > + --manifest-path=intel-mkl-sys/Cargo.toml + --no-default-features + --features=${{ matrix.feature }},download diff --git a/.github/workflows/intel-mkl-tool.yml b/.github/workflows/intel-mkl-tool.yml new file mode 100644 index 00000000..f98d8380 --- /dev/null +++ b/.github/workflows/intel-mkl-tool.yml @@ -0,0 +1,34 @@ +name: intel-mkl-tool + +on: + push: + branches: + - master + pull_request: {} + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: + - windows-2019 + - macos-10.15 + - ubuntu-18.04 + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path=intel-mkl-tool/Cargo.toml + + docker: + runs-on: ubuntu-18.04 + strategy: + matrix: + target: ["test", "seek", "package", "download"] + steps: + - uses: actions/checkout@v1 + - name: Test with mkl-rust container + run: make -C intel-mkl-tool ${{ matrix.target }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0163f98c..eb1f7158 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -7,52 +7,6 @@ on: pull_request: {} jobs: - windows: - runs-on: windows-2019 - strategy: - matrix: - target: ["intel-mkl-src", "intel-mkl-sys", "intel-mkl-tool"] - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path=${{ matrix.target }}/Cargo.toml - - macos: - runs-on: macos-10.15 - strategy: - matrix: - target: ["intel-mkl-src", "intel-mkl-sys", "intel-mkl-tool"] - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path=${{ matrix.target }}/Cargo.toml - - linux: - runs-on: ubuntu-18.04 - strategy: - matrix: - target: ["intel-mkl-src", "intel-mkl-sys", "intel-mkl-tool"] - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path=${{ matrix.target }}/Cargo.toml - - docker: - runs-on: ubuntu-18.04 - strategy: - matrix: - target: ["test", "seek", "package", "download"] - steps: - - uses: actions/checkout@v1 - - name: Test with mkl-rust container - run: make -C intel-mkl-tool ${{ matrix.target }} - check-format: runs-on: ubuntu-18.04 steps: @@ -61,4 +15,3 @@ jobs: with: command: fmt args: -- --check - diff --git a/README.md b/README.md index 2b444c89..4c0b9619 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,19 @@ Redistribution of Intel MKL as a crate. Tested on Linux, macOS, and Windows (sin [VM]: https://software.intel.com/en-us/mkl-developer-reference-c-vector-mathematical-functions [VSL]: https://software.intel.com/en-us/mkl-developer-reference-c-statistical-functions +## Supported features + +| feature name | Linux | macOS | Windows | +|:-----------------------|:------------------:|:------------------:|:------------------:| +| mkl-static-lp64-iomp | :heavy_check_mark: | - | - | +| mkl-static-lp64-seq | :heavy_check_mark: | - | - | +| mkl-static-ilp64-iomp | :heavy_check_mark: | - | - | +| mkl-static-ilp64-seq | :heavy_check_mark: | - | - | +| mkl-dynamic-lp64-iomp | :heavy_check_mark: | :heavy_check_mark: | - | +| mkl-dynamic-lp64-seq | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| mkl-dynamic-ilp64-iomp | :heavy_check_mark: | :heavy_check_mark: | - | +| mkl-dynamic-ilp64-seq | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | + ## Usage This crate is a `*-src` crate. This downloads and link Intel MKL, but does not introduce any symbols. diff --git a/intel-mkl-src/Cargo.toml b/intel-mkl-src/Cargo.toml index 8e555055..2a6db1d2 100644 --- a/intel-mkl-src/Cargo.toml +++ b/intel-mkl-src/Cargo.toml @@ -14,13 +14,24 @@ build = "build.rs" links = "mkl_core" [features] -default = [] -use-shared = [] +default = ["download", "mkl-dynamic-lp64-iomp"] -[build-dependencies.intel-mkl-tool] -version = "0.1.0" -path = "../intel-mkl-tool" -default-features = false +# MKL config +# https://software.intel.com/content/www/us/en/develop/articles/intel-math-kernel-library-intel-mkl-and-pkg-config-tool.html +mkl-static-lp64-iomp = [] +mkl-static-lp64-seq = [] +mkl-static-ilp64-iomp = [] +mkl-static-ilp64-seq = [] +mkl-dynamic-lp64-iomp = [] +mkl-dynamic-lp64-seq = [] +mkl-dynamic-ilp64-iomp = [] +mkl-dynamic-ilp64-seq = [] -[dev-dependencies] -libc = "0.2.65" +# Enable downloading from AWS S3 when not found +download = [] +# (Experimental) Cache download archive ad $XDG_DATA_HOME/intel-mkl-tool/ +xdg-data-home = [] + +[build-dependencies] +anyhow = "1" +intel-mkl-tool = { version = "0.1.0", path = "../intel-mkl-tool", default-features = false } diff --git a/intel-mkl-src/build.rs b/intel-mkl-src/build.rs index 842cd021..dad6aa97 100644 --- a/intel-mkl-src/build.rs +++ b/intel-mkl-src/build.rs @@ -20,23 +20,50 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use anyhow::*; +use intel_mkl_tool::*; use std::{env, path::*}; -fn main() { - let out_dir = if let Some(path) = intel_mkl_tool::seek_pkg_config() { - path - } else { - let out_dir = if cfg!(feature = "use-shared") { - intel_mkl_tool::xdg_home_path() +#[cfg(feature = "mkl-static-lp64-iomp")] +const MKL_CONFIG: &str = "mkl-static-lp64-iomp"; +#[cfg(feature = "mkl-static-lp64-seq")] +const MKL_CONFIG: &str = "mkl-static-lp64-seq"; +#[cfg(feature = "mkl-static-ilp64-iomp")] +const MKL_CONFIG: &str = "mkl-static-ilp64-iomp"; +#[cfg(feature = "mkl-static-ilp64-seq")] +const MKL_CONFIG: &str = "mkl-static-ilp64-seq"; +#[cfg(feature = "mkl-dynamic-lp64-iomp")] +const MKL_CONFIG: &str = "mkl-dynamic-lp64-iomp"; +#[cfg(feature = "mkl-dynamic-lp64-seq")] +const MKL_CONFIG: &str = "mkl-dynamic-lp64-seq"; +#[cfg(feature = "mkl-dynamic-ilp64-iomp")] +const MKL_CONFIG: &str = "mkl-dynamic-ilp64-iomp"; +#[cfg(feature = "mkl-dynamic-ilp64-seq")] +const MKL_CONFIG: &str = "mkl-dynamic-ilp64-seq"; + +fn main() -> Result<()> { + let cfg = Config::from_str(MKL_CONFIG).unwrap(); + + // already exists on system + if let Ok(entry) = Entry::from_config(cfg) { + entry.print_cargo_metadata(); + return Ok(()); + } + + // download if not found + if cfg!(feature = "download") { + let path = if cfg!(feature = "xdg-data-home") { + xdg_home_path() } else { - PathBuf::from(env::var("OUT_DIR").expect("Failed to get OUT_DIR")) + PathBuf::from(env::var("OUT_DIR").unwrap()) }; - - intel_mkl_tool::download_default(&out_dir).expect("Failed to downalod Intel-MKL archive"); - out_dir - }; - println!("cargo:rustc-link-search={}", out_dir.display()); - println!("cargo:rustc-link-lib=mkl_intel_lp64"); - println!("cargo:rustc-link-lib=mkl_sequential"); - println!("cargo:rustc-link-lib=mkl_core"); + println!( + r#"cargo:warning="Download Intel MKL archive into {}""#, + path.display() + ); + cfg.download(path)?; + let entry = Entry::from_config(cfg).unwrap(); // must found + entry.print_cargo_metadata(); + } + Ok(()) } diff --git a/intel-mkl-src/tests/link.rs b/intel-mkl-src/tests/link.rs deleted file mode 100644 index 7da3d3a8..00000000 --- a/intel-mkl-src/tests/link.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Link test - -extern crate intel_mkl_src; -extern crate libc; - -use libc::*; - -extern "C" { - fn LAPACKE_dsyev( - matrix_layout: c_int, - jobz: c_char, - uplo: c_char, - n: c_int, - a: *mut c_double, - lda: c_int, - w: *mut c_double, - ) -> c_int; -} - -#[test] -fn link2lapacke() { - let matrix_layout = 102; - let jobz = b'V' as i8; - let uplo = b'U' as i8; - let n = 1; - let mut a = vec![0.0]; - let lda = 1; - let mut w = vec![0.0]; - unsafe { - LAPACKE_dsyev( - matrix_layout, - jobz, - uplo, - n, - a.as_mut_ptr(), - lda, - w.as_mut_ptr(), - ); - } -} diff --git a/intel-mkl-sys/Cargo.toml b/intel-mkl-sys/Cargo.toml index 312c1e37..f04c5474 100644 --- a/intel-mkl-sys/Cargo.toml +++ b/intel-mkl-sys/Cargo.toml @@ -9,9 +9,25 @@ repository = "https://github.com/rust-math/intel-mkl-src" keywords = ["ffi"] license = "MIT" -[dependencies.intel-mkl-src] -path = "../intel-mkl-src" -version = "0.5.0" +[features] +default = ["download", "mkl-dynamic-lp64-iomp"] + +# MKL config +# https://software.intel.com/content/www/us/en/develop/articles/intel-math-kernel-library-intel-mkl-and-pkg-config-tool.html +mkl-static-lp64-iomp = ["intel-mkl-src/mkl-static-lp64-iomp"] +mkl-static-lp64-seq = ["intel-mkl-src/mkl-static-lp64-seq"] +mkl-static-ilp64-iomp = ["intel-mkl-src/mkl-static-ilp64-iomp"] +mkl-static-ilp64-seq = ["intel-mkl-src/mkl-static-ilp64-seq"] +mkl-dynamic-lp64-iomp = ["intel-mkl-src/mkl-dynamic-lp64-iomp"] +mkl-dynamic-lp64-seq = ["intel-mkl-src/mkl-dynamic-lp64-seq"] +mkl-dynamic-ilp64-iomp = ["intel-mkl-src/mkl-dynamic-ilp64-iomp"] +mkl-dynamic-ilp64-seq = ["intel-mkl-src/mkl-dynamic-ilp64-seq"] + +# Enable downloading from AWS S3 when not found +download = ["intel-mkl-src/download"] + +[dependencies] +intel-mkl-src = { path = "../intel-mkl-src", version = "0.5.0", default-features = false } [dev-dependencies] criterion = "0.3.0" diff --git a/intel-mkl-tool/Cargo.toml b/intel-mkl-tool/Cargo.toml index e30eea3e..bcecda65 100644 --- a/intel-mkl-tool/Cargo.toml +++ b/intel-mkl-tool/Cargo.toml @@ -28,6 +28,9 @@ zstd = "0.5.1" structopt = { version = "0.3.5", optional = true } env_logger = { version = "0.7.1", optional = true } +[dev-dependencies] +paste = "*" + [[bin]] name = "intel-mkl-tool" path = "src/cli.rs" diff --git a/intel-mkl-tool/src/cli.rs b/intel-mkl-tool/src/cli.rs index 7317a27f..a4dbd5b5 100644 --- a/intel-mkl-tool/src/cli.rs +++ b/intel-mkl-tool/src/cli.rs @@ -64,7 +64,7 @@ fn main() -> Result<()> { } else { println!("{:<22}", lib.name()); } - for (path, name) in &lib.files() { + for (path, name) in &lib.found_files() { println!(" {:<25} at {}", name, path.display()); } } diff --git a/intel-mkl-tool/src/config.rs b/intel-mkl-tool/src/config.rs index 7884f378..23861514 100644 --- a/intel-mkl-tool/src/config.rs +++ b/intel-mkl-tool/src/config.rs @@ -95,9 +95,13 @@ impl Config { } /// Common components + /// + /// The order must be following (or equivalent libs) + /// + /// mkl_intel_lp64 > mkl_intel_thread > mkl_core > iomp5 + /// pub fn libs(&self) -> Vec { let mut libs = Vec::new(); - libs.push("mkl_core".into()); match self.index_size { Interface::LP64 => { libs.push("mkl_intel_lp64".into()); @@ -108,13 +112,16 @@ impl Config { }; match self.parallel { Threading::OpenMP => { - libs.push("iomp5".into()); libs.push("mkl_intel_thread".into()); } Threading::Sequential => { libs.push("mkl_sequential".into()); } }; + libs.push("mkl_core".into()); + if matches!(self.parallel, Threading::OpenMP) { + libs.push("iomp5".into()); + } libs } @@ -213,4 +220,35 @@ mod tests { assert!(Config::from_str("mkl-static-lp64-omp").is_err()); Ok(()) } + + macro_rules! impl_test_download { + ($name:expr) => { + paste::item! { + #[test] + fn []() -> Result<()> { + let name = $name; + let cfg = Config::from_str(name)?; + cfg.download(format!("test_download/{}", name))?; + Ok(()) + } + } + }; + } + + mod dynamic { + use super::*; + impl_test_download!("mkl-dynamic-lp64-seq"); + impl_test_download!("mkl-dynamic-lp64-iomp"); + impl_test_download!("mkl-dynamic-ilp64-seq"); + impl_test_download!("mkl-dynamic-ilp64-iomp"); + } + + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + mod static_ { + use super::*; + impl_test_download!("mkl-static-lp64-seq"); + impl_test_download!("mkl-static-lp64-iomp"); + impl_test_download!("mkl-static-ilp64-seq"); + impl_test_download!("mkl-static-ilp64-iomp"); + } } diff --git a/intel-mkl-tool/src/entry.rs b/intel-mkl-tool/src/entry.rs index 03e3c6c8..5aaf0f73 100644 --- a/intel-mkl-tool/src/entry.rs +++ b/intel-mkl-tool/src/entry.rs @@ -26,8 +26,14 @@ impl Targets { Self(targets) } - fn found_all(&self) -> bool { - self.0.iter().all(|(_key, value)| value.is_some()) + fn found_files(&self) -> Vec<(PathBuf, String)> { + self.iter() + .flat_map(|(name, path)| Some((path.as_ref()?.clone(), name.clone()))) + .collect() + } + + fn found_any(&self) -> bool { + self.0.iter().any(|(_key, value)| value.is_some()) } fn seek(&mut self, dir: &Path) { @@ -93,7 +99,7 @@ impl Entry { let path = xdg_home_path().join(config.name()); targets.seek(&path); - if targets.found_all() { + if targets.found_any() { return Ok(Self { config, targets }); } else { // None found @@ -105,11 +111,8 @@ impl Entry { self.config.name() } - pub fn files(&self) -> Vec<(PathBuf, String)> { - self.targets - .iter() - .map(|(name, path)| (path.as_ref().unwrap().clone(), name.clone())) - .collect() + pub fn found_files(&self) -> Vec<(PathBuf, String)> { + self.targets.found_files() } pub fn available() -> Vec { @@ -124,7 +127,7 @@ impl Entry { /// - This will not work for OUT_DIR or XDG_DATA_HOME entry, /// and returns Error in these cases pub fn version(&self) -> Result<(u32, u32)> { - for (path, _) in &self.files() { + for (path, _) in &self.found_files() { // assumes following directory structure: // // - mkl @@ -178,7 +181,7 @@ impl Entry { let zstd = zstd::stream::write::Encoder::new(buf, 6)?; let mut ar = tar::Builder::new(zstd); ar.mode(tar::HeaderMode::Deterministic); - for (path, name) in self.files() { + for (path, name) in self.found_files() { let lib = path.join(&name); ar.append_path_with_name(lib, name)?; } @@ -188,7 +191,11 @@ impl Entry { } pub fn print_cargo_metadata(&self) { - let paths: HashSet = self.files().into_iter().map(|(path, _name)| path).collect(); // must be redundant + let paths: HashSet = self + .found_files() + .into_iter() + .map(|(path, _name)| path) + .collect(); // must be redundant for path in paths { println!("cargo:rustc-link-search={}", path.display()); } @@ -198,14 +205,10 @@ impl Entry { println!("cargo:rustc-link-lib=static={}", lib); } LinkType::Shared => { - println!("cargo:rustc-link-lib=shared={}", lib); + println!("cargo:rustc-link-lib=dylib={}", lib); } } } - - for common in &["pthread", "m", "dl"] { - println!("cargo:rustc-link-lib=dylib={}", common); - } } } diff --git a/intel-mkl-tool/src/lib.rs b/intel-mkl-tool/src/lib.rs index 161bc0ae..02a251c9 100644 --- a/intel-mkl-tool/src/lib.rs +++ b/intel-mkl-tool/src/lib.rs @@ -165,14 +165,3 @@ pub fn download_default>(out_dir: P) -> Result<()> { cfg.download(out_dir)?; Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn download() -> Result<()> { - download_default("./test_download")?; - Ok(()) - } -}