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+ names : Vec < & ' static str > ,
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 ( & current_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+ if cfg ! ( target_os = "windows" ) {
57+ // Defaults to path: C:\Users\Default\AppData\Local
58+ let local_app_data = env:: var ( "LOCALAPPDATA" )
59+ . unwrap_or_else ( |_| String :: from ( "C:\\ Users\\ Default\\ AppData\\ Local" ) ) ;
60+
61+ Ok ( InstallPath {
62+ paths : vec ! [ PathBuf :: from( local_app_data)
63+ . join( "MetaCall" )
64+ . join( "metacall" ) ] ,
65+ names : vec ! [ "metacall.dll" ] ,
66+ } )
67+ } else if cfg ! ( target_os = "macos" ) {
68+ Ok ( InstallPath {
69+ paths : vec ! [
70+ PathBuf :: from( "/opt/homebrew/lib/" ) ,
71+ PathBuf :: from( "/usr/local/lib/" ) ,
72+ ] ,
73+ names : vec ! [ "metacall.dylib" ] ,
74+ } )
75+ } else if cfg ! ( target_os = "linux" ) {
76+ Ok ( InstallPath {
77+ paths : vec ! [ PathBuf :: from( "/usr/local/lib/" ) , PathBuf :: from( "/gnu/lib/" ) ] ,
78+ names : vec ! [ "libmetacall.so" ] ,
79+ } )
80+ } else {
81+ Err ( format ! ( "Platform {} not supported" , env:: consts:: OS ) . into ( ) )
82+ }
83+ }
84+
85+ /// Get search paths, checking for custom installation path first
86+ fn get_search_config ( ) -> Result < InstallPath , Box < dyn std:: error:: Error > > {
87+ // First, check if user specified a custom path
88+ if let Ok ( custom_path) = env:: var ( "METACALL_INSTALL_PATH" ) {
89+ // For custom paths, we need to search for any metacall library variant
90+ return Ok ( InstallPath {
91+ paths : vec ! [ PathBuf :: from( custom_path) ] ,
92+ names : vec ! [ "libmetacall.so" , "libmetacalld.so" , "libmetacall.dylib" , "libmetacalld.dylib" , "metacall.dll" , "metacalld.dll" ]
93+ } ) ;
94+ }
95+
96+ // Fall back to platform-specific paths
97+ platform_install_paths ( )
98+ }
99+
100+ /// Find the MetaCall library
101+ /// This orchestrates the search process
102+ fn find_metacall_library ( ) -> Result < PathBuf , Box < dyn std:: error:: Error > > {
103+ let search_config = get_search_config ( ) ?;
104+
105+ // Search in each configured path
106+ for search_path in & search_config. paths {
107+ for name in & search_config. names {
108+ // Search with no limit in depth
109+ match find_files_recursively ( search_path, & name. to_string ( ) , None ) {
110+ Ok ( files) if !files. is_empty ( ) => {
111+ let found_lib = fs:: canonicalize ( & files[ 0 ] ) ?;
112+
113+ return Ok ( found_lib. clone ( ) ) ;
114+ }
115+ Ok ( _) => {
116+ // No files found in this path, continue searching
117+ continue ;
118+ }
119+ Err ( e) => {
120+ eprintln ! (
121+ "Error searching in {}: {}" ,
122+ search_path. display( ) ,
123+ e
124+ ) ;
125+ continue ;
126+ }
127+ }
128+ }
129+ }
130+
131+ // If we get here, library wasn't found
132+ let search_paths: Vec < String > = search_config
133+ . paths
134+ . iter ( )
135+ . map ( |p| p. display ( ) . to_string ( ) )
136+ . collect ( ) ;
137+
138+ Err ( format ! (
139+ "MetaCall library not found. Searched in: {}. \
140+ If you have it installed elsewhere, set METACALL_INSTALL_PATH environment variable.",
141+ search_paths. join( ", " )
142+ )
143+ . into ( ) )
144+ }
2145
3146fn main ( ) {
4147 // When running tests from CMake
@@ -21,16 +164,21 @@ fn main() {
21164 }
22165 }
23166 } 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" )
167+ // When building from Cargo, try to find MetaCall
168+ match find_metacall_library ( ) {
169+ Ok ( lib_path ) => {
170+ // Extract the directory containing the library
171+ if let Some ( lib_dir ) = lib_path . parent ( ) {
172+ println ! ( "cargo:rustc-link-search=native={}" , lib_dir . display ( ) ) ;
173+ println ! ( "cargo:rustc-link-lib=dylib=metacall" ) ;
174+ }
32175 }
33- _ => {
176+ Err ( e) => {
177+ // Print the error
178+ eprintln ! ( "Failed to find MetaCall library with: {e} \
179+ Still trying to link in case the library is in system paths") ;
180+
181+ // Still try to link in case the library is in system paths
34182 println ! ( "cargo:rustc-link-lib=dylib=metacall" )
35183 }
36184 }
0 commit comments