1pub mod macros;
2
3use std::env;
4use std::path::PathBuf;
5use log::{debug};
6
7pub fn find(lib_name: &str, pkg_name: Option<&str>) -> Option<String> {
8 debug!("Starting search for library '{}' package = '{}'", lib_name, pkg_name.unwrap_or("None"));
10 let pkg_name = pkg_name.unwrap_or(lib_name);
11
12 let extension = match env::consts::OS {
14 "windows" => ".dll",
15 "macos" => ".dylib",
16 _ => ".so", };
18 debug!("Chose library extension '{}' for OS '{}'", extension, env::consts::OS);
19
20 let mut roots = vec![env::current_exe().ok()?.parent()?.to_path_buf()];
22 debug!("Adding these current executable directories to search roots:");
23 for root in &roots {
24 debug!(" {}", root.to_string_lossy());
25 }
26
27 if let Ok(conda_prefix) = env::var("CONDA_PREFIX") {
29 debug!("Detected Conda environment:");
30 debug!(" {}", conda_prefix.to_string());
31 roots.push(PathBuf::from(conda_prefix));
32 }
33
34
35
36 let env_suffixes = ["HOME", "DIR"];
38
39
40 for suffix in &env_suffixes {
41 let combined = format!("{}_{}", pkg_name, suffix);
42 for env_var in [combined.to_uppercase(), combined.to_lowercase()].iter() {
43 if let Ok(home) = env::var(&env_var) {
44 if home.is_empty() {
45 continue;
46 }
47 debug!("Found environment variable '{}'.", env_var);
48 let fullname = PathBuf::from(&env_var);
49 debug!(" {}", fullname.display());
50 roots.push(fullname);
51 }
52 }
53 }
54
55
56 for path_var in &["LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH"] {
58 if let Ok(paths) = env::var(path_var) {
59 if paths.is_empty() {
60 continue;
61 }
62 debug!("Found {}", path_var);
63 for home in paths.split(':') {
64 let fullname = PathBuf::from(home).join(format!("lib{}{}", lib_name, extension));
65 debug!(" {}", fullname.display());
66 roots.push(fullname);
67 }
68 }
69 }
70
71 debug!("Checking common system paths:");
73 for root in &["/", "/usr/", "/usr/local/", "/opt/", "/opt/homebrew/"] {
74 let folder = PathBuf::from(root);
75 if !folder.exists() {
76 continue;
77 }
78 debug!(" {}", folder.display());
79 roots.push(folder);
80 }
81
82 debug!("Searching roots in order:");
84 let mut found : Vec<String> = vec![];
85 for root in &roots {
86 for lib_dir in &["lib", "lib64"] {
87 let fullname = root.join(lib_dir).join(format!("lib{}{}", lib_name, extension));
88 debug!(" {} {}", if fullname.exists() { "✅" } else { "❌" }, fullname.to_string_lossy());
89 if fullname.exists() {
90 found.push(fullname.to_string_lossy().to_string());
91 }
92 }
93 }
94
95 if found.len() > 0 {
96 return Some(found[0].clone());
97 }
98
99 let cc_attempt = find_library(lib_name);
101 if cc_attempt.is_some() {
102 debug!("Found library using `cc` crate: {}", cc_attempt.as_ref().unwrap());
103 }
104 return cc_attempt
105}
106
107fn find_library(lib_name: &str) -> Option<String> {
108 if let Ok(output) = std::process::Command::new("ldconfig").arg("-p").output() {
109 let output_str = String::from_utf8_lossy(&output.stdout);
110 for line in output_str.lines() {
111 if line.contains(&format!("lib{}.", lib_name)) {
112 if let Some(path) = line.split_whitespace().last() {
113 return Some(path.to_string());
114 }
115 }
116 }
117 }
118 None
119}