|
1 | | -use std::env; |
| 1 | +use std::{ |
| 2 | + env, fs, |
| 3 | + path::{Path, PathBuf}, |
| 4 | + vec, |
| 5 | +}; |
| 6 | + |
| 7 | +// Search for MetaCall libraries in platform-specific locations |
| 8 | +// Handle custom installation paths via environment variables |
| 9 | +// Find configuration files recursively |
| 10 | +// Provide helpful error messages when things aren't found |
| 11 | + |
| 12 | +/// Represents the install paths for a platform |
| 13 | +struct InstallPath { |
| 14 | + paths: Vec<PathBuf>, |
| 15 | + name: String, |
| 16 | +} |
| 17 | + |
| 18 | +/// Find files recursively in a directory matching a pattern |
| 19 | +fn find_files_recursively<P: AsRef<Path>>( |
| 20 | + root_dir: P, |
| 21 | + filename: &str, |
| 22 | + max_depth: Option<usize>, |
| 23 | +) -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> { |
| 24 | + let mut matches = Vec::new(); |
| 25 | + let mut stack = vec![(root_dir.as_ref().to_path_buf(), 0)]; |
| 26 | + |
| 27 | + while let Some((current_dir, depth)) = stack.pop() { |
| 28 | + if let Some(max) = max_depth { |
| 29 | + if depth > max { |
| 30 | + continue; |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + if let Ok(entries) = fs::read_dir(¤t_dir) { |
| 35 | + for entry in entries.flatten() { |
| 36 | + let path = entry.path(); |
| 37 | + |
| 38 | + if path.is_file() { |
| 39 | + // Simple filename comparison instead of regex |
| 40 | + if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { |
| 41 | + if file_name == filename { |
| 42 | + matches.push(path); |
| 43 | + } |
| 44 | + } |
| 45 | + } else if path.is_dir() { |
| 46 | + stack.push((path, depth + 1)); |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + Ok(matches) |
| 53 | +} |
| 54 | + |
| 55 | +fn platform_install_paths() -> Result<InstallPath, Box<dyn std::error::Error>> { |
| 56 | + // let platform = env::var("PLATFORM"); |
| 57 | + |
| 58 | + if cfg!(target_os = "windows") { |
| 59 | + // defaults to path: |
| 60 | + // C:\Users\Default\AppData\Local |
| 61 | + let local_app_data = env::var("LOCALAPPDATA") |
| 62 | + .unwrap_or_else(|_| String::from("C:\\Users\\Default\\AppData\\Local")); |
| 63 | + |
| 64 | + println!("windows"); |
| 65 | + Ok(InstallPath { |
| 66 | + paths: vec![PathBuf::from(local_app_data) |
| 67 | + .join("Metacall") |
| 68 | + .join("metacall")], |
| 69 | + name: "metacall.dll".to_string(), |
| 70 | + }) |
| 71 | + } else if cfg!(target_os = "macos") { |
| 72 | + println!("macos"); |
| 73 | + Ok(InstallPath { |
| 74 | + paths: vec![ |
| 75 | + PathBuf::from("/opt/homebrew/lib/"), |
| 76 | + PathBuf::from("/usr/local/lib/"), |
| 77 | + ], |
| 78 | + name: "metacall.dylib".to_string(), |
| 79 | + }) |
| 80 | + } else if cfg!(target_os = "linux") { |
| 81 | + println!("linux"); |
| 82 | + Ok(InstallPath { |
| 83 | + paths: vec![PathBuf::from("/usr/local/lib/"), PathBuf::from("/gnu/lib/")], |
| 84 | + name: "libmetacall.so".to_string(), |
| 85 | + }) |
| 86 | + } else { |
| 87 | + Err(format!("Platform {} not supported", env::consts::OS).into()) |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +/// Get search paths, checking for custom installation path first |
| 92 | +fn get_search_config() -> Result<InstallPath, Box<dyn std::error::Error>> { |
| 93 | + // First, check if user specified a custom path |
| 94 | + if let Ok(custom_path) = env::var("METACALL_INSTALL_PATH") { |
| 95 | + // For custom paths, we need to search for any metacall library variant |
| 96 | + return Ok(InstallPath { |
| 97 | + paths: vec![PathBuf::from(custom_path)], |
| 98 | + name: r"^(lib)?metacall(d)?\.(so|dylib|dll)$".to_string(), |
| 99 | + }); |
| 100 | + } |
| 101 | + |
| 102 | + // Fall back to platform-specific paths |
| 103 | + platform_install_paths() |
| 104 | +} |
| 105 | + |
| 106 | +/// Find the MetaCall library |
| 107 | +/// This orchestrates the search process |
| 108 | +fn find_metacall_library() -> Result<PathBuf, Box<dyn std::error::Error>> { |
| 109 | + let search_config = get_search_config()?; |
| 110 | + |
| 111 | + // Search in each configured path |
| 112 | + for search_path in &search_config.paths { |
| 113 | + println!( |
| 114 | + "cargo:warning=Searching for MetaCall in: {}", |
| 115 | + search_path.display() |
| 116 | + ); |
| 117 | + |
| 118 | + // Only search at depth 0 (current directory) |
| 119 | + match find_files_recursively(search_path, &search_config.name, Some(0)) { |
| 120 | + Ok(files) if !files.is_empty() => { |
| 121 | + let found_lib = &files[0]; |
| 122 | + println!( |
| 123 | + "cargo:warning=Found MetaCall library: {}", |
| 124 | + found_lib.display() |
| 125 | + ); |
| 126 | + return Ok(found_lib.clone()); |
| 127 | + } |
| 128 | + Ok(_) => { |
| 129 | + // No files found in this path, continue searching |
| 130 | + continue; |
| 131 | + } |
| 132 | + Err(e) => { |
| 133 | + println!( |
| 134 | + "cargo:warning=Error searching in {}: {}", |
| 135 | + search_path.display(), |
| 136 | + e |
| 137 | + ); |
| 138 | + continue; |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + // If we get here, library wasn't found |
| 144 | + let search_paths: Vec<String> = search_config |
| 145 | + .paths |
| 146 | + .iter() |
| 147 | + .map(|p| p.display().to_string()) |
| 148 | + .collect(); |
| 149 | + |
| 150 | + Err(format!( |
| 151 | + "MetaCall library not found. Searched in: {}. \ |
| 152 | + If you have it installed elsewhere, set METACALL_INSTALL_PATH environment variable.", |
| 153 | + search_paths.join(", ") |
| 154 | + ) |
| 155 | + .into()) |
| 156 | +} |
2 | 157 |
|
3 | 158 | fn main() { |
4 | 159 | // When running tests from CMake |
5 | 160 | if let Ok(val) = env::var("PROJECT_OUTPUT_DIR") { |
6 | | - // Link search path to build folder |
7 | 161 | println!("cargo:rustc-link-search=native={val}"); |
8 | 162 |
|
9 | | - // Link against correct version of metacall |
10 | 163 | match env::var("CMAKE_BUILD_TYPE") { |
11 | | - Ok(val) => { |
12 | | - if val == "Debug" { |
13 | | - // Try to link the debug version when running tests |
14 | | - println!("cargo:rustc-link-lib=dylib=metacalld"); |
15 | | - } else { |
16 | | - println!("cargo:rustc-link-lib=dylib=metacall"); |
17 | | - } |
| 164 | + Ok(val) if val == "Debug" => { |
| 165 | + println!("cargo:rustc-link-lib=dylib=metacalld"); |
18 | 166 | } |
19 | | - Err(_) => { |
| 167 | + _ => { |
20 | 168 | println!("cargo:rustc-link-lib=dylib=metacall"); |
21 | 169 | } |
22 | 170 | } |
23 | | - } else { |
24 | | - // When building from Cargo |
25 | | - let profile = env::var("PROFILE").unwrap(); |
26 | | - match profile.as_str() { |
27 | | - // "debug" => { |
28 | | - // println!("cargo:rustc-link-lib=dylib=metacalld"); |
29 | | - // } |
30 | | - "debug" | "release" => { |
31 | | - println!("cargo:rustc-link-lib=dylib=metacall") |
| 171 | + return; |
| 172 | + } |
| 173 | + |
| 174 | + // When building from Cargo - try to find MetaCall |
| 175 | + match find_metacall_library() { |
| 176 | + Ok(lib_path) => { |
| 177 | + // Extract the directory containing the library |
| 178 | + if let Some(lib_dir) = lib_path.parent() { |
| 179 | + println!("cargo:rustc-link-search=native={}", lib_dir.display()); |
32 | 180 | } |
33 | | - _ => { |
34 | | - println!("cargo:rustc-link-lib=dylib=metacall") |
| 181 | + |
| 182 | + // Link against the library |
| 183 | + let profile = env::var("PROFILE").unwrap_or_else(|_| "release".to_string()); |
| 184 | + match profile.as_str() { |
| 185 | + "debug" | "release" => { |
| 186 | + println!("cargo:rustc-link-lib=dylib=metacall"); |
| 187 | + } |
| 188 | + _ => { |
| 189 | + println!("cargo:rustc-link-lib=dylib=metacall"); |
| 190 | + } |
35 | 191 | } |
36 | 192 | } |
| 193 | + Err(e) => { |
| 194 | + println!("cargo:warning={e}"); |
| 195 | + // Still try to link in case the library is in system paths |
| 196 | + println!("cargo:rustc-link-lib=dylib=metacall"); |
| 197 | + } |
37 | 198 | } |
38 | 199 | } |
0 commit comments