rstsr_lapack_ffi/lapack/
mod.rs

1#![allow(non_snake_case)]
2#![allow(non_camel_case_types)]
3#![allow(clippy::missing_safety_doc)]
4#![allow(clippy::type_complexity)]
5#![allow(clippy::too_many_arguments)]
6
7pub const MOD_NAME: &str = module_path!();
8pub const LIB_NAME: &str = "LAPACK"; // for code, e.g. "MKL"
9pub const LIB_NAME_SHOW: &str = "LAPACK"; // for display, e.g. "oneMKL"
10pub const LIB_NAME_LINK: &str = "lapack"; // for linking, e.g. "mkl_rt"
11
12#[cfg(feature = "dynamic_loading")]
13mod dynamic_loading_specific {
14    use super::*;
15    use libloading::Library;
16    use std::fmt::Debug;
17    use std::sync::OnceLock;
18
19    fn get_lib_candidates() -> Vec<String> {
20        use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
21        let mut candidates = vec![];
22
23        // user defined candidates
24        for paths in [format!("RSTSR_DYLOAD_{LIB_NAME}").as_str(), "RSTSR_DYLOAD"] {
25            if let Ok(path) = std::env::var(paths) {
26                candidates.extend(path.split(":").map(|s| s.to_string()).collect::<Vec<_>>());
27            }
28        }
29
30        // other candidates configuration
31        let int_type = if cfg!(feature = "ilp64") { "64" } else { "32" };
32        let int_name = if cfg!(feature = "ilp64") { "ilp64" } else { "lp64" };
33
34        candidates.extend(vec![
35            format!("{DLL_PREFIX}lapack{int_type}{DLL_SUFFIX}"),
36            format!("{DLL_PREFIX}lapack_{int_type}{DLL_SUFFIX}"),
37            format!("{DLL_PREFIX}lapack-{int_type}{DLL_SUFFIX}"),
38            format!("{DLL_PREFIX}lapack_{int_name}{DLL_SUFFIX}"),
39            format!("{DLL_PREFIX}lapack-{int_name}{DLL_SUFFIX}"),
40            format!("{DLL_PREFIX}lapack{DLL_SUFFIX}"),
41            format!("{DLL_PREFIX}lapacke{int_type}{DLL_SUFFIX}"),
42            format!("{DLL_PREFIX}lapacke_{int_type}{DLL_SUFFIX}"),
43            format!("{DLL_PREFIX}lapacke-{int_type}{DLL_SUFFIX}"),
44            format!("{DLL_PREFIX}lapacke_{int_name}{DLL_SUFFIX}"),
45            format!("{DLL_PREFIX}lapacke-{int_name}{DLL_SUFFIX}"),
46            format!("{DLL_PREFIX}lapacke{DLL_SUFFIX}"),
47        ]);
48        candidates
49    }
50
51    fn check_lib_loaded(lib: &DyLoadLib) -> bool {
52        lib.dsyevd_.is_some()
53    }
54
55    fn panic_no_lib_found<S: Debug>(candidates: &[S], err_msg: &str) -> ! {
56        panic!(
57            r#"
58This happens in module `{MOD_NAME}`.
59Unable to dynamically load the {LIB_NAME_SHOW} (`{LIB_NAME_LINK}`) shared library.
60Candidates: {candidates:#?}
61
62Please check
63- if dynamic-loading is not desired, please disable the `dynamic_loading` feature in your `Cargo.toml` (by something like --no-default-features).
64- if you want to provide custom {LIB_NAME_SHOW} library, use environment variable `RSTSR_DYLOAD_{LIB_NAME}` or `RSTSR_DYLOAD` to specify the path to the library.
65- if `lib{LIB_NAME_LINK}.so` (linux) or `lib{LIB_NAME_LINK}.dylib` (macOS) or `lib{LIB_NAME_LINK}.dll` (Windows) is installed on your system.
66- if `LD_LIBRARY_PATH` (linux) or `DYLD_LIBRARY_PATH` (macOS) or `PATH` (Windows) environment variable is set correctly (any path that's visible to linker).
67- this crate does not use things like `LD_PRELOAD` or `DYLD_INSERT_LIBRARIES` to load the library.
68- this crate does not support static linking of libraries when dynamic-loading.
69
70Error message(s):
71{err_msg}
72"#
73        )
74    }
75
76    fn panic_condition_not_met<S: Debug>(candidates: &[S]) -> ! {
77        panic!(
78            r#"
79This happens in module `{MOD_NAME}`.
80Unable to dynamically load the {LIB_NAME_SHOW} (`{LIB_NAME_LINK}`) shared library, due to condition unfulfilled.
81Condition: `dsyevd_` not found.
82Found libraries: {candidates:#?}
83
84Please check
85- if dynamic-loading is not desired, please disable the `dynamic_loading` feature in your `Cargo.toml` (by something like --no-default-features).
86- if you want to provide custom {LIB_NAME_SHOW} library, use environment variable `RSTSR_DYLOAD_{LIB_NAME}` or `RSTSR_DYLOAD` to specify the path to the library.
87- sequence of libraries matters: `RSTSR_DYLOAD_{LIB_NAME}` will be tried first, then `RSTSR_DYLOAD`, then system dynamic library search paths.
88"#
89        )
90    }
91
92    pub unsafe fn dyload_lib() -> &'static DyLoadLib {
93        static LIB: OnceLock<DyLoadLib> = OnceLock::new();
94
95        LIB.get_or_init(|| {
96            let candidates = get_lib_candidates();
97            let (mut libraries, mut libraries_path) = (vec![], vec![]);
98            let mut err_msg = String::new();
99            for candidate in &candidates {
100                match Library::new(candidate) {
101                    Ok(l) => {
102                        libraries.push(l);
103                        libraries_path.push(candidate.to_string());
104                    },
105                    Err(e) => err_msg.push_str(&format!("Failed to load `{candidate}`: {e}\n")),
106                }
107            }
108            let lib = DyLoadLib::new(libraries, libraries_path);
109            if lib.__libraries.is_empty() {
110                panic_no_lib_found(&candidates, &err_msg);
111            }
112            if !check_lib_loaded(&lib) {
113                panic_condition_not_met(&lib.__libraries_path);
114            }
115            lib
116        })
117    }
118}
119
120#[cfg(feature = "dynamic_loading")]
121pub use dynamic_loading_specific::*;
122
123/* #region general configuration */
124
125pub(crate) mod ffi_base;
126pub use ffi_base::*;
127
128#[cfg(not(feature = "dynamic_loading"))]
129pub(crate) mod ffi_extern;
130#[cfg(not(feature = "dynamic_loading"))]
131pub use ffi_extern::*;
132
133#[cfg(feature = "dynamic_loading")]
134pub(crate) mod dyload_compatible;
135#[cfg(feature = "dynamic_loading")]
136pub(crate) mod dyload_initializer;
137#[cfg(feature = "dynamic_loading")]
138pub(crate) mod dyload_struct;
139
140#[cfg(feature = "dynamic_loading")]
141pub use dyload_compatible::*;
142#[cfg(feature = "dynamic_loading")]
143pub use dyload_struct::*;
144
145/* #endregion */
146
147#[test]
148fn playground() {
149    // test libraries loaded
150    let time = std::time::Instant::now();
151    let l = unsafe { dyload_lib() };
152    println!("Time taken to load LAPACK library: {:?}", time.elapsed());
153    println!("Loaded LAPACK library: {:?}", l.__libraries);
154    println!("Loaded LAPACK library: {:?}", l.dsyevd_);
155
156    // test if the library is loaded correctly
157    let mut a: Vec<f64> = vec![2.0, 1.0, 3.0, 4.0];
158    let uplo = 'L';
159    let n = 2;
160    let lda = 2;
161    let mut info = 0;
162    unsafe {
163        dpotrf_(&uplo as *const _ as *const _, &n, a.as_mut_ptr(), &lda, &mut info);
164    }
165    println!("{a:?}");
166    assert_eq!(info, 0);
167}