rsfindlibs/
lib.rs

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    // Determine the package name
9    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    // Determine the library file extension based on the platform
13    let extension = match env::consts::OS {
14        "windows" => ".dll",
15        "macos" => ".dylib",
16        _ => ".so", // Assume Unix-like system
17    };
18    debug!("Chose library extension '{}' for OS '{}'", extension, env::consts::OS);
19
20    // sys.prefix equivalent: `env::current_exe()` gives the path to the executable
21    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    // Check for `CONDA_PREFIX` in the environment
28    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    // Check environment variables like `PKG_HOME` and `PKG_DIR`
37    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    // Check `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`
57    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    // Check common system paths
72    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    // Search in `lib` and `lib64` under roots
83    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    // Fallback: Use `find_library` (requires the `cc` crate for better functionality)
100    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}