pub mod macros;
use std::env;
use std::path::PathBuf;
use log::{debug};
pub fn find(lib_name: &str, pkg_name: Option<&str>) -> Option<String> {
debug!("Starting search for library '{}' package = '{}'", lib_name, pkg_name.unwrap_or("None"));
let pkg_name = pkg_name.unwrap_or(lib_name);
let extension = match env::consts::OS {
"windows" => ".dll",
"macos" => ".dylib",
_ => ".so", };
debug!("Chose library extension '{}' for OS '{}'", extension, env::consts::OS);
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());
}
if let Ok(conda_prefix) = env::var("CONDA_PREFIX") {
debug!("Detected Conda environment:");
debug!(" {}", conda_prefix.to_string());
roots.push(PathBuf::from(conda_prefix));
}
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);
}
}
}
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);
}
}
}
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);
}
debug!("Searching roots in order:");
let mut found : Vec<String> = 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());
}
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<String> {
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
}