|
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 |
| - names: Vec<&'static str>, |
16 |
| -} |
17 |
| - |
18 |
| -/// Represents the match of a library when it's found |
19 |
| -struct LibraryPath { |
20 |
| - path: PathBuf, |
21 |
| - library: String, |
22 |
| -} |
23 |
| - |
24 |
| -/// Find files recursively in a directory matching a pattern |
25 |
| -fn find_files_recursively<P: AsRef<Path>>( |
26 |
| - root_dir: P, |
27 |
| - filename: &str, |
28 |
| - max_depth: Option<usize>, |
29 |
| -) -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> { |
30 |
| - let mut matches = Vec::new(); |
31 |
| - let mut stack = vec![(root_dir.as_ref().to_path_buf(), 0)]; |
32 |
| - |
33 |
| - while let Some((current_dir, depth)) = stack.pop() { |
34 |
| - if let Some(max) = max_depth { |
35 |
| - if depth > max { |
36 |
| - continue; |
37 |
| - } |
38 |
| - } |
39 |
| - |
40 |
| - if let Ok(entries) = fs::read_dir(¤t_dir) { |
41 |
| - for entry in entries.flatten() { |
42 |
| - let path = entry.path(); |
43 |
| - |
44 |
| - if path.is_file() { |
45 |
| - // Simple filename comparison instead of regex |
46 |
| - if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { |
47 |
| - if file_name == filename { |
48 |
| - matches.push(path); |
49 |
| - } |
50 |
| - } |
51 |
| - } else if path.is_dir() { |
52 |
| - stack.push((path, depth + 1)); |
53 |
| - } |
54 |
| - } |
55 |
| - } |
56 |
| - } |
57 |
| - |
58 |
| - Ok(matches) |
59 |
| -} |
60 |
| - |
61 |
| -fn platform_install_paths() -> Result<InstallPath, Box<dyn std::error::Error>> { |
62 |
| - if cfg!(target_os = "windows") { |
63 |
| - // Defaults to path: C:\Users\Default\AppData\Local |
64 |
| - let local_app_data = env::var("LOCALAPPDATA") |
65 |
| - .unwrap_or_else(|_| String::from("C:\\Users\\Default\\AppData\\Local")); |
66 |
| - |
67 |
| - Ok(InstallPath { |
68 |
| - paths: vec![PathBuf::from(local_app_data) |
69 |
| - .join("MetaCall") |
70 |
| - .join("metacall")], |
71 |
| - names: vec!["metacall.lib"], |
72 |
| - }) |
73 |
| - } else if cfg!(target_os = "macos") { |
74 |
| - Ok(InstallPath { |
75 |
| - paths: vec![ |
76 |
| - PathBuf::from("/opt/homebrew/lib/"), |
77 |
| - PathBuf::from("/usr/local/lib/"), |
78 |
| - ], |
79 |
| - names: vec!["libmetacall.dylib"], |
80 |
| - }) |
81 |
| - } else if cfg!(target_os = "linux") { |
82 |
| - Ok(InstallPath { |
83 |
| - paths: vec![PathBuf::from("/usr/local/lib/"), PathBuf::from("/gnu/lib/")], |
84 |
| - names: vec!["libmetacall.so"], |
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 |
| - names: vec![ |
99 |
| - "libmetacall.so", |
100 |
| - "libmetacalld.so", |
101 |
| - "libmetacall.dylib", |
102 |
| - "libmetacalld.dylib", |
103 |
| - "metacall.lib", |
104 |
| - "metacalld.lib", |
105 |
| - ], |
106 |
| - }); |
107 |
| - } |
108 |
| - |
109 |
| - // Fall back to platform-specific paths |
110 |
| - platform_install_paths() |
111 |
| -} |
112 |
| - |
113 |
| -/// Get the parent path and library name |
114 |
| -fn get_parent_and_library(path: &Path) -> Option<(PathBuf, String)> { |
115 |
| - let parent = path.parent()?.to_path_buf(); |
116 |
| - |
117 |
| - // Get the file stem (filename without extension) |
118 |
| - let stem = path.file_stem()?.to_str()?; |
119 |
| - |
120 |
| - // Remove "lib" prefix if present |
121 |
| - let cleaned_stem = stem.strip_prefix("lib").unwrap_or(stem).to_string(); |
122 |
| - |
123 |
| - Some((parent, cleaned_stem)) |
124 |
| -} |
125 |
| - |
126 |
| -/// Find the MetaCall library |
127 |
| -/// This orchestrates the search process |
128 |
| -fn find_metacall_library() -> Result<LibraryPath, Box<dyn std::error::Error>> { |
129 |
| - let search_config = get_search_config()?; |
130 |
| - |
131 |
| - // Search in each configured path |
132 |
| - for search_path in &search_config.paths { |
133 |
| - for name in &search_config.names { |
134 |
| - // Search with no limit in depth |
135 |
| - match find_files_recursively(search_path, name, None) { |
136 |
| - Ok(files) if !files.is_empty() => { |
137 |
| - let found_lib = fs::canonicalize(&files[0])?; |
138 |
| - |
139 |
| - match get_parent_and_library(&found_lib) { |
140 |
| - Some((parent, name)) => { |
141 |
| - return Ok(LibraryPath { |
142 |
| - path: parent, |
143 |
| - library: name, |
144 |
| - }) |
145 |
| - } |
146 |
| - None => continue, |
147 |
| - }; |
148 |
| - } |
149 |
| - Ok(_) => { |
150 |
| - // No files found in this path, continue searching |
151 |
| - continue; |
152 |
| - } |
153 |
| - Err(e) => { |
154 |
| - eprintln!("Error searching in {}: {}", search_path.display(), e); |
155 |
| - continue; |
156 |
| - } |
157 |
| - } |
158 |
| - } |
159 |
| - } |
160 |
| - |
161 |
| - // If we get here, library wasn't found |
162 |
| - let search_paths: Vec<String> = search_config |
163 |
| - .paths |
164 |
| - .iter() |
165 |
| - .map(|p| p.display().to_string()) |
166 |
| - .collect(); |
167 |
| - |
168 |
| - Err(format!( |
169 |
| - "MetaCall library not found. Searched in: {}. \ |
170 |
| - If you have it installed elsewhere, set METACALL_INSTALL_PATH environment variable.", |
171 |
| - search_paths.join(", ") |
172 |
| - ) |
173 |
| - .into()) |
174 |
| -} |
175 |
| - |
176 |
| -fn define_library_search_path(env_var: &str, separator: &str, path: &Path) -> String { |
177 |
| - // Get the current value of the env var, if any |
178 |
| - let existing = env::var(env_var).unwrap_or_default(); |
179 |
| - let path_str: String = String::from(path.to_str().unwrap()); |
180 |
| - |
181 |
| - // Append to it |
182 |
| - let combined = if existing.is_empty() { |
183 |
| - path_str |
184 |
| - } else { |
185 |
| - format!("{}{}{}", existing, separator, path_str) |
186 |
| - }; |
187 |
| - |
188 |
| - format!("{}={}", env_var, combined) |
189 |
| -} |
190 |
| - |
191 | 1 | fn main() {
|
192 |
| - // When running tests from CMake |
193 |
| - if let Ok(val) = env::var("PROJECT_OUTPUT_DIR") { |
194 |
| - // Link search path to build folder |
195 |
| - println!("cargo:rustc-link-search=native={val}"); |
196 |
| - |
197 |
| - // Link against correct version of metacall |
198 |
| - match env::var("CMAKE_BUILD_TYPE") { |
199 |
| - Ok(val) => { |
200 |
| - if val == "Debug" { |
201 |
| - // Try to link the debug version when running tests |
202 |
| - println!("cargo:rustc-link-lib=dylib=metacalld"); |
203 |
| - } else { |
204 |
| - println!("cargo:rustc-link-lib=dylib=metacall"); |
205 |
| - } |
206 |
| - } |
207 |
| - Err(_) => { |
208 |
| - println!("cargo:rustc-link-lib=dylib=metacall"); |
209 |
| - } |
210 |
| - } |
211 |
| - } else { |
212 |
| - // When building from Cargo, try to find MetaCall |
213 |
| - match find_metacall_library() { |
214 |
| - Ok(lib_path) => { |
215 |
| - // Define linker flags |
216 |
| - println!("cargo:rustc-link-search=native={}", lib_path.path.display()); |
217 |
| - println!("cargo:rustc-link-lib=dylib={}", lib_path.library); |
218 |
| - |
219 |
| - // Set the runtime environment variable for finding the library during tests |
220 |
| - #[cfg(target_os = "linux")] |
221 |
| - const ENV_VAR: &str = "LD_LIBRARY_PATH"; |
222 |
| - |
223 |
| - #[cfg(target_os = "macos")] |
224 |
| - const ENV_VAR: &str = "DYLD_LIBRARY_PATH"; |
225 |
| - |
226 |
| - #[cfg(target_os = "windows")] |
227 |
| - const ENV_VAR: &str = "PATH"; |
228 |
| - |
229 |
| - #[cfg(target_os = "aix")] |
230 |
| - const ENV_VAR: &str = "LIBPATH"; |
231 |
| - |
232 |
| - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "aix"))] |
233 |
| - const SEPARATOR: &str = ":"; |
234 |
| - |
235 |
| - #[cfg(target_os = "windows")] |
236 |
| - const SEPARATOR: &str = ";"; |
237 |
| - |
238 |
| - println!( |
239 |
| - "cargo:rustc-env={}", |
240 |
| - define_library_search_path(ENV_VAR, SEPARATOR, &lib_path.path) |
241 |
| - ); |
242 |
| - } |
243 |
| - Err(e) => { |
244 |
| - // Print the error |
245 |
| - eprintln!( |
246 |
| - "Failed to find MetaCall library with: {e} \ |
247 |
| - Still trying to link in case the library is in system paths" |
248 |
| - ); |
249 |
| - |
250 |
| - // Still try to link in case the library is in system paths |
251 |
| - println!("cargo:rustc-link-lib=dylib=metacall") |
252 |
| - } |
253 |
| - } |
254 |
| - } |
| 2 | + // Find MetaCall library |
| 3 | + metacall_sys::build(); |
255 | 4 | }
|
0 commit comments