From d2bd2f11c4f0e9a0f9ac94fd07cd1fc953bc1953 Mon Sep 17 00:00:00 2001 From: Shirong_Wang Date: Sat, 13 Jun 2026 17:12:38 +0800 Subject: [PATCH 1/2] fix for windows --- dftd3/src/ffi_dynamic/mod.rs | 57 +++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/dftd3/src/ffi_dynamic/mod.rs b/dftd3/src/ffi_dynamic/mod.rs index c69eed1..b6d59ed 100644 --- a/dftd3/src/ffi_dynamic/mod.rs +++ b/dftd3/src/ffi_dynamic/mod.rs @@ -10,11 +10,13 @@ //! order: //! //! 1. User-defined candidates via environment variables `DFTD3_DYLOAD`. +//! Multiple paths can be separated by `:` (Unix/macOS) or `;` (Windows). //! 2. LD_LIBRARY_PATH style discovery via environment variables //! `LD_LIBRARY_PATH` (Linux), `DYLD_LIBRARY_PATH` and //! `DYLD_FALLBACK_LIBRARY_PATH` (macOS), `PATH` (Windows). Note we are not //! distinguishing different operating systems, so all these environment -//! variables will be checked on all platforms. +//! variables will be checked on all platforms. Path lists are split by `:` +//! on Unix/macOS and `;` on Windows. //! 3. Python interpreter path discovery: For each python interpreter found, the //! library is expected to be at the `lib` directory of the python //! installation. For example, if python is at `/path/bin/python`, the @@ -22,11 +24,14 @@ //! - The python interpreter path of `DFTD3_PYTHON_PATH` environment //! variable, if set. //! - The conda prefix path of `CONDA_PREFIX` environment variable, if set. +//! On Windows also checks `{CONDA_PREFIX}/Library/bin` and +//! `{CONDA_PREFIX}/Library/lib`. //! - The python interpreter path in `PATH` environment variable, if exists. //! Will first check `python`, then `python3`. //! 4. Standard system candidates, such as `lib{LIB_NAME_LINK}.so` in some //! common library directories such as `/usr/lib`, `/usr/local/lib`, and -//! `/lib`. +//! `/lib`. These are Unix-specific and silently skipped when absent on +//! Windows. //! //! For API developer, if you want to check the library `libs-dftd3.so` loading //! sequence, you can try the following code: @@ -44,6 +49,11 @@ pub const MOD_NAME: &str = module_path!(); pub const LIB_NAME: &str = "DFTD3"; pub const LIB_NAME_SHOW: &str = "s-dftd3"; +#[cfg(windows)] +/// On Windows (MinGW), the library includes a "-1" SO version suffix. +/// The "-1" may need updating when upstream bumps ABI. +pub const LIB_NAME_LINK: &str = "s-dftd3-1"; +#[cfg(not(windows))] pub const LIB_NAME_LINK: &str = "s-dftd3"; #[cfg(feature = "dynamic_loading")] @@ -53,8 +63,21 @@ mod dynamic_loading_specific { use std::fmt::Debug; use std::sync::OnceLock; + #[cfg(not(windows))] use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; + #[cfg(windows)] + use std::env::consts::DLL_SUFFIX; + #[cfg(windows)] + /// MinGW convention uses "lib" prefix, but std::env::consts::DLL_PREFIX + /// returns "" on all Windows targets regardless of toolchain. + const DLL_PREFIX: &str = "lib"; + + #[cfg(windows)] + const PATH_LIST_SEPARATOR: char = ';'; + #[cfg(not(windows))] + const PATH_LIST_SEPARATOR: char = ':'; + /// Detect Python interpreter path and return the corresponding lib /// directory. Uses OnceLock pattern for lazy initialization. static PYTHON_LIB_PATH: OnceLock> = OnceLock::new(); @@ -71,18 +94,29 @@ mod dynamic_loading_specific { } } - // 2. Check conda prefix exists + // 2. Check conda prefix exists (on Windows also checks Library/{bin,lib}) 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); } + #[cfg(windows)] + { + let conda_library_bin = format!("{conda_prefix}/Library/bin"); + if std::path::Path::new(&conda_library_bin).exists() { + lib_paths.push(conda_library_bin); + } + let conda_library_lib = format!("{conda_prefix}/Library/lib"); + if std::path::Path::new(&conda_library_lib).exists() { + lib_paths.push(conda_library_lib); + } + } } // 3. Try to find python in PATH if let Ok(paths) = std::env::var("PATH") { // first check python, then python3 - for path in paths.split(":") { + for path in paths.split(PATH_LIST_SEPARATOR) { 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) { @@ -90,7 +124,7 @@ mod dynamic_loading_specific { } } } - for path in paths.split(":") { + for path in paths.split(PATH_LIST_SEPARATOR) { 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) { @@ -122,14 +156,14 @@ mod dynamic_loading_specific { fn get_lib_candidates() -> Vec { let mut candidates = vec![]; - // User-defined candidates via environment variables + // User-defined candidates via environment variables (paths split by platform separator) for env_var in [format!("DFTD3_DYLOAD_{LIB_NAME}").as_str(), "DFTD3_DYLOAD"] { if let Ok(path) = std::env::var(env_var) { - candidates.extend(path.split(":").map(|s| s.to_string())); + candidates.extend(path.split(PATH_LIST_SEPARATOR).map(|s| s.to_string())); } } - // LD_LIBRARY_PATH style discovery + // LD_LIBRARY_PATH style discovery (paths split by platform separator) for env_var in [ "LD_LIBRARY_PATH", // linux "DYLD_LIBRARY_PATH", // macos @@ -137,7 +171,7 @@ mod dynamic_loading_specific { "PATH", // windows ] { if let Ok(paths) = std::env::var(env_var) { - for path in paths.split(":") { + for path in paths.split(PATH_LIST_SEPARATOR) { candidates.push(format!("{path}/{DLL_PREFIX}{LIB_NAME_LINK}{DLL_SUFFIX}")); } } @@ -173,10 +207,11 @@ Candidates: {candidates:#?} Please check: - If dynamic-loading is not desired, disable the `dynamic_loading` feature in Cargo.toml. - Use environment variable `DFTD3_DYLOAD_{LIB_NAME}` or `DFTD3_DYLOAD` to specify the library path. -- If `lib{LIB_NAME_LINK}.so` is installed on your system. -- If `LD_LIBRARY_PATH` is set correctly. +- If `lib{LIB_NAME_LINK}.so` (or `{LIB_NAME_LINK}.dll` on Windows) is installed on your system. +- If `LD_LIBRARY_PATH` (Unix/macOS) or `PATH` (Windows) is set correctly. - Python interpreter path discovery: if Python is at `/path/bin/python`, the library is expected at `/path/lib/libs-dftd3.so`. +- On Windows with conda, try `{LIB_NAME_LINK}` from `/Library/bin`. Error message(s): {err_msg} From 239655bcf2ad6164ae6a4ccbebdb529bdee9a8fc Mon Sep 17 00:00:00 2001 From: Shirong_Wang Date: Sat, 13 Jun 2026 17:35:23 +0800 Subject: [PATCH 2/2] fmt --- dftd3/src/ffi_dynamic/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dftd3/src/ffi_dynamic/mod.rs b/dftd3/src/ffi_dynamic/mod.rs index b6d59ed..b186777 100644 --- a/dftd3/src/ffi_dynamic/mod.rs +++ b/dftd3/src/ffi_dynamic/mod.rs @@ -156,7 +156,8 @@ mod dynamic_loading_specific { fn get_lib_candidates() -> Vec { let mut candidates = vec![]; - // User-defined candidates via environment variables (paths split by platform separator) + // User-defined candidates via environment variables (paths split by platform + // separator) for env_var in [format!("DFTD3_DYLOAD_{LIB_NAME}").as_str(), "DFTD3_DYLOAD"] { if let Ok(path) = std::env::var(env_var) { candidates.extend(path.split(PATH_LIST_SEPARATOR).map(|s| s.to_string()));