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
+ }
2
145
3
146
fn main ( ) {
4
147
// When running tests from CMake
@@ -21,16 +164,21 @@ fn main() {
21
164
}
22
165
}
23
166
} 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
+ }
32
175
}
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
34
182
println ! ( "cargo:rustc-link-lib=dylib=metacall" )
35
183
}
36
184
}
0 commit comments