pub mod macros; use std::env; use std::path::PathBuf; use log::{debug}; pub fn find(lib_name: &str, pkg_name: Option<&str>) -> Option { // Determine the package name debug!("Starting search for library '{}' package = '{}'", lib_name, pkg_name.unwrap_or("None")); let pkg_name = pkg_name.unwrap_or(lib_name); // Determine the library file extension based on the platform let extension = match env::consts::OS { "windows" => ".dll", "macos" => ".dylib", _ => ".so", // Assume Unix-like system }; debug!("Chose library extension '{}' for OS '{}'", extension, env::consts::OS); // sys.prefix equivalent: `env::current_exe()` gives the path to the executable let mut roots = vec![env::current_exe().ok()?.parent()?.to_path_buf()]; debug!("Adding these current executable directories to search roots:"); for root in &roots { debug!(" {}", root.to_string_lossy()); } // Check for `CONDA_PREFIX` in the environment if let Ok(conda_prefix) = env::var("CONDA_PREFIX") { debug!("Detected Conda environment:"); debug!(" {}", conda_prefix.to_string()); roots.push(PathBuf::from(conda_prefix)); } // Check environment variables like `PKG_HOME` and `PKG_DIR` let env_suffixes = ["HOME", "DIR"]; for suffix in &env_suffixes { let combined = format!("{}_{}", pkg_name, suffix); for env_var in [combined.to_uppercase(), combined.to_lowercase()].iter() { if let Ok(home) = env::var(&env_var) { if home.is_empty() { continue; } debug!("Found environment variable '{}'.", env_var); let fullname = PathBuf::from(&env_var); debug!(" {}", fullname.display()); roots.push(fullname); } } } // Check `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH` for path_var in &["LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH"] { if let Ok(paths) = env::var(path_var) { if paths.is_empty() { continue; } debug!("Found {}", path_var); for home in paths.split(':') { let fullname = PathBuf::from(home).join(format!("lib{}{}", lib_name, extension)); debug!(" {}", fullname.display()); roots.push(fullname); } } } // Check common system paths debug!("Checking common system paths:"); for root in &["/", "/usr/", "/usr/local/", "/opt/", "/opt/homebrew/"] { let folder = PathBuf::from(root); if !folder.exists() { continue; } debug!(" {}", folder.display()); roots.push(folder); } // Search in `lib` and `lib64` under roots debug!("Searching roots in order:"); let mut found : Vec = vec![]; for root in &roots { for lib_dir in &["lib", "lib64"] { let fullname = root.join(lib_dir).join(format!("lib{}{}", lib_name, extension)); debug!(" {} {}", if fullname.exists() { "✅" } else { "❌" }, fullname.to_string_lossy()); if fullname.exists() { found.push(fullname.to_string_lossy().to_string()); } } } if found.len() > 0 { return Some(found[0].clone()); } // Fallback: Use `find_library` (requires the `cc` crate for better functionality) let cc_attempt = find_library(lib_name); if cc_attempt.is_some() { debug!("Found library using `cc` crate: {}", cc_attempt.as_ref().unwrap()); } return cc_attempt } fn find_library(lib_name: &str) -> Option { if let Ok(output) = std::process::Command::new("ldconfig").arg("-p").output() { let output_str = String::from_utf8_lossy(&output.stdout); for line in output_str.lines() { if line.contains(&format!("lib{}.", lib_name)) { if let Some(path) = line.split_whitespace().last() { return Some(path.to_string()); } } } } None }