├── .gitmodules ├── openblas-build ├── .gitignore ├── src │ ├── lib.rs │ ├── download.rs │ ├── error.rs │ ├── check.rs │ └── build.rs ├── Cargo.toml └── Makefile.conf ├── openblas-src ├── .gitignore ├── src │ └── lib.rs ├── tests │ ├── lib.rs │ └── fortran_lapack.rs ├── Cargo.toml └── build.rs ├── .gitignore ├── Cargo.toml ├── Cross.toml ├── .github └── workflows │ ├── rust.yml │ ├── openblas-build.yml │ └── openblas-src.yml ├── LICENSE.md ├── CHANGELOG.md └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /openblas-build/.gitignore: -------------------------------------------------------------------------------- 1 | test_build/ 2 | -------------------------------------------------------------------------------- /openblas-src/.gitignore: -------------------------------------------------------------------------------- 1 | OpenBLAS-*/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | source_* 3 | target 4 | -------------------------------------------------------------------------------- /openblas-src/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../../README.md")] 2 | #![no_std] 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "openblas-src", 4 | "openblas-build", 5 | ] 6 | 7 | [workspace.package] 8 | rust-version = "1.71.1" 9 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-gnu] 2 | image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main" 3 | pre-build = [ 4 | "dpkg --add-architecture $CROSS_DEB_ARCH", 5 | "apt-get update && apt-get --assume-yes install libopenblas-dev:$CROSS_DEB_ARCH libssl-dev" 6 | ] 7 | 8 | [target.armv7-unknown-linux-gnueabihf] 9 | image = "ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf:main" 10 | pre-build = [ 11 | "dpkg --add-architecture $CROSS_DEB_ARCH", 12 | "apt-get update && apt-get --assume-yes install libopenblas-dev:$CROSS_DEB_ARCH libssl-dev" 13 | ] 14 | 15 | -------------------------------------------------------------------------------- /openblas-src/tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | extern crate openblas_src; 3 | 4 | use libc::c_float; 5 | 6 | extern "C" { 7 | pub fn srotg_(a: *mut c_float, b: *mut c_float, c: *mut c_float, s: *mut c_float); 8 | } 9 | 10 | #[test] 11 | fn link() { 12 | unsafe { 13 | let mut a: f32 = 0.0; 14 | let mut b: f32 = 0.0; 15 | let mut c: f32 = 42.0; 16 | let mut d: f32 = 42.0; 17 | srotg_( 18 | &mut a as *mut _, 19 | &mut b as *mut _, 20 | &mut c as *mut _, 21 | &mut d as *mut _, 22 | ); 23 | assert!(c == 1.0); 24 | assert!(d == 0.0); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /openblas-build/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Helper crate for openblas-src/build.rs 2 | //! 3 | //! The `make` system of [OpenBLAS][OpenBLAS] has large number of inputs, 4 | //! and detects environmental informations. 5 | //! 6 | //! Requirements 7 | //! ------------ 8 | //! 9 | //! This crate executes `make` as external command, 10 | //! and inspects its deliverables using [GNU binutils][binutils] (`nm` and `objdump`). 11 | //! 12 | //! [binutils]: https://www.gnu.org/software/binutils/ 13 | //! [OpenBLAS]: https://github.com/xianyi/OpenBLAS 14 | 15 | mod build; 16 | mod check; 17 | mod download; 18 | pub mod error; 19 | pub use build::*; 20 | pub use check::*; 21 | pub use download::*; 22 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | paths: 9 | - 'openblas-build/**' 10 | - 'openblas-src/**' 11 | - '.github/workflows/rust.yml' 12 | - '*.toml' 13 | workflow_dispatch: {} 14 | 15 | jobs: 16 | check-format: 17 | runs-on: ubuntu-22.04 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Check format 21 | run: cargo fmt -- --check 22 | 23 | clippy: 24 | runs-on: ubuntu-22.04 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | submodules: "true" 29 | - name: Check format 30 | run: cargo clippy 31 | -------------------------------------------------------------------------------- /openblas-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openblas-build" 3 | version = "0.10.13" 4 | license = "Apache-2.0/MIT" 5 | edition = "2018" 6 | authors = ["Toshiki Teramura "] 7 | description = "The package provides a build helper for OpenBLAS." 8 | documentation = "https://docs.rs/openblas-build" 9 | homepage = "https://github.com/blas-lapack-rs/openblas-src" 10 | repository = "https://github.com/blas-lapack-rs/openblas-src" 11 | readme = "../README.md" 12 | exclude = ["test_build/"] 13 | rust-version = "1.71.1" 14 | 15 | [dependencies] 16 | anyhow = "1.0.68" 17 | cc = "1.0" 18 | flate2 = "1.0.25" 19 | tar = "0.4.38" 20 | thiserror = "2.0" 21 | ureq = { version = "3.0", default-features = false, features = [ 22 | "native-tls", 23 | ] } 24 | 25 | [dev-dependencies] 26 | walkdir = "2.0" 27 | -------------------------------------------------------------------------------- /.github/workflows/openblas-build.yml: -------------------------------------------------------------------------------- 1 | name: openblas-build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - 'openblas-build/**' 9 | - '.github/workflows/openblas-build.yml' 10 | pull_request: 11 | paths: 12 | - 'openblas-build/**' 13 | workflow_dispatch: {} 14 | 15 | jobs: 16 | linux: 17 | runs-on: ubuntu-22.04 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | test_target: 22 | - build_no_lapacke 23 | - build_no_shared 24 | - build_openmp 25 | env: 26 | RUST_BACKTRACE: 1 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: "recursive" 31 | - name: Install gfortran by apt 32 | run: | 33 | sudo apt update 34 | sudo apt install -y gfortran 35 | - name: Common minor tests 36 | run: cargo test -p openblas-build 37 | - name: Build test 38 | run: cargo test ${{ matrix.test_target }} -p openblas-build -- --ignored 39 | -------------------------------------------------------------------------------- /openblas-build/Makefile.conf: -------------------------------------------------------------------------------- 1 | OSNAME=Linux 2 | ARCH=x86_64 3 | C_COMPILER=GCC 4 | BINARY32= 5 | BINARY64=1 6 | CEXTRALIB=-L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0 -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../.. -lc 7 | F_COMPILER=GFORTRAN 8 | FC=gfortran 9 | BU=_ 10 | FEXTRALIB=-L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0 -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../.. -lgfortran -lm -lquadmath -lm -lc 11 | CORE=HASWELL 12 | LIBCORE=haswell 13 | NUM_CORES=12 14 | HAVE_MMX=1 15 | HAVE_SSE=1 16 | HAVE_SSE2=1 17 | HAVE_SSE3=1 18 | HAVE_SSSE3=1 19 | HAVE_SSE4_1=1 20 | HAVE_SSE4_2=1 21 | HAVE_AVX=1 22 | HAVE_AVX2=1 23 | HAVE_FMA3=1 24 | MAKE += -j 12 25 | SHGEMM_UNROLL_M=8 26 | SHGEMM_UNROLL_N=4 27 | SGEMM_UNROLL_M=8 28 | SGEMM_UNROLL_N=4 29 | DGEMM_UNROLL_M=4 30 | DGEMM_UNROLL_N=8 31 | QGEMM_UNROLL_M=2 32 | QGEMM_UNROLL_N=2 33 | CGEMM_UNROLL_M=8 34 | CGEMM_UNROLL_N=2 35 | ZGEMM_UNROLL_M=4 36 | ZGEMM_UNROLL_N=2 37 | XGEMM_UNROLL_M=1 38 | XGEMM_UNROLL_N=1 39 | CGEMM3M_UNROLL_M=8 40 | CGEMM3M_UNROLL_N=4 41 | ZGEMM3M_UNROLL_M=4 42 | ZGEMM3M_UNROLL_N=4 43 | XGEMM3M_UNROLL_M=2 44 | XGEMM3M_UNROLL_N=2 45 | -------------------------------------------------------------------------------- /openblas-build/src/download.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use std::path::{Path, PathBuf}; 3 | use ureq::{ 4 | config::Config, 5 | tls::{TlsConfig, TlsProvider}, 6 | }; 7 | 8 | const OPENBLAS_VERSION: &str = "0.3.30"; 9 | 10 | pub fn openblas_source_url() -> String { 11 | format!( 12 | "https://github.com/OpenMathLib/OpenBLAS/releases/download/v{}/OpenBLAS-{}.tar.gz", 13 | OPENBLAS_VERSION, OPENBLAS_VERSION 14 | ) 15 | } 16 | 17 | pub fn download(out_dir: &Path) -> Result { 18 | let dest = out_dir.join(format!("OpenBLAS-{}", OPENBLAS_VERSION)); 19 | if !dest.exists() { 20 | let buf = get_agent() 21 | .get(&openblas_source_url()) 22 | .call()? 23 | .into_body() 24 | .into_reader(); 25 | let gz_stream = flate2::read::GzDecoder::new(buf); 26 | let mut ar = tar::Archive::new(gz_stream); 27 | ar.unpack(out_dir)?; 28 | assert!(dest.exists()); 29 | } 30 | Ok(dest) 31 | } 32 | 33 | fn get_agent() -> ureq::Agent { 34 | Config::builder() 35 | .tls_config( 36 | TlsConfig::builder() 37 | .provider(TlsProvider::NativeTls) 38 | .build(), 39 | ) 40 | .build() 41 | .new_agent() 42 | } 43 | -------------------------------------------------------------------------------- /openblas-src/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openblas-src" 3 | version = "0.10.13" 4 | license = "Apache-2.0/MIT" 5 | edition = "2018" 6 | authors = [ 7 | "Corey Richardson ", 8 | "Ethan Smith ", 9 | "Ivan Ukhov ", 10 | "Jim Turner ", 11 | "Ken Elkabany ", 12 | "Mitsutoshi Aoe ", 13 | "Steve Harris ", 14 | "Toshiki Teramura ", 15 | ] 16 | description = "The package provides a source of BLAS and LAPACK via OpenBLAS." 17 | documentation = "https://docs.rs/openblas-src" 18 | homepage = "https://github.com/blas-lapack-rs/openblas-src" 19 | repository = "https://github.com/blas-lapack-rs/openblas-src" 20 | readme = "../README.md" 21 | categories = ["science"] 22 | keywords = ["linear-algebra"] 23 | build = "build.rs" 24 | links = "openblas" 25 | rust-version = "1.71.1" 26 | 27 | [features] 28 | default = ["cblas", "lapacke"] 29 | 30 | cache = [] 31 | cblas = [] 32 | lapacke = [] 33 | static = [] 34 | system = [] 35 | 36 | [dev-dependencies] 37 | libc = "0.2" 38 | 39 | [build-dependencies] 40 | pkg-config = "0.3.30" 41 | dirs = "6.0.0" 42 | openblas-build = { version = "0.10.13", path = "../openblas-build" } 43 | 44 | [target.'cfg(target_os="windows")'.build-dependencies] 45 | vcpkg = "0.2" 46 | -------------------------------------------------------------------------------- /openblas-build/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{io, path::*, process::Command}; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error)] 5 | pub enum Error { 6 | #[error("Subprocess returns with non-zero status: {status}")] 7 | NonZeroExitStatus { status: i32 }, 8 | 9 | #[error("Subprocess cannot start: {error:?}")] 10 | SubprocessCannotStart { error: io::Error }, 11 | 12 | #[error("Fortran compiler not found. It is necessary to build LAPACK.")] 13 | FortranCompilerNotFound, 14 | 15 | #[error("Cannot canonicalize path in Linker flag: {}", path.display())] 16 | CannotCanonicalizePath { path: PathBuf }, 17 | 18 | #[error("Makefile.conf does not exist in {}", out_dir.display())] 19 | MakeConfNotExist { out_dir: PathBuf }, 20 | 21 | #[error("Library file does not exist: {}", path.display())] 22 | LibraryNotExist { path: PathBuf }, 23 | 24 | #[error("Target {} is unsupported", target)] 25 | UnsupportedTarget { target: String }, 26 | 27 | #[error( 28 | "Cross compile information is missing and cannot be inferred, {}", 29 | info 30 | )] 31 | MissingCrossCompileInfo { info: String }, 32 | 33 | #[error("Other IO errors: {0:?}")] 34 | IOError(#[from] io::Error), 35 | } 36 | 37 | pub(crate) trait CheckCall { 38 | fn check_call(&mut self) -> Result<(), Error>; 39 | } 40 | 41 | impl CheckCall for Command { 42 | fn check_call(&mut self) -> Result<(), Error> { 43 | match self.status() { 44 | Ok(status) => { 45 | if !status.success() { 46 | Err(Error::NonZeroExitStatus { 47 | status: status.code().unwrap_or(-1), 48 | }) 49 | } else { 50 | Ok(()) 51 | } 52 | } 53 | Err(error) => Err(Error::SubprocessCannotStart { error }), 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /openblas-src/tests/fortran_lapack.rs: -------------------------------------------------------------------------------- 1 | // FIXME This should be test also on Windows and macOS. 2 | // 3 | // However, we have several problems: 4 | // 5 | // - we cannot build Fortran part of OpenBLAS on windows-msvc platform 6 | // because the absence of Fortran compiler 7 | // 8 | // - In macOS, we can get gfortran by `brew install gcc`, 9 | // but it is too time-consuming to execute on CI. 10 | // GitHub Actions's macOS instance says gfotran is "installed", 11 | // but it is too fragile me to give up using it. 12 | // 13 | #![cfg(target_os = "linux")] 14 | 15 | extern crate openblas_src as _src; 16 | 17 | extern "C" { 18 | fn dormbr_( 19 | vect: *const u8, 20 | side: *const u8, 21 | trans: *const u8, 22 | m: *const i32, 23 | n: *const i32, 24 | k: *const i32, 25 | A: *const f64, 26 | lda: *const i32, 27 | tau: *const f64, 28 | C: *mut f64, 29 | ldc: *const i32, 30 | work: *mut f64, 31 | lwork: *const i32, 32 | info: *mut i32, 33 | ); 34 | } 35 | 36 | // `dormbr_` is imported from reference LAPACK written in Fortran into OpenBLAS project. 37 | // This test will fail to link when OpenBLAS does not build Fortran part. 38 | #[test] 39 | fn test_link_lapack() { 40 | let m = 1; 41 | let n = 1; 42 | let k = 1; 43 | let vect = b'Q'; 44 | let side = b'L'; 45 | let trans = b'N'; 46 | let a = vec![0.0]; 47 | let lda = 1; 48 | let mut c = vec![0.0]; 49 | let ldc = 1; 50 | let tau = 0.0; 51 | let mut work = vec![0.0]; 52 | let lwork = 1; 53 | let mut info = 0; 54 | unsafe { 55 | dormbr_( 56 | &vect, 57 | &side, 58 | &trans, 59 | &m, 60 | &n, 61 | &k, 62 | a.as_ptr(), 63 | &lda, 64 | &tau, 65 | c.as_mut_ptr(), 66 | &ldc, 67 | work.as_mut_ptr(), 68 | &lwork, 69 | &mut info, 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | The project is dual licensed under the terms of the Apache License, Version 2.0, 4 | and the MIT License. You may obtain copies of the two licenses at 5 | 6 | * https://www.apache.org/licenses/LICENSE-2.0 and 7 | * https://opensource.org/licenses/MIT, respectively. 8 | 9 | The following two notices apply to every file of the project. 10 | 11 | ## The Apache License 12 | 13 | ``` 14 | Copyright 2015–2021 The openblas-src Developers 15 | 16 | Licensed under the Apache License, Version 2.0 (the “License”); you may not use 17 | this file except in compliance with the License. You may obtain a copy of the 18 | License at 19 | 20 | http://www.apache.org/licenses/LICENSE-2.0 21 | 22 | Unless required by applicable law or agreed to in writing, software distributed 23 | under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 24 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 25 | specific language governing permissions and limitations under the License. 26 | ``` 27 | 28 | ## The MIT License 29 | 30 | ``` 31 | Copyright 2015–2021 The openblas-src Developers 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of 34 | this software and associated documentation files (the “Software”), to deal in 35 | the Software without restriction, including without limitation the rights to 36 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 37 | the Software, and to permit persons to whom the Software is furnished to do so, 38 | subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 45 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 46 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 47 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | ``` 50 | -------------------------------------------------------------------------------- /.github/workflows/openblas-src.yml: -------------------------------------------------------------------------------- 1 | name: openblas-src 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - 'openblas-build/**' 9 | - 'openblas-src/**' 10 | - '.github/workflows/openblas-src.yml' 11 | - '*.toml' 12 | pull_request: 13 | paths: 14 | - 'openblas-build/**' 15 | - 'openblas-src/**' 16 | - '.github/workflows/openblas-src.yml' 17 | - '*.toml' 18 | workflow_dispatch: {} 19 | 20 | jobs: 21 | windows-msvc: 22 | runs-on: windows-2022 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | triple: 27 | - x64-windows 28 | - x64-windows-static 29 | - x64-windows-static-md 30 | steps: 31 | - uses: actions/checkout@v4 32 | - uses: actions/cache@v4 33 | with: 34 | path: ./vcpkg 35 | key: vcpkg-openblas-${{ matrix.triple }} 36 | - name: Install vcpkg 37 | run: | 38 | git clone https://github.com/Microsoft/vcpkg.git --depth 1 39 | cd vcpkg 40 | ./bootstrap-vcpkg.bat 41 | - name: Install OpenBLAS by vcpkg 42 | run: | 43 | ./vcpkg/vcpkg.exe install openblas:${{ matrix.triple }} 44 | - name: Test features=system 45 | run: cargo test --features=system -p openblas-src 46 | env: 47 | VCPKG_ROOT: ${{ github.workspace }}/vcpkg 48 | if: ${{ matrix.triple == 'x64-windows' }} 49 | 50 | - name: Test features=system,static 51 | run: cargo test --features=system,static -p openblas-src 52 | env: 53 | VCPKG_ROOT: ${{ github.workspace }}/vcpkg 54 | if: ${{ matrix.triple == 'x64-windows-static-md' }} 55 | 56 | - name: Test features=system,static with crt-static 57 | run: cargo test --features=system,static -p openblas-src 58 | env: 59 | VCPKG_ROOT: ${{ github.workspace }}/vcpkg 60 | RUSTFLAGS: "-C target-feature=+crt-static" 61 | if: ${{ matrix.triple == 'x64-windows-static' }} 62 | 63 | macos: 64 | runs-on: macos-14 65 | strategy: 66 | fail-fast: false 67 | matrix: 68 | feature: 69 | - "" 70 | - static 71 | - system 72 | steps: 73 | - uses: actions/checkout@v4 74 | with: 75 | submodules: "recursive" 76 | - name: Install OpenBLAS by homebrew 77 | run: | 78 | brew install openblas 79 | if: ${{ contains(matrix.feature, 'system') }} 80 | - name: Test features=${{ matrix.feature }} 81 | run: cargo test --features=${{ matrix.feature }} -p openblas-src 82 | 83 | x86_64-unknown-linux-gnu: 84 | runs-on: ubuntu-22.04 85 | strategy: 86 | fail-fast: false 87 | matrix: 88 | feature: 89 | - "" 90 | - static 91 | - system 92 | steps: 93 | - uses: actions/checkout@v4 94 | with: 95 | submodules: "recursive" 96 | - name: Install gfortran by apt 97 | run: | 98 | sudo apt update 99 | sudo apt install -y gfortran 100 | - name: Install OpenBLAS by apt 101 | run: | 102 | sudo apt update 103 | sudo apt install -y libopenblas-dev 104 | if: ${{ contains(matrix.feature, 'system') }} 105 | - name: Test features=${{ matrix.feature }} 106 | run: cargo test --features=${{ matrix.feature }} -p openblas-src 107 | 108 | cross: 109 | name: ${{matrix.target}} (${{matrix.feature}}) 110 | runs-on: ubuntu-22.04 111 | strategy: 112 | fail-fast: false 113 | matrix: 114 | feature: 115 | - "" 116 | - static 117 | - system 118 | target: 119 | - aarch64-unknown-linux-gnu 120 | - armv7-unknown-linux-gnueabihf 121 | steps: 122 | - uses: actions/checkout@v4 123 | - name: Install toolchain 124 | uses: dtolnay/rust-toolchain@stable 125 | with: 126 | target: ${{matrix.target}} 127 | - name: Install Cross 128 | uses: taiki-e/install-action@v2 129 | with: 130 | tool: cross 131 | - name: Test features=${{ matrix.feature }} 132 | run: cross test --target ${{matrix.target}} --features=${{ matrix.feature }} --manifest-path=openblas-src/Cargo.toml 133 | msrv-test: 134 | name: MSRV test 135 | runs-on: ubuntu-22.04 136 | steps: 137 | - uses: actions/checkout@v4 138 | - name: Install OpenBLAS by apt 139 | run: | 140 | sudo apt update 141 | sudo apt install -y libopenblas-dev 142 | - name: ensure dependencies 143 | run: CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS="fallback" cargo update 144 | - name: Install toolchain 145 | uses: dtolnay/rust-toolchain@1.71 146 | - name: Test MSRV 147 | run: cargo check -p openblas-src --locked --features=system 148 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | [Unreleased](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.13...master) 4 | ----------- 5 | 6 | [0.10.13 - 2025-09-09](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.12...v0.10.13) 7 | ----------- 8 | 9 | ## What's Changed 10 | * Remove duplicated link instructions by @jkawamoto in https://github.com/blas-lapack-rs/openblas-src/pull/141 11 | * Export path to libopenblas for external usage by @jkawamoto in https://github.com/blas-lapack-rs/openblas-src/pull/142 12 | 13 | ## New Contributors 14 | * @jkawamoto made their first contribution in https://github.com/blas-lapack-rs/openblas-src/pull/141 15 | 16 | [0.10.12 - 2025-06-21](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.11...v0.10.12) 17 | ----------- 18 | 19 | ### What's Changed 20 | 21 | * Remove unused deps by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/138 22 | * Bump ureq from 2 to 3. Bump openblas from 0.3.28 to 0.3.30 by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/137 23 | 24 | [0.10.11 - 2024-12-19](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.10...v0.10.11) 25 | ----------- 26 | 27 | ### What's Changed 28 | 29 | * Set MSRV to 1.71.1 and add CI to test MSRV by @Dirreke https://github.com/blas-lapack-rs/openblas-src/pull/134 30 | 31 | [0.10.10 - 2024-12-08](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.9...v0.10.10) 32 | ----------- 33 | 34 | ### What's Changed 35 | 36 | * Take proxy environment variables into account by @xoolive in https://github.com/blas-lapack-rs/openblas-src/pull/120 37 | * rewrite CI by @gkobeaga and @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/123 38 | * Update OpenBLAS to version 0.3.28 by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/119 39 | * Use pkg-config and fix build on doc.rs by @Fuuzetsu ,@j-baker and @HenrikJStromberg in https://github.com/blas-lapack-rs/openblas-src/pull/125 40 | * Add cache for windows CI by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/128 41 | Refactor code, Drop LAPACKE inspection, Drop FORTRAN check by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/127 42 | * Detect TARGET, CC, HOSTCC, FC automically when cross-compiling by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/129 43 | * Update README and CHANGELOGS by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/130 44 | 45 | [0.10.9 - 2024-02-03](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.8...openblas-src-v0.10.9) 46 | -------------------- 47 | 48 | ### What's Changed 49 | 50 | * Use ubuntu 22.04 image on CI by @termoshtt in https://github.com/blas-lapack-rs/openblas-src/pull/110 51 | * OpenBLAS v0.3.25 & Extended Target Support & Build Fixes by @gkobeaga in https://github.com/blas-lapack-rs/openblas-src/pull/108 52 | * add rerun-if flags by @Dirreke in https://github.com/blas-lapack-rs/openblas-src/pull/105 53 | * respect OPENBLAS_{{CC, FC, HOSTCC}} env vars on linux by @mike-kfed in https://github.com/blas-lapack-rs/openblas-src/pull/102 54 | * Use macos-14 instance for CI by @termoshtt in https://github.com/blas-lapack-rs/openblas-src/pull/112 55 | 56 | [0.10.8 - 2024-02-03](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.7...openblas-src-v0.10.8) 57 | -------------------- 58 | 59 | ### What's Changed 60 | 61 | * Use native-tls/native-certs features of ureq crate by @lazareviczoran in https://github.com/blas-lapack-rs/openblas-src/pull/98 62 | 63 | [0.10.7 - 2023-01-14](https://github.com/blas-lapack-rs/openblas-src/compare/openblas-src-v0.10.5...openblas-src-v0.10.7) 64 | -------------------- 65 | 66 | 0.10.6 has been yanked 67 | 68 | ### What's Changed 69 | 70 | * homebrew directory depends on architecture. Include libomp libs by @maparent in https://github.com/blas-lapack-rs/openblas-src/pull/89 71 | * Use `brew --prefix` command to get library path by @termoshtt in https://github.com/blas-lapack-rs/openblas-src/pull/93 72 | * Upgrade OpenBLAS to 0.3.21 by @termoshtt in https://github.com/blas-lapack-rs/openblas-src/pull/92 73 | * Use tar.gz image of OpenBLAS by @termoshtt in https://github.com/blas-lapack-rs/openblas-src/pull/95 74 | * Expand tar.gz on `OUT_DIR` by @termoshtt in https://github.com/blas-lapack-rs/openblas-src/pull/96 75 | * Download OpenBLAS source code from GitHub Release in build.rs by @termoshtt in https://github.com/blas-lapack-rs/openblas-src/pull/97 76 | 77 | 0.10.5 - 2022-08-27 78 | -------------------- 79 | 80 | From this release, `openblas-build` crate will have same version as `openblas-src` crate. 81 | 82 | ### Fixed 83 | - Add support for using a custom target under linux https://github.com/blas-lapack-rs/openblas-src/pull/78 84 | 85 | ### Changed 86 | - OpenBLAS 0.3.20 https://github.com/blas-lapack-rs/openblas-src/pull/85 87 | - OpenBLAS 0.3.17 https://github.com/blas-lapack-rs/openblas-src/pull/76 88 | - Use dynamic CRT link for vcpkg https://github.com/blas-lapack-rs/openblas-src/pull/69 https://github.com/blas-lapack-rs/openblas-src/pull/71 89 | 90 | ### Internal 91 | - Run cargo-clippy and rustfmt on CI https://github.com/blas-lapack-rs/openblas-src/pull/86 92 | 93 | 0.10.4 - 2021-04-03 94 | -------------------- 95 | 96 | ### Fixed 97 | - Change link search order https://github.com/blas-lapack-rs/openblas-src/pull/61 98 | 99 | 0.10.3 - 2021-04-02 100 | ------------------- 101 | 102 | ### Fixed 103 | - Update "cache" feature description https://github.com/blas-lapack-rs/openblas-src/pull/63 104 | 105 | ### Changed 106 | - Upgrade OpenBLAS to 0.3.14 https://github.com/blas-lapack-rs/openblas-src/pull/65 107 | 108 | 0.10.2 - 2021-01-30 109 | -------------------- 110 | 111 | 0.10.0 and 0.10.1 has been yanked and changes from 0.9.0 is summarized here. 112 | 113 | ### Fixed 114 | - Detect OpenBLAS does not build some parts of LAPACK while "lapack" feature is enabled https://github.com/blas-lapack-rs/openblas-src/issues/49 115 | - openblas-build crate has been introduced to resolve this issue 116 | 117 | ### Added 118 | - openblas-build crate is introduced to sneak OpenBLAS build system configure 119 | - Link test for LAPACK routines written in Fortran https://github.com/blas-lapack-rs/openblas-src/pull/43 120 | - Switch to openblas-build on Linux https://github.com/blas-lapack-rs/openblas-src/pull/52 121 | - Not on macOS due to https://github.com/blas-lapack-rs/openblas-src/issues/54 122 | - Create openblas-build crate https://github.com/blas-lapack-rs/openblas-src/pull/47 123 | - cargo-workspace setup https://github.com/blas-lapack-rs/openblas-src/pull/45 124 | - "system" feature support for windows-msvc target through vcpkg https://github.com/blas-lapack-rs/openblas-src/pull/35 125 | 126 | ### Changed 127 | - Use Rust 2018 edition https://github.com/blas-lapack-rs/openblas-src/pull/46 128 | - Switch to GitHub Actions from AppVeyor + Travis CI https://github.com/blas-lapack-rs/openblas-src/pull/40 129 | 130 | 0.9.0 - 2020-03-08 131 | -------------------- 132 | 133 | ### Changed 134 | - Build products are placed on OUT_DIR to work `cargo clean` properly https://github.com/blas-lapack-rs/openblas-src/pull/31 135 | - Previous behavior (placed on .cargo/) is reproduced with "cache" feature 136 | -------------------------------------------------------------------------------- /openblas-build/src/check.rs: -------------------------------------------------------------------------------- 1 | //! Check make results 2 | 3 | use crate::error::*; 4 | use std::{ 5 | collections::HashSet, 6 | fs, 7 | hash::Hash, 8 | io::{self, BufRead}, 9 | path::*, 10 | }; 11 | 12 | /// Parse compiler linker flags, `-L` and `-l` 13 | /// 14 | /// - Search paths defined by `-L` will be removed if not exists, 15 | /// and will be canonicalize 16 | /// 17 | /// ``` 18 | /// use openblas_build::*; 19 | /// let info = LinkFlags::parse("-L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0 -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../.. -lc").unwrap(); 20 | /// assert_eq!(info.libs, vec!["c"]); 21 | /// ``` 22 | #[derive(Debug, Clone, Default)] 23 | pub struct LinkFlags { 24 | /// Existing paths specified by `-L` 25 | pub search_paths: Vec, 26 | /// Libraries specified by `-l` 27 | pub libs: Vec, 28 | } 29 | 30 | fn as_sorted_vec(set: HashSet) -> Vec { 31 | let mut v: Vec<_> = set.into_iter().collect(); 32 | v.sort(); 33 | v 34 | } 35 | 36 | impl LinkFlags { 37 | pub fn parse(line: &str) -> Result { 38 | let mut search_paths = HashSet::new(); 39 | let mut libs = HashSet::new(); 40 | for entry in line.split(' ') { 41 | if entry.starts_with("-L") { 42 | let path = PathBuf::from(entry.trim_start_matches("-L")); 43 | if !path.exists() { 44 | continue; 45 | } 46 | search_paths.insert( 47 | path.canonicalize() 48 | .map_err(|_| Error::CannotCanonicalizePath { path })?, 49 | ); 50 | } 51 | if entry.starts_with("-l") { 52 | libs.insert(entry.trim_start_matches("-l").into()); 53 | } 54 | } 55 | Ok(LinkFlags { 56 | search_paths: as_sorted_vec(search_paths), 57 | libs: as_sorted_vec(libs), 58 | }) 59 | } 60 | } 61 | 62 | /// Parse Makefile.conf which generated by OpenBLAS make system 63 | #[derive(Debug, Clone, Default)] 64 | pub struct MakeConf { 65 | pub os_name: String, 66 | pub no_fortran: bool, 67 | pub c_extra_libs: LinkFlags, 68 | pub f_extra_libs: LinkFlags, 69 | } 70 | 71 | impl MakeConf { 72 | /// Parse from file 73 | pub fn new>(path: P) -> Result { 74 | let mut detail = MakeConf::default(); 75 | let f = fs::File::open(&path).map_err(|_| Error::MakeConfNotExist { 76 | out_dir: path.as_ref().to_owned(), 77 | })?; 78 | let buf = io::BufReader::new(f); 79 | for line in buf.lines() { 80 | let line = line.expect("Makefile.conf should not include non-UTF8 string"); 81 | if line.is_empty() { 82 | continue; 83 | } 84 | let entry: Vec<_> = line.split('=').collect(); 85 | if entry.len() != 2 { 86 | continue; 87 | } 88 | match entry[0] { 89 | "OSNAME" => detail.os_name = entry[1].into(), 90 | "NOFORTRAN" => detail.no_fortran = true, 91 | "CEXTRALIB" => detail.c_extra_libs = LinkFlags::parse(entry[1])?, 92 | "FEXTRALIB" => detail.f_extra_libs = LinkFlags::parse(entry[1])?, 93 | _ => continue, 94 | } 95 | #[cfg(target_os = "macos")] 96 | detail.c_extra_libs.libs.retain(|lib| lib != "to_library"); 97 | } 98 | Ok(detail) 99 | } 100 | } 101 | 102 | /// Library inspection using binutils (`nm` and `objdump`) as external command 103 | /// 104 | /// - Linked shared libraries using `objdump -p` external command. 105 | /// - Global "T" symbols in the text (code) section of library using `nm -g` external command. 106 | #[derive(Debug, Clone)] 107 | #[cfg(test)] 108 | pub(crate) struct LibInspect { 109 | pub libs: Vec, 110 | pub symbols: Vec, 111 | } 112 | 113 | #[cfg(test)] 114 | impl LibInspect { 115 | /// Inspect library file 116 | /// 117 | /// Be sure that `nm -g` and `objdump -p` are executed in this function 118 | pub fn new>(path: P) -> Result { 119 | use std::process::Command; 120 | let path = path.as_ref(); 121 | if !path.exists() { 122 | return Err(Error::LibraryNotExist { 123 | path: path.to_owned(), 124 | }); 125 | } 126 | 127 | let nm_out = Command::new("nm").arg("-g").arg(path).output()?; 128 | 129 | // assumes `nm` output like following: 130 | // 131 | // ``` 132 | // 0000000000909b30 T zupmtr_ 133 | // ``` 134 | let mut symbols: Vec<_> = nm_out 135 | .stdout 136 | .lines() 137 | .flat_map(|line| { 138 | let line = line.expect("nm output should not include non-UTF8 output"); 139 | let entry: Vec<_> = line.trim().split(' ').collect(); 140 | if entry.len() == 3 && entry[1] == "T" { 141 | Some(entry[2].into()) 142 | } else { 143 | None 144 | } 145 | }) 146 | .collect(); 147 | symbols.sort(); // sort alphabetically 148 | 149 | let mut libs: Vec<_> = Command::new("objdump") 150 | .arg("-p") 151 | .arg(path) 152 | .output()? 153 | .stdout 154 | .lines() 155 | .flat_map(|line| { 156 | let line = line.expect("objdump output should not include non-UTF8 output"); 157 | if line.trim().starts_with("NEEDED") { 158 | Some(line.trim().trim_start_matches("NEEDED").trim().to_string()) 159 | } else { 160 | None 161 | } 162 | }) 163 | .collect(); 164 | libs.sort(); 165 | 166 | Ok(LibInspect { libs, symbols }) 167 | } 168 | 169 | pub fn has_cblas(&self) -> bool { 170 | for sym in &self.symbols { 171 | if sym.starts_with("cblas_") { 172 | return true; 173 | } 174 | } 175 | false 176 | } 177 | 178 | pub fn has_lapack(&self) -> bool { 179 | for sym in &self.symbols { 180 | if sym == "dsyev_" { 181 | return true; 182 | } 183 | } 184 | false 185 | } 186 | 187 | pub fn has_lapacke(&self) -> bool { 188 | for sym in &self.symbols { 189 | if sym.starts_with("LAPACKE_") { 190 | return true; 191 | } 192 | } 193 | false 194 | } 195 | 196 | pub fn has_lib(&self, name: &str) -> bool { 197 | for lib in &self.libs { 198 | if let Some(stem) = lib.split('.').next() { 199 | if stem == format!("lib{}", name) { 200 | return true; 201 | } 202 | }; 203 | } 204 | false 205 | } 206 | } 207 | 208 | #[cfg(test)] 209 | mod tests { 210 | use super::*; 211 | #[test] 212 | fn detail_from_makefile_conf() { 213 | let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("Makefile.conf"); 214 | assert!(path.exists()); 215 | let detail = MakeConf::new(path).unwrap(); 216 | assert!(!detail.no_fortran); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openblas-src [![Package][package-img]][package-url] [![Documentation][documentation-img]][documentation-url] [![Build][build-img]][build-url] 2 | 3 | The package provides a source of [BLAS] and [LAPACK] via [OpenBLAS]. 4 | 5 | ## [Architecture] 6 | 7 | ## Configuration 8 | 9 | The following Cargo features are supported: 10 | 11 | * `cache` to build in a shared directory instead of `target` (see below), 12 | * `cblas` to build CBLAS (enabled by default), 13 | * `lapacke` to build LAPACKE (enabled by default), 14 | * `static` to link to OpenBLAS statically, 15 | * `system` to skip building the bundled OpenBLAS. 16 | 17 | Note: On Windows, OpenBLAS can not be built from source. The `system` feature is 18 | supposed to be used. 19 | 20 | ## Dependencies 21 | 22 | If you want to build OpenBLAS from source, you need to have the following dependencies 23 | installed: 24 | 25 | * HOSTCC compiler (e.g., `gcc`, `clang`, or `icc`), 26 | * `make`, 27 | * CC compiler of the target architecture (e.g., `aarch64-linux-gnu-gcc` for `aarch64`), 28 | * Fortran compiler of the target architecture(e.g., `gfortran`, `flang`, or `ifort`), 29 | if there is no Fortran compiler detected, the flag `NOFORTRAN` should be set to `1` 30 | and `OpenBLAS` will only compile BLAS and f2c-converted LAPACK. For more information, 31 | please refer to the [Use f2c translations of LAPACK when no Fortran compiler is available][f2c-translations]. 32 | 33 | ## Caching 34 | 35 | The `cache` feature allows the OpenBLAS build products to be reused between 36 | crates that have different `target` directories. This avoids rebuilding OpenBLAS 37 | unnecessarily. However, this also prevents `cargo clean` from working properly, 38 | since the aforementioned build products will not be removed by the command. 39 | 40 | The OpenBLAS binary will be placed at `${XDG_DATA_HOME}/openblas_build/[hash of 41 | build configure object]`. For example, build with LAPACK and build without 42 | LAPACK will be placed on different directories. If you build OpenBLAS as a 43 | shared library, you need to add the above directory to `LD_LIBRARY_PATH` (for 44 | Linux) or `DYLD_LIBRARY_PATH` (for macOS). Since build from source is not 45 | supported on Windows (see next section), this feature is also not supported. 46 | 47 | ## Windows and vcpkg 48 | 49 | On Windows, `openblas-src` relies on [vcpkg] to find OpenBLAS. Before building, 50 | you must have the correct OpenBLAS installed for your target triplet and kind of 51 | linking. For instance, to link dynamically for the `x86_64-pc-windows-msvc` 52 | toolchain, install `openblas` for the `x64-windows` triplet: 53 | 54 | ```sh 55 | vcpkg install openblas --triplet x64-windows 56 | ``` 57 | 58 | To link OpenBLAS statically, install `openblas` for the `x64-windows-static-md` triplet: 59 | 60 | ```sh 61 | vcpkg install openblas --triplet x64-windows-static-md 62 | ``` 63 | 64 | To link OpenBLAS and C Runtime (CRT) statically, install `openblas` for the 65 | `x64-windows-static` triplet: 66 | 67 | ```sh 68 | vcpkg install openblas --triplet x64-windows-static 69 | ``` 70 | 71 | and build with `+crt-static` option 72 | 73 | ```sh 74 | RUSTFLAGS='-C target-feature=+crt-static' cargo build --target x86_64-pc-windows-msvc 75 | ``` 76 | 77 | Please see the ["Static and dynamic C runtimes" in The Rust reference][crt-static] for detail. 78 | 79 | ## ENV variables 80 | 81 | ### Proxy 82 | 83 | The `openblas-src` crate will detect and use proxy settings from your environment 84 | variables, such as `http_proxy` and `https_proxy` to download necessary dependencies. 85 | 86 | ### Build System through OpenBLAS 87 | 88 | According to the [OpenbLAS build system], the variables used by OpenBLAS could be 89 | passed through environment, such as `DYNAMIC_LIST`, `NUM_THREADS`. 90 | 91 | **HOWEVER**, for some of the variables, the `openblas-src` crate rename them to 92 | others to avoid conflicts with the existing envs. The following is the list of 93 | the variables that are renamed: 94 | 95 | | OpenBLAS variable | openblas-src variable | 96 | | ----------------- | --------------------- | 97 | | TARGET | OPENBLAS_TARGET | 98 | | CC | OPENBLAS_CC | 99 | | FC | OPENBLAS_FC | 100 | | HOSTCC | OPENBLAS_HOSTCC | 101 | | RANLIB | OPENBLAS_RANLIB | 102 | 103 | ### Variables emitted by build.rs 104 | 105 | This crate exports the following environment variables for downstream crates’ build scripts: 106 | 107 | - `DEP_OPENBLAS_INCLUDE`: Absolute path to the OpenBLAS C headers directory (e.g., a directory that 108 | contains `cblas.h`, `lapacke.h` when enabled). 109 | - `DEP_OPENBLAS_LIBRARY`: Absolute path to the produced OpenBLAS library artifact (e.g., `libopenblas.a`, 110 | `libopenblas.so`, `openblas.lib`, depending on platform/linking). 111 | 112 | ## Cross-compile 113 | 114 | Apart from providing the `--target` option to `cargo build`, one also has to 115 | specify the [cross-compilation variables of OpenBLAS][openblas-cross-compile]. 116 | They can be set as environment variables for `cargo build` using the `OPENBLAS_` 117 | prefix as follows: `OPENBLAS_CC`, `OPENBLAS_FC`, `OPENBLAS_HOSTCC`, and 118 | `OPENBLAS_TARGET`. 119 | 120 | If you do not set these variables, the `openblas-build` will try to detect them. 121 | 122 | For `OPENBLAS_TARGET`, the basic target that corresponds to the arch of `--target` 123 | will be used. 124 | 125 | | Rust target | OpenBLAS target | 126 | | ----------- | --------------- | 127 | | aarch64 | ARMV8 | 128 | | arm | ARMV6 | 129 | | armv5te | ARMV5 | 130 | | armv6 | ARMV6 | 131 | | armv7 | ARMV7 | 132 | | loongarch64 | LOONGSONGENERIC | 133 | | mips64 | MIPS64_GENERIC | 134 | | mips64el | MIPS64_GENERIC | 135 | | riscv64 | RISCV64_GENERIC | 136 | | csky | CK860FV | 137 | | sparc | SPARCV7 | 138 | 139 | For `OPENBLAS_CC` and `OPENBLAS_HOSTCC`, the `cc` crate will be used to detect 140 | the compiler. Please refer to the [cc documentation](https://docs.rs/cc/latest/cc/) 141 | for more information. 142 | 143 | For `OPENBLAS_FC`, `openblas-build` will try to detect the compiler through the 144 | `OPENBLAS_CC` set above. It will replace the `gcc` with `gfortran`, `clang` with 145 | `flang`, and `icc` with `ifort` and then test if the Fortran compiler exists. 146 | 147 | Note: If there is no Fortran compiler detected, the build flag `NOFORTRAN` will 148 | be set to `1` and `OpenBLAS` will only compile BLAS and f2c-converted LAPACK. 149 | For more information, please refer to the 150 | [Use f2c translations of LAPACK when no Fortran compiler is available][f2c-translations]. 151 | 152 | ## Contribution 153 | 154 | Your contribution is highly appreciated. Do not hesitate to open an issue or a 155 | pull request. Note that any contribution submitted for inclusion in the project 156 | will be licensed according to the terms given in [LICENSE.md](LICENSE.md). 157 | 158 | [architecture]: https://blas-lapack-rs.github.io/architecture 159 | [blas]: https://en.wikipedia.org/wiki/BLAS 160 | [lapack]: https://en.wikipedia.org/wiki/LAPACK 161 | [OpenBLAS]: http://www.openmathlib.org/OpenBLAS/ 162 | [openblas-cross-compile]: http://www.openmathlib.org/OpenBLAS/docs/user_manual/#cross-compile 163 | [OpenbLAS build system]: http://www.openmathlib.org/OpenBLAS/docs/build_system/ 164 | [vcpkg]: https://github.com/Microsoft/vcpkg 165 | [f2c-translations]: https://github.com/OpenMathLib/OpenBLAS/pull/3539 166 | [crt-static]: https://doc.rust-lang.org/reference/linkage.html#static-and-dynamic-c-runtimes 167 | 168 | [build-img]: https://github.com/blas-lapack-rs/openblas-src/workflows/Rust/badge.svg 169 | [build-url]: https://github.com/blas-lapack-rs/openblas-src/actions?query=workflow%3ARust 170 | [documentation-img]: https://docs.rs/openblas-src/badge.svg 171 | [documentation-url]: https://docs.rs/openblas-src 172 | [package-img]: https://img.shields.io/crates/v/openblas-src.svg 173 | [package-url]: https://crates.io/crates/openblas-src 174 | -------------------------------------------------------------------------------- /openblas-src/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::*, process::Command}; 2 | 3 | #[allow(unused)] 4 | fn run(command: &mut Command) { 5 | println!("Running: `{:?}`", command); 6 | match command.status() { 7 | Ok(status) => { 8 | if !status.success() { 9 | panic!("Failed: `{:?}` ({})", command, status); 10 | } 11 | } 12 | Err(error) => { 13 | panic!("Failed: `{:?}` ({})", command, error); 14 | } 15 | } 16 | } 17 | 18 | fn feature_enabled(feature: &str) -> bool { 19 | env::var(format!("CARGO_FEATURE_{}", feature.to_uppercase())).is_ok() 20 | } 21 | 22 | /// Add path where pacman (on msys2) install OpenBLAS 23 | /// 24 | /// - `pacman -S mingw-w64-x86_64-openblas` will install 25 | /// - `libopenbla.dll` into `/mingw64/bin` 26 | /// - `libopenbla.a` into `/mingw64/lib` 27 | /// - But we have to specify them using `-L` in **Windows manner** 28 | /// - msys2 `/` is `C:\msys64\` in Windows by default install 29 | /// - It can be convert using `cygpath` command 30 | fn windows_gnu_system() { 31 | let include_path = String::from_utf8( 32 | Command::new("cygpath") 33 | .arg("-w") 34 | .arg("/mingw64/include") 35 | .output() 36 | .expect("Failed to exec cygpath") 37 | .stdout, 38 | ) 39 | .expect("cygpath output includes non UTF-8 string"); 40 | let lib_path = String::from_utf8( 41 | Command::new("cygpath") 42 | .arg("-w") 43 | .arg(if feature_enabled("static") { 44 | "/mingw64/bin" 45 | } else { 46 | "/mingw64/lib" 47 | }) 48 | .output() 49 | .expect("Failed to exec cygpath") 50 | .stdout, 51 | ) 52 | .expect("cygpath output includes non UTF-8 string"); 53 | println!("cargo:rustc-link-search={}", lib_path); 54 | println!("cargo:INCLUDE={}", include_path); 55 | println!("cargo:LIBRARY={}", lib_path); 56 | } 57 | 58 | /// Use vcpkg for msvc "system" feature 59 | fn windows_msvc_system() { 60 | if !feature_enabled("static") { 61 | env::set_var("VCPKGRS_DYNAMIC", "1"); 62 | } 63 | #[cfg(target_env = "msvc")] 64 | vcpkg::find_package("openblas").expect( 65 | "vcpkg failed to find OpenBLAS package , Try to install it using `vcpkg install openblas:$(ARCH)-windows(-static)(-md)`" 66 | ); 67 | if !cfg!(target_env = "msvc") { 68 | unreachable!(); 69 | } 70 | } 71 | 72 | /// Add linker flag (`-L`) to path where brew installs OpenBLAS 73 | fn macos_system() { 74 | fn brew_prefix(target: &str) -> PathBuf { 75 | let out = Command::new("brew") 76 | .arg("--prefix") 77 | .arg(target) 78 | .output() 79 | .expect("brew not installed"); 80 | assert!(out.status.success(), "`brew --prefix` failed"); 81 | let path = String::from_utf8(out.stdout).expect("Non-UTF8 path by `brew --prefix`"); 82 | PathBuf::from(path.trim()) 83 | } 84 | let openblas = brew_prefix("openblas"); 85 | let libomp = brew_prefix("libomp"); 86 | 87 | println!("cargo:rustc-link-search={}/lib", openblas.display()); 88 | println!("cargo:rustc-link-search={}/lib", libomp.display()); 89 | println!("cargo:INCLUDE={}", openblas.join("include").display()); 90 | println!("cargo:LIBRARY={}", openblas.join("lib").display()); 91 | } 92 | 93 | fn main() { 94 | if env::var("DOCS_RS").is_ok() { 95 | return; 96 | } 97 | let link_kind = if feature_enabled("static") { 98 | "static" 99 | } else { 100 | "dylib" 101 | }; 102 | if feature_enabled("system") { 103 | // Use pkg-config to find OpenBLAS 104 | if pkg_config::Config::new() 105 | .statik(feature_enabled("static")) 106 | .probe("openblas") 107 | .is_ok() 108 | { 109 | return; 110 | } 111 | 112 | if cfg!(target_os = "windows") { 113 | if cfg!(target_env = "gnu") { 114 | windows_gnu_system(); 115 | } else if cfg!(target_env = "msvc") { 116 | windows_msvc_system(); 117 | } else { 118 | panic!( 119 | "Unsupported ABI for Windows: {}", 120 | env::var("CARGO_CFG_TARGET_ENV").unwrap() 121 | ); 122 | } 123 | } 124 | if cfg!(target_os = "macos") { 125 | macos_system(); 126 | } 127 | } else { 128 | if cfg!(target_env = "msvc") { 129 | panic!( 130 | "Non-vcpkg builds are not supported on Windows. You must use the 'system' feature." 131 | ) 132 | } 133 | build(); 134 | } 135 | println!("cargo:rustc-link-lib={}=openblas", link_kind); 136 | } 137 | 138 | /// Build OpenBLAS using openblas-build crate 139 | fn build() { 140 | println!("cargo:rerun-if-env-changed=OPENBLAS_TARGET"); 141 | println!("cargo:rerun-if-env-changed=OPENBLAS_CC"); 142 | println!("cargo:rerun-if-env-changed=OPENBLAS_HOSTCC"); 143 | println!("cargo:rerun-if-env-changed=OPENBLAS_FC"); 144 | println!("cargo:rerun-if-env-changed=OPENBLAS_RANLIB"); 145 | let mut cfg = openblas_build::Configure::default(); 146 | if !feature_enabled("cblas") { 147 | cfg.no_cblas = true; 148 | } 149 | if !feature_enabled("lapacke") { 150 | cfg.no_lapacke = true; 151 | } 152 | if feature_enabled("static") { 153 | cfg.no_shared = true; 154 | } else { 155 | cfg.no_static = true; 156 | } 157 | if let Ok(target) = env::var("OPENBLAS_TARGET") { 158 | cfg.target = Some( 159 | target 160 | .parse() 161 | .expect("Unsupported target is specified by $OPENBLAS_TARGET"), 162 | ) 163 | // Do not default to the native target (represented by `cfg.target == None`) 164 | // because most user set `$OPENBLAS_TARGET` explicitly will hope not to use the native target. 165 | } 166 | cfg.compilers.cc = env::var("OPENBLAS_CC").ok(); 167 | cfg.compilers.hostcc = env::var("OPENBLAS_HOSTCC").ok(); 168 | cfg.compilers.fc = env::var("OPENBLAS_FC").ok(); 169 | cfg.compilers.ranlib = env::var("OPENBLAS_RANLIB").ok(); 170 | 171 | let output = if feature_enabled("cache") { 172 | use std::{ 173 | collections::hash_map::DefaultHasher, 174 | hash::{Hash, Hasher}, 175 | }; 176 | // Build OpenBLAS on user's data directory. 177 | // See https://docs.rs/dirs/5.0.1/dirs/fn.data_dir.html 178 | // 179 | // On Linux, `data_dir` returns `$XDG_DATA_HOME` or `$HOME/.local/share`. 180 | // This build script creates a directory based on the hash value of `cfg`, 181 | // i.e. `$XDG_DATA_HOME/openblas_build/[Hash of cfg]`, and build OpenBLAS there. 182 | // 183 | // This build will be shared among several projects using openblas-src crate. 184 | // It makes users not to build OpenBLAS in every `cargo build`. 185 | let mut hasher = DefaultHasher::new(); 186 | cfg.hash(&mut hasher); 187 | 188 | dirs::data_dir() 189 | .expect("Cannot get user's data directory") 190 | .join("openblas_build") 191 | .join(format!("{:x}", hasher.finish())) 192 | } else { 193 | PathBuf::from(env::var("OUT_DIR").unwrap()) 194 | }; 195 | let source = openblas_build::download(&output).unwrap(); 196 | 197 | // If OpenBLAS is build as shared, user of openblas-src will have to find `libopenblas.so` at runtime. 198 | // 199 | // `cargo run` appends the link paths to `LD_LIBRARY_PATH` specified by `cargo:rustc-link-search`, 200 | // and user's crate can find it then. 201 | // 202 | // However, when user try to run it directly like `./target/release/user_crate_exe`, it will say 203 | // "error while loading shared libraries: libopenblas.so: cannot open shared object file: No such file or directory". 204 | // 205 | // Be sure that `cargo:warning` is shown only when openblas-src is build as path dependency... 206 | // https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargowarningmessage 207 | if !feature_enabled("static") { 208 | let ld_name = if cfg!(target_os = "macos") { 209 | "DYLD_LIBRARY_PATH" 210 | } else { 211 | "LD_LIBRARY_PATH" 212 | }; 213 | println!( 214 | "cargo:warning=OpenBLAS is built as a shared library. You need to set {}={}", 215 | ld_name, 216 | source.display() 217 | ); 218 | } 219 | 220 | let build_result = cfg.build(&source); 221 | let make_conf = match build_result { 222 | Ok(c) => c, 223 | Err(openblas_build::error::Error::MissingCrossCompileInfo { info }) => { 224 | panic!( 225 | "Cross compile information is missing and cannot be inferred: OPENBLAS_{}", 226 | info 227 | ); 228 | } 229 | Err(e) => { 230 | panic!("OpenBLAS build failed: {}", e); 231 | } 232 | }; 233 | 234 | println!("cargo:rustc-link-search={}", source.display()); 235 | println!("cargo:INCLUDE={}", source.display()); 236 | println!("cargo:LIBRARY={}", source.display()); 237 | for search_path in &make_conf.c_extra_libs.search_paths { 238 | println!("cargo:rustc-link-search={}", search_path.display()); 239 | } 240 | for lib in &make_conf.c_extra_libs.libs { 241 | println!("cargo:rustc-link-lib={}", lib); 242 | } 243 | for search_path in &make_conf.f_extra_libs.search_paths { 244 | println!("cargo:rustc-link-search={}", search_path.display()); 245 | } 246 | for lib in &make_conf.f_extra_libs.libs { 247 | println!("cargo:rustc-link-lib={}", lib); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /openblas-build/src/build.rs: -------------------------------------------------------------------------------- 1 | //! Execute make of OpenBLAS, and its options 2 | 3 | use crate::{check::*, error::*}; 4 | use std::{env, fs, path::*, process::Command, str::FromStr}; 5 | 6 | /// Interface for 32-bit interger (LP64) and 64-bit integer (ILP64) 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 8 | pub enum Interface { 9 | LP64, 10 | ILP64, 11 | } 12 | 13 | /// CPU list in [TargetList](https://github.com/xianyi/OpenBLAS/blob/v0.3.10/TargetList.txt) 14 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 15 | #[allow(non_camel_case_types)] // to use original identifiers 16 | pub enum Target { 17 | // for DYNNAMIC_ARCH=1 18 | GENERIC, 19 | // X86/X86_64 Intel 20 | P2, 21 | KATMAI, 22 | COPPERMINE, 23 | NORTHWOOD, 24 | PRESCOTT, 25 | BANIAS, 26 | YONAH, 27 | CORE2, 28 | PENRYN, 29 | DUNNINGTON, 30 | NEHALEM, 31 | SANDYBRIDGE, 32 | HASWELL, 33 | SKYLAKEX, 34 | ATOM, 35 | COOPERLAKE, 36 | SAPPHIRERAPIDS, 37 | 38 | // X86/X86_64 AMD 39 | ATHLON, 40 | OPTERON, 41 | OPTERON_SSE3, 42 | BARCELONA, 43 | SHANGHAI, 44 | ISTANBUL, 45 | BOBCAT, 46 | BULLDOZER, 47 | PILEDRIVER, 48 | STEAMROLLER, 49 | EXCAVATOR, 50 | ZEN, 51 | 52 | // X86/X86_64 generic 53 | SSE_GENERIC, 54 | VIAC3, 55 | NANO, 56 | 57 | // Power 58 | POWER4, 59 | POWER5, 60 | POWER6, 61 | POWER7, 62 | POWER8, 63 | POWER9, 64 | POWER10, 65 | PPCG4, 66 | PPC970, 67 | PPC970MP, 68 | PPC440, 69 | PPC440FP2, 70 | CELL, 71 | 72 | // MIPS 73 | P5600, 74 | MIPS1004K, 75 | MIPS24K, 76 | 77 | // MIPS64 78 | MIPS64_GENERIC, 79 | SICORTEX, 80 | LOONGSON3A, 81 | LOONGSON3B, 82 | I6400, 83 | P6600, 84 | I6500, 85 | 86 | // IA64 87 | ITANIUM2, 88 | 89 | // Sparc 90 | SPARC, 91 | SPARCV7, 92 | 93 | // ARM 94 | CORTEXA15, 95 | CORTEXA9, 96 | ARMV7, 97 | ARMV6, 98 | ARMV5, 99 | 100 | // ARM64 101 | ARMV8, 102 | CORTEXA53, 103 | CORTEXA57, 104 | CORTEXA72, 105 | CORTEXA73, 106 | CORTEXA76, 107 | CORTEXA510, 108 | CORTEXA710, 109 | CORTEXX1, 110 | CORTEXX2, 111 | NEOVERSEN1, 112 | NEOVERSEV1, 113 | NEOVERSEN2, 114 | CORTEXA55, 115 | EMAG8180, 116 | FALKOR, 117 | THUNDERX, 118 | THUNDERX2T99, 119 | TSV110, 120 | THUNDERX3T110, 121 | VORTEX, 122 | A64FX, 123 | ARMV8SVE, 124 | FT2000, 125 | 126 | // System Z 127 | ZARCH_GENERIC, 128 | Z13, 129 | Z14, 130 | 131 | // RISC-V 64: 132 | RISCV64_GENERIC, 133 | RISCV64_ZVL128B, 134 | C910V, 135 | x280, 136 | RISCV64_ZVL236B, 137 | 138 | // LOONGARCH64: 139 | LOONGSONGENERIC, 140 | LOONGSON2K1000, 141 | LOONGSON3R5, 142 | LA64_GENERIC, 143 | LA264, 144 | LA464, 145 | 146 | // Elbrus E2000: 147 | E2K, 148 | 149 | // Alpha 150 | EV4, 151 | EV5, 152 | EV6, 153 | 154 | // CSKY 155 | CSKY, 156 | CK860FV, 157 | } 158 | 159 | impl FromStr for Target { 160 | type Err = Error; 161 | 162 | fn from_str(s: &str) -> Result { 163 | let target = match s.to_ascii_lowercase().as_str() { 164 | "generic" => Self::GENERIC, 165 | // X86/X86_64 Intel 166 | "p2" => Self::P2, 167 | "katamai" => Self::KATMAI, 168 | "coppermine" => Self::COPPERMINE, 169 | "northwood" => Self::NORTHWOOD, 170 | "prescott" => Self::PRESCOTT, 171 | "banias" => Self::BANIAS, 172 | "yonah" => Self::YONAH, 173 | "core2" => Self::CORE2, 174 | "penryn" => Self::PENRYN, 175 | "dunnington" => Self::DUNNINGTON, 176 | "nehalem" => Self::NEHALEM, 177 | "sandybridge" => Self::SANDYBRIDGE, 178 | "haswell" => Self::HASWELL, 179 | "skylakex" => Self::SKYLAKEX, 180 | "atom" => Self::ATOM, 181 | "cooperlake" => Self::COOPERLAKE, 182 | "sapphirerapids" => Self::SAPPHIRERAPIDS, 183 | 184 | // X86/X86_64 AMD 185 | "athlon" => Self::ATHLON, 186 | "opteron" => Self::OPTERON, 187 | "opteron_sse3" => Self::OPTERON_SSE3, 188 | "barcelona" => Self::BARCELONA, 189 | "shanghai" => Self::SHANGHAI, 190 | "istanbul" => Self::ISTANBUL, 191 | "bobcat" => Self::BOBCAT, 192 | "bulldozer" => Self::BULLDOZER, 193 | "piledriver" => Self::PILEDRIVER, 194 | "steamroller" => Self::STEAMROLLER, 195 | "excavator" => Self::EXCAVATOR, 196 | "zen" => Self::ZEN, 197 | 198 | // X86/X86_64 generic 199 | "sse_generic" => Self::SSE_GENERIC, 200 | "viac3" => Self::VIAC3, 201 | "nano" => Self::NANO, 202 | 203 | // Power 204 | "power4" => Self::POWER4, 205 | "power5" => Self::POWER5, 206 | "power6" => Self::POWER6, 207 | "power7" => Self::POWER7, 208 | "power8" => Self::POWER8, 209 | "power9" => Self::POWER9, 210 | "power10" => Self::POWER10, 211 | "ppcg4" => Self::PPCG4, 212 | "ppc970" => Self::PPC970, 213 | "ppc970mp" => Self::PPC970MP, 214 | "ppc440" => Self::PPC440, 215 | "ppc440fp2" => Self::PPC440FP2, 216 | "cell" => Self::CELL, 217 | 218 | // MIPS 219 | "p5600" => Self::P5600, 220 | "mips1004k" => Self::MIPS1004K, 221 | "mips24k" => Self::MIPS24K, 222 | 223 | // MIPS64 224 | "mips64_generic" => Self::MIPS64_GENERIC, 225 | "sicortex" => Self::SICORTEX, 226 | "loongson3a" => Self::LOONGSON3A, 227 | "loongson3b" => Self::LOONGSON3B, 228 | "i6400" => Self::I6400, 229 | "p6600" => Self::P6600, 230 | "i6500" => Self::I6500, 231 | 232 | // IA64 233 | "itanium2" => Self::ITANIUM2, 234 | 235 | // Sparc 236 | "sparc" => Self::SPARC, 237 | "sparcv7" => Self::SPARCV7, 238 | 239 | // ARM 240 | "cortexa15" => Self::CORTEXA15, 241 | "cortexa9" => Self::CORTEXA9, 242 | "armv7" => Self::ARMV7, 243 | "armv6" => Self::ARMV6, 244 | "armv5" => Self::ARMV5, 245 | 246 | // ARM64 247 | "armv8" => Self::ARMV8, 248 | "cortexa53" => Self::CORTEXA53, 249 | "cortexa57" => Self::CORTEXA57, 250 | "cortexa72" => Self::CORTEXA72, 251 | "cortexa73" => Self::CORTEXA73, 252 | "cortexa76" => Self::CORTEXA76, 253 | "cortexa510" => Self::CORTEXA510, 254 | "cortexa710" => Self::CORTEXA710, 255 | "cortexx1" => Self::CORTEXX1, 256 | "cortexx2" => Self::CORTEXX2, 257 | "neoversen1" => Self::NEOVERSEN1, 258 | "neoversev1" => Self::NEOVERSEV1, 259 | "neoversen2" => Self::NEOVERSEN2, 260 | "cortexa55" => Self::CORTEXA55, 261 | "emag8180" => Self::EMAG8180, 262 | "falkor" => Self::FALKOR, 263 | "thunderx" => Self::THUNDERX, 264 | "thunderx2t99" => Self::THUNDERX2T99, 265 | "tsv110" => Self::TSV110, 266 | "thunderx3t110" => Self::THUNDERX3T110, 267 | "vortex" => Self::VORTEX, 268 | "a64fx" => Self::A64FX, 269 | "armv8sve" => Self::ARMV8SVE, 270 | "ft2000" => Self::FT2000, 271 | 272 | // System Z 273 | "zarch_generic" => Self::ZARCH_GENERIC, 274 | "z13" => Self::Z13, 275 | "z14" => Self::Z14, 276 | 277 | // RISC-V 64: 278 | "riscv64_generic" => Self::RISCV64_GENERIC, 279 | "riscv64_zvl128b" => Self::RISCV64_ZVL128B, 280 | "c910v" => Self::C910V, 281 | "x280" => Self::x280, 282 | "riscv64_zvl236b" => Self::RISCV64_ZVL236B, 283 | 284 | // LOONGARCH64: 285 | "loongsongeneric" => Self::LOONGSONGENERIC, 286 | "longson2k1000" => Self::LOONGSON2K1000, 287 | "longson3r5" => Self::LOONGSON3R5, 288 | "la64_generic" => Self::LA64_GENERIC, 289 | "la264" => Self::LA264, 290 | "la464" => Self::LA464, 291 | 292 | // Elbrus E2000: 293 | "e2k" => Self::E2K, 294 | 295 | // Alpha 296 | "ev4" => Self::EV4, 297 | "ev5" => Self::EV5, 298 | "ev6" => Self::EV6, 299 | 300 | // CSKY 301 | "csky" => Self::CSKY, 302 | "ck860fv" => Self::CK860FV, 303 | 304 | _ => { 305 | return Err(Error::UnsupportedTarget { 306 | target: s.to_string(), 307 | }) 308 | } 309 | }; 310 | Ok(target) 311 | } 312 | } 313 | 314 | impl Target { 315 | fn get_generic_target() -> Option { 316 | let target = env::var("TARGET").unwrap(); 317 | let target_arch = target.split('-').nth(0).unwrap(); 318 | match target_arch { 319 | "aarch64" => Some(Target::ARMV8), 320 | "arm" => Some(Target::ARMV6), 321 | "armv5te" => Some(Target::ARMV5), 322 | "armv6" => Some(Target::ARMV6), 323 | "armv7" => Some(Target::ARMV7), 324 | "loongarch64" => Some(Target::LOONGSONGENERIC), 325 | "mips64" => Some(Target::MIPS64_GENERIC), 326 | "mips64el" => Some(Target::MIPS64_GENERIC), 327 | "riscv64" => Some(Target::RISCV64_GENERIC), 328 | "csky" => Some(Target::CK860FV), 329 | "sparc" => Some(Target::SPARCV7), 330 | //TODO: add more generic targets 331 | _ => None, 332 | } 333 | } 334 | } 335 | 336 | #[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] 337 | pub struct Compilers { 338 | pub cc: Option, 339 | pub fc: Option, 340 | pub hostcc: Option, 341 | pub ranlib: Option, 342 | } 343 | 344 | /// make option generator 345 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 346 | pub struct Configure { 347 | pub no_static: bool, 348 | pub no_shared: bool, 349 | pub no_cblas: bool, 350 | pub no_lapack: bool, 351 | pub no_lapacke: bool, 352 | pub use_thread: bool, 353 | pub use_openmp: bool, 354 | pub dynamic_arch: bool, 355 | pub interface: Interface, 356 | pub target: Option, 357 | pub compilers: Compilers, 358 | } 359 | 360 | impl Default for Configure { 361 | fn default() -> Self { 362 | Configure { 363 | no_static: false, 364 | no_shared: false, 365 | no_cblas: false, 366 | no_lapack: false, 367 | no_lapacke: false, 368 | use_thread: false, 369 | use_openmp: false, 370 | dynamic_arch: false, 371 | interface: Interface::LP64, 372 | target: None, 373 | compilers: Compilers::default(), 374 | } 375 | } 376 | } 377 | 378 | impl Configure { 379 | fn make_args(&self) -> Result, Error> { 380 | // check if it is cross-compilation 381 | let build_target = env::var("TARGET").unwrap_or_default(); 382 | let build_host = env::var("HOST").unwrap_or_default(); 383 | let is_cross_compile = build_target != build_host; 384 | 385 | let mut args = Vec::new(); 386 | if self.no_static { 387 | args.push("NO_STATIC=1".into()); 388 | } 389 | if self.no_shared { 390 | args.push("NO_SHARED=1".into()); 391 | } 392 | if self.no_cblas { 393 | args.push("NO_CBLAS=1".into()); 394 | } 395 | if self.no_lapack { 396 | args.push("NO_LAPACK=1".into()); 397 | } 398 | if self.no_lapacke { 399 | args.push("NO_LAPACKE=1".into()); 400 | } 401 | if self.use_thread { 402 | args.push("USE_THREAD=1".into()); 403 | } 404 | if self.use_openmp { 405 | args.push("USE_OPENMP=1".into()); 406 | } 407 | if matches!(self.interface, Interface::ILP64) { 408 | args.push("INTERFACE64=1".into()); 409 | } 410 | if let Some(target) = self.target.as_ref() { 411 | args.push(format!("TARGET={:?}", target)); 412 | } else if is_cross_compile { 413 | if let Some(target) = Target::get_generic_target() { 414 | args.push(format!("TARGET={:?}", target)); 415 | } else { 416 | return Err(Error::MissingCrossCompileInfo { 417 | info: "TARGET".to_string(), 418 | }); 419 | } 420 | } 421 | 422 | let mut cc_compiler = self.compilers.cc.clone(); 423 | if let Some(cc) = self.compilers.cc.as_ref() { 424 | args.push(format!("CC={}", cc)); 425 | } else if is_cross_compile { 426 | let compiler = cc::Build::new().get_compiler(); 427 | let compiler_path = compiler.path().to_str(); 428 | if let Some(cc) = compiler_path { 429 | args.push(format!("CC={}", cc)); 430 | cc_compiler = Some(cc.to_string()); 431 | } else { 432 | return Err(Error::MissingCrossCompileInfo { 433 | info: "CC".to_string(), 434 | }); 435 | } 436 | } 437 | if let Some(fc) = self.compilers.fc.as_ref() { 438 | args.push(format!("FC={}", fc)) 439 | } else if is_cross_compile { 440 | let mut fortran = false; 441 | if let Some(cc) = cc_compiler { 442 | let fc = cc 443 | .replace("gcc", "gfortran") 444 | .replace("clang", "flang") 445 | .replace("icc", "ifort"); 446 | 447 | if Command::new(&fc).arg("--version").check_call().is_ok() { 448 | args.push(format!("FC={}", fc)); 449 | fortran = true; 450 | } 451 | } 452 | if !fortran { 453 | println!("cargo:warning=OpenBLAS: Detecting fortran compiler failed. Can only compile BLAS and f2c-converted LAPACK."); 454 | args.push("NOFORTRAN=1".into()); 455 | } 456 | } 457 | if let Some(hostcc) = self.compilers.hostcc.as_ref() { 458 | args.push(format!("HOSTCC={}", hostcc)) 459 | } else if is_cross_compile { 460 | let compiler = cc::Build::new().target(build_host.as_str()).get_compiler(); 461 | let compiler_path = compiler.path().to_str(); 462 | if let Some(hostcc) = compiler_path { 463 | args.push(format!("HOSTCC={}", hostcc)); 464 | } else { 465 | return Err(Error::MissingCrossCompileInfo { 466 | info: "HOSTCC".to_string(), 467 | }); 468 | } 469 | } 470 | if let Some(ranlib) = self.compilers.ranlib.as_ref() { 471 | args.push(format!("RANLIB={}", ranlib)) 472 | } 473 | Ok(args) 474 | } 475 | 476 | /// Build OpenBLAS 477 | /// 478 | /// Libraries are created directly under `out_dir` e.g. `out_dir/libopenblas.a` 479 | /// 480 | /// Error 481 | /// ----- 482 | /// - Build deliverables are invalid same as [inspect]. 483 | /// This means that the system environment is not appropriate to execute `make`, 484 | /// e.g. LAPACK is required but there is no Fortran compiler. 485 | /// 486 | pub fn build>(self, openblas_root: P) -> Result { 487 | let root = openblas_root.as_ref(); 488 | // Do not build if libraries and Makefile.conf already exist and are valid 489 | if let Ok(make_conf) = MakeConf::new(root.join("Makefile.conf")) { 490 | return Ok(make_conf); 491 | } 492 | 493 | // check if cross compile is needed 494 | // let build_target = env::var("TARGET").unwrap_or_default(); 495 | // let build_host = env::var("HOST").unwrap_or_default(); 496 | // let is_cross_compile = build_target != build_host; 497 | // if is_cross_compile && (self.compilers.cc.is_none() || self.compilers.hostcc.is_none()) { 498 | // return Err(Error::MissingCrossCompileInfo); 499 | // } 500 | 501 | // Run `make` as an subprocess 502 | // 503 | // - This will automatically run in parallel without `-j` flag 504 | // - The `make` of OpenBLAS outputs 30k lines, 505 | // which will be redirected into `out.log` and `err.log`. 506 | // - cargo sets `TARGET` environment variable as target triple (e.g. x86_64-unknown-linux-gnu) 507 | // while binding build.rs, but `make` read it as CPU target specification. 508 | // 509 | let out = fs::File::create(root.join("out.log")).expect("Cannot create log file"); 510 | let err = fs::File::create(root.join("err.log")).expect("Cannot create log file"); 511 | match Command::new("make") 512 | .current_dir(root) 513 | .stdout(out) 514 | .stderr(err) 515 | .args(self.make_args()?) 516 | .args(["all"]) 517 | .env_remove("TARGET") 518 | .check_call() 519 | { 520 | Ok(_) => {} 521 | Err(err @ Error::NonZeroExitStatus { .. }) => { 522 | eprintln!( 523 | "{}", 524 | fs::read_to_string(root.join("err.log")).expect("Cannot read log file") 525 | ); 526 | return Err(err); 527 | } 528 | Err(e) => { 529 | return Err(e); 530 | } 531 | } 532 | 533 | let make_conf = MakeConf::new(root.join("Makefile.conf"))?; 534 | if make_conf.no_fortran { 535 | println!("cargo:warning=OpenBLAS: Detecting fortran compiler failed. Only BLAS and f2c-converted LAPACK are compiled."); 536 | } 537 | Ok(make_conf) 538 | } 539 | } 540 | 541 | #[cfg(test)] 542 | mod tests { 543 | use super::*; 544 | 545 | #[test] 546 | fn target_from_str() { 547 | assert_eq!(Target::from_str("p2").unwrap(), Target::P2); 548 | assert!(matches!( 549 | Target::from_str("p3").unwrap_err(), 550 | crate::error::Error::UnsupportedTarget { .. } 551 | )); 552 | } 553 | 554 | fn get_openblas_source>(out_dir: P) -> PathBuf { 555 | let openblas_src_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../openblas-src/"); 556 | let source = crate::download(&openblas_src_root).unwrap(); 557 | // copy files to the target directory 558 | let out_dir = out_dir.as_ref(); 559 | fs::create_dir_all(out_dir).unwrap(); 560 | for entry in walkdir::WalkDir::new(&source) { 561 | let entry = entry.unwrap(); 562 | let src = entry.path(); 563 | let dest = out_dir.join(src.strip_prefix(&source).unwrap()); 564 | if entry.file_type().is_dir() { 565 | fs::create_dir_all(&dest).unwrap(); 566 | } else { 567 | fs::copy(src, dest).unwrap(); 568 | } 569 | } 570 | out_dir.to_path_buf() 571 | } 572 | 573 | #[ignore] 574 | #[test] 575 | fn build_default() { 576 | let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 577 | let out_dir = root.join("test_build/build_default"); 578 | let opt = Configure::default(); 579 | let _ = opt.build(get_openblas_source(&out_dir)).unwrap(); 580 | } 581 | 582 | #[ignore] 583 | #[test] 584 | fn build_no_shared() { 585 | let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 586 | let out_dir = root.join("test_build/build_no_shared"); 587 | let mut opt = Configure::default(); 588 | opt.no_shared = true; 589 | opt.build(get_openblas_source(&out_dir)).unwrap(); 590 | let _ = LibInspect::new(out_dir.join("libopenblas.a")).unwrap(); 591 | } 592 | 593 | #[ignore] 594 | #[test] 595 | fn build_no_lapacke() { 596 | let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 597 | let out_dir = root.join("test_build/build_no_lapacke"); 598 | let mut opt = Configure::default(); 599 | opt.no_lapacke = true; 600 | let _ = opt.build(get_openblas_source(&out_dir)).unwrap(); 601 | let lib_name = if cfg!(target_os = "macos") { 602 | "libopenblas.dylib" 603 | } else { 604 | "libopenblas.so" 605 | }; 606 | let lib_inspect = LibInspect::new(out_dir.join(lib_name)).unwrap(); 607 | 608 | assert!(lib_inspect.has_lapack()); 609 | assert!(!lib_inspect.has_lapacke()); 610 | } 611 | 612 | #[ignore] 613 | #[test] 614 | fn build_no_cblas() { 615 | let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 616 | let out_dir = root.join("test_build/build_no_cblas"); 617 | let mut opt = Configure::default(); 618 | opt.no_lapacke = true; 619 | let _ = opt.build(get_openblas_source(&out_dir)).unwrap(); 620 | let lib_name = if cfg!(target_os = "macos") { 621 | "libopenblas.dylib" 622 | } else { 623 | "libopenblas.so" 624 | }; 625 | let lib_inspect = LibInspect::new(out_dir.join(lib_name)).unwrap(); 626 | 627 | assert!(!lib_inspect.has_cblas()); 628 | } 629 | 630 | #[ignore] 631 | #[test] 632 | fn build_openmp() { 633 | let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 634 | let out_dir = root.join("test_build/build_openmp"); 635 | let mut opt = Configure::default(); 636 | opt.use_openmp = true; 637 | let _ = opt.build(get_openblas_source(&out_dir)).unwrap(); 638 | let lib_name = if cfg!(target_os = "macos") { 639 | "libopenblas.dylib" 640 | } else { 641 | "libopenblas.so" 642 | }; 643 | let lib_inspect = LibInspect::new(out_dir.join(lib_name)).unwrap(); 644 | assert!(lib_inspect.has_lib("gomp")); 645 | } 646 | } 647 | --------------------------------------------------------------------------------