#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::type_complexity)]
#![allow(clippy::too_many_arguments)]
pub const MOD_NAME: &str = module_path!();
pub const LIB_NAME: &str = "DFTD4";
pub const LIB_NAME_SHOW: &str = "dftd4";
pub const LIB_NAME_LINK: &str = "dftd4";
#[cfg(feature = "dynamic_loading")]
mod dynamic_loading_specific {
use super::*;
use libloading::Library;
use std::fmt::Debug;
use std::sync::OnceLock;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
static PYTHON_LIB_PATH: OnceLock<Vec<String>> = OnceLock::new();
fn detect_python_lib_paths() -> Vec<String> {
PYTHON_LIB_PATH
.get_or_init(|| {
let mut lib_paths = vec![];
if let Ok(python_path) = std::env::var("DFTD4_PYTHON_PATH") {
if let Some(lib_path) = extract_lib_from_python_bin(&python_path) {
lib_paths.push(lib_path);
}
}
if let Ok(conda_prefix) = std::env::var("CONDA_PREFIX") {
let conda_lib_path = format!("{conda_prefix}/lib");
if std::path::Path::new(&conda_lib_path).exists() {
lib_paths.push(conda_lib_path);
}
}
if let Ok(paths) = std::env::var("PATH") {
for path in paths.split(":") {
let python_bin = format!("{path}/python");
if std::path::Path::new(&python_bin).exists() {
if let Some(lib_path) = extract_lib_from_python_bin(&python_bin) {
lib_paths.push(lib_path);
}
}
}
for path in paths.split(":") {
let python_bin = format!("{path}/python3");
if std::path::Path::new(&python_bin).exists() {
if let Some(lib_path) = extract_lib_from_python_bin(&python_bin) {
lib_paths.push(lib_path);
}
}
}
}
lib_paths
})
.clone()
}
fn extract_lib_from_python_bin(python_bin: &str) -> Option<String> {
let bin_path = std::path::Path::new(python_bin);
if let Some(parent) = bin_path.parent() {
if let Some(base) = parent.parent() {
let lib_path = base.join("lib");
if lib_path.exists() {
return Some(lib_path.to_string_lossy().to_string());
}
}
}
None
}
fn get_lib_candidates() -> Vec<String> {
let mut candidates = vec![];
for env_var in [format!("DFTD4_DYLOAD_{LIB_NAME}").as_str(), "DFTD4_DYLOAD"] {
if let Ok(path) = std::env::var(env_var) {
candidates.extend(path.split(":").map(|s| s.to_string()));
}
}
for env_var in [
"LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "DYLD_FALLBACK_LIBRARY_PATH", "PATH", ] {
if let Ok(paths) = std::env::var(env_var) {
for path in paths.split(":") {
candidates.push(format!("{path}/{DLL_PREFIX}{LIB_NAME_LINK}{DLL_SUFFIX}"));
}
}
}
for lib_path in detect_python_lib_paths() {
candidates.push(format!("{lib_path}/{DLL_PREFIX}{LIB_NAME_LINK}{DLL_SUFFIX}"));
}
candidates.extend(vec![
format!("{DLL_PREFIX}{LIB_NAME_LINK}{DLL_SUFFIX}"),
format!("{DLL_PREFIX}dftd4{DLL_SUFFIX}"),
format!("/usr/lib/{DLL_PREFIX}{LIB_NAME_LINK}{DLL_SUFFIX}"),
format!("/usr/local/lib/{DLL_PREFIX}{LIB_NAME_LINK}{DLL_SUFFIX}"),
format!("/lib/{DLL_PREFIX}{LIB_NAME_LINK}{DLL_SUFFIX}"),
]);
candidates
}
fn check_lib_loaded(lib: &DyLoadLib) -> bool {
lib.dftd4_get_version.is_some()
}
fn panic_no_lib_found<S: Debug>(candidates: &[S], err_msg: &str) -> ! {
panic!(
r#"
This happens in module `{MOD_NAME}`.
Unable to dynamically load the {LIB_NAME_SHOW} (`{LIB_NAME_LINK}`) shared library.
Candidates: {candidates:#?}
Please check:
- If dynamic-loading is not desired, disable the `dynamic_loading` feature in Cargo.toml.
- Use environment variable `DFTD4_DYLOAD_{LIB_NAME}` or `DFTD4_DYLOAD` to specify the library path.
- If `lib{LIB_NAME_LINK}.so` is installed on your system.
- If `LD_LIBRARY_PATH` is set correctly.
- Python interpreter path discovery: if Python is at `/path/bin/python`,
the library is expected at `/path/lib/libdftd4.so`.
Error message(s):
{err_msg}
"#
)
}
fn panic_condition_not_met<S: Debug>(candidates: &[S]) -> ! {
panic!(
r#"
This happens in module `{MOD_NAME}`.
Library loaded but condition not met: `dftd4_get_version` not found.
Found libraries: {candidates:#?}
Please check that the loaded library is a valid dftd4 library.
"#
)
}
pub unsafe fn dyload_lib() -> &'static DyLoadLib {
static LIB: OnceLock<DyLoadLib> = OnceLock::new();
LIB.get_or_init(|| {
let candidates = get_lib_candidates();
let (mut libraries, mut libraries_path) = (vec![], vec![]);
let mut err_msg = String::new();
for candidate in &candidates {
match Library::new(candidate) {
Ok(l) => {
libraries.push(l);
libraries_path.push(candidate.to_string());
},
Err(e) => err_msg.push_str(&format!(
"Failed to load `{candidate}`: {e}
"
)),
}
}
let lib = DyLoadLib::new(libraries, libraries_path);
if lib.__libraries.is_empty() {
panic_no_lib_found(&candidates, &err_msg);
}
if !check_lib_loaded(&lib) {
panic_condition_not_met(&lib.__libraries_path);
}
lib
})
}
}
#[cfg(feature = "dynamic_loading")]
pub use dynamic_loading_specific::*;
pub(crate) mod ffi_base;
pub use ffi_base::*;
#[cfg(feature = "dynamic_loading")]
pub(crate) mod dyload_compatible;
#[cfg(feature = "dynamic_loading")]
pub(crate) mod dyload_initializer;
#[cfg(feature = "dynamic_loading")]
pub(crate) mod dyload_struct;
#[cfg(feature = "dynamic_loading")]
pub use dyload_compatible::*;
#[cfg(feature = "dynamic_loading")]
pub use dyload_struct::*;