@@ -8,13 +8,100 @@ use std::process::{Command, Output};
8
8
use semver:: Version ;
9
9
use shlex:: Shlex ;
10
10
11
- use super :: Result ;
11
+ use super :: library:: Linkage ;
12
+ use super :: { Result , TARGET_ENV_MSVC } ;
13
+
14
+ #[ derive( Debug , PartialEq , Eq ) ]
15
+ pub struct LinkLib ( pub Linkage , pub String ) ;
16
+
17
+ impl LinkLib {
18
+ #[ inline]
19
+ pub fn emit_cargo_rustc_link ( & self ) -> String {
20
+ format ! ( "cargo:rustc-link-lib={}{}" , self . 0 . as_cargo_rustc_link_spec( ) , self . 1 ) // replace with cargo:: syntax when MSRV is 1.77
21
+ }
22
+
23
+ /// Returns Some(new_file_name) with extra parts of the filename removed, None in case of error
24
+ pub fn with_cleaned_up_lib_filename ( self ) -> Option < Self > {
25
+ Some ( Self (
26
+ self . 0 ,
27
+ Path :: new ( & self . 1 )
28
+ . file_name ( )
29
+ . and_then ( |filename| Self :: cleanup_lib_filename ( filename) . unwrap_or ( filename) . to_str ( ) ) ?
30
+ . to_string ( ) ,
31
+ ) )
32
+ }
33
+
34
+ /// Returns Some(new_file_name) if some parts of the filename were removed, None otherwise
35
+ pub fn cleanup_lib_filename ( filename : & OsStr ) -> Option < & OsStr > {
36
+ let mut new_filename = filename;
37
+ // used to check for the file extension (with dots stripped) and for the part of the filename
38
+ const LIB_EXTS : [ & str ; 7 ] = [ ".so." , ".a." , ".dll." , ".lib." , ".dylib." , ".framework." , ".tbd." ] ;
39
+ let filename_path = Path :: new ( new_filename) ;
40
+ // strip lib extension from the filename
41
+ if let ( Some ( stem) , Some ( extension) ) = ( filename_path. file_stem ( ) , filename_path. extension ( ) . and_then ( OsStr :: to_str) ) {
42
+ if LIB_EXTS . iter ( ) . any ( |e| e. trim_matches ( '.' ) . eq_ignore_ascii_case ( extension) ) {
43
+ new_filename = stem;
44
+ }
45
+ }
46
+ if let Some ( mut file) = new_filename. to_str ( ) {
47
+ let orig_len = file. len ( ) ;
48
+
49
+ // strip "lib" prefix from the filename unless targeting MSVC
50
+ if !* TARGET_ENV_MSVC {
51
+ file = file. strip_prefix ( "lib" ) . unwrap_or ( file) ;
52
+ }
53
+
54
+ // strip lib extension + suffix (e.g. .so.4.6.0) from the filename
55
+ LIB_EXTS . iter ( ) . for_each ( |& inner_ext| {
56
+ if let Some ( inner_ext_idx) = file. find ( inner_ext) {
57
+ file = & file[ ..inner_ext_idx] ;
58
+ }
59
+ } ) ;
60
+ if orig_len != file. len ( ) {
61
+ new_filename = OsStr :: new ( file) ;
62
+ }
63
+ }
64
+ if new_filename. len ( ) != filename. len ( ) {
65
+ Some ( new_filename)
66
+ } else {
67
+ None
68
+ }
69
+ }
70
+ }
71
+
72
+ impl From < & str > for LinkLib {
73
+ fn from ( value : & str ) -> Self {
74
+ let ( linkage, value) = Linkage :: from_prefixed_str ( value) ;
75
+ Self ( linkage, value. to_string ( ) )
76
+ }
77
+ }
78
+
79
+ #[ derive( Debug , PartialEq , Eq ) ]
80
+ pub struct LinkSearch ( pub Linkage , pub PathBuf ) ;
81
+
82
+ impl LinkSearch {
83
+ #[ inline]
84
+ pub fn emit_cargo_rustc_link_search ( & self ) -> String {
85
+ format ! (
86
+ "cargo:rustc-link-search={}{}" ,
87
+ self . 0 . as_cargo_rustc_link_search_spec( ) ,
88
+ self . 1 . to_str( ) . expect( "Can't convert link search path to UTF-8 string" )
89
+ ) // replace with cargo:: syntax when MSRV is 1.77
90
+ }
91
+ }
92
+
93
+ impl From < & str > for LinkSearch {
94
+ fn from ( value : & str ) -> Self {
95
+ let ( linkage, value) = Linkage :: from_prefixed_str ( value) ;
96
+ Self ( linkage, value. into ( ) )
97
+ }
98
+ }
12
99
13
100
pub struct ProbeResult {
14
101
pub version : Option < Version > ,
15
102
pub include_paths : Vec < PathBuf > ,
16
- pub link_paths : Vec < PathBuf > ,
17
- pub link_libs : Vec < String > ,
103
+ pub link_paths : Vec < LinkSearch > ,
104
+ pub link_libs : Vec < LinkLib > ,
18
105
}
19
106
20
107
pub struct CmakeProbe < ' r > {
@@ -117,12 +204,16 @@ impl<'r> CmakeProbe<'r> {
117
204
118
205
pub ( crate ) fn extract_from_cmdline (
119
206
cmdline : & str ,
207
+ skip_cmd : bool ,
120
208
include_paths : & mut Vec < PathBuf > ,
121
- link_paths : & mut Vec < PathBuf > ,
122
- link_libs : & mut Vec < String > ,
209
+ link_paths : & mut Vec < LinkSearch > ,
210
+ link_libs : & mut Vec < LinkLib > ,
123
211
) {
124
212
eprintln ! ( "=== Extracting build arguments from: {cmdline}" ) ;
125
213
let mut args = Shlex :: new ( cmdline. trim ( ) ) ;
214
+ if skip_cmd {
215
+ args. next ( ) ;
216
+ }
126
217
while let Some ( arg) = args. next ( ) {
127
218
let arg = arg. trim ( ) ;
128
219
if let Some ( path) = arg. strip_prefix ( "-I" ) {
@@ -131,14 +222,14 @@ impl<'r> CmakeProbe<'r> {
131
222
include_paths. push ( path) ;
132
223
}
133
224
} else if let Some ( path) = arg. strip_prefix ( "-L" ) . or_else ( || arg. strip_prefix ( "-Wl,-rpath," ) ) {
134
- let path = PathBuf :: from ( path. trim_start ( ) ) ;
225
+ let path = LinkSearch ( Linkage :: Default , PathBuf :: from ( path. trim_start ( ) ) ) ;
135
226
if !link_paths. contains ( & path) {
136
227
link_paths. push ( path) ;
137
228
}
138
229
} else if let Some ( lib) = arg. strip_prefix ( "-l" ) {
139
230
// unresolved cmake dependency specification like Qt5::Core
140
- if !lib. contains ( "::" ) {
141
- link_libs. push ( lib. trim_start ( ) . to_string ( ) ) ;
231
+ if !lib. contains ( "::" ) && lib != "gflags_shared" {
232
+ link_libs. push ( LinkLib ( Linkage :: Default , lib. trim_start ( ) . to_string ( ) ) ) ;
142
233
}
143
234
} else if let Some ( framework) = arg. strip_prefix ( "-framework" ) {
144
235
let framework = framework. trim_start ( ) ;
@@ -147,45 +238,41 @@ impl<'r> CmakeProbe<'r> {
147
238
} else {
148
239
framework. to_string ( )
149
240
} ;
150
- let framework_path = Path :: new ( & framework) ;
151
- let has_extension = framework_path
152
- . extension ( )
153
- . and_then ( OsStr :: to_str)
154
- . map_or ( false , |ext| ext. eq_ignore_ascii_case ( "framework" ) ) ;
155
- if has_extension {
156
- link_libs. push ( framework) ;
157
- } else {
158
- link_libs. push ( format ! ( "{}.framework" , framework) ) ;
159
- }
241
+ link_libs. push ( LinkLib ( Linkage :: Framework , framework) ) ;
160
242
} else if !arg. starts_with ( '-' ) {
161
243
let path = Path :: new ( arg) ;
162
- if let Some ( file) = path. file_name ( ) . and_then ( super :: cleanup_lib_filename) {
244
+ if let Some ( cleaned_lib_filename) = path. file_name ( ) . and_then ( LinkLib :: cleanup_lib_filename) {
245
+ let linkage = Linkage :: from_path ( path) ;
163
246
if let Some ( parent) = path. parent ( ) . map ( |p| p. to_owned ( ) ) {
164
- if !link_paths. contains ( & parent) {
165
- link_paths. push ( parent) ;
247
+ let search_path = LinkSearch ( linkage, parent) ;
248
+ if !link_paths. contains ( & search_path) {
249
+ link_paths. push ( search_path) ;
166
250
}
167
251
} else {
168
252
panic ! ( "{}" , arg. to_string( ) ) ;
169
253
}
170
- link_libs. push ( file. to_str ( ) . expect ( "Non-UTF8 filename" ) . to_string ( ) ) ;
254
+ link_libs. push ( LinkLib (
255
+ linkage,
256
+ cleaned_lib_filename. to_str ( ) . expect ( "Non-UTF8 filename" ) . to_string ( ) ,
257
+ ) ) ;
171
258
}
172
259
} else {
173
260
eprintln ! ( "=== Unexpected cmake compiler argument found: {arg}" ) ;
174
261
}
175
262
}
176
263
}
177
264
178
- fn extract_from_makefile ( & self , link_paths : & mut Vec < PathBuf > , link_libs : & mut Vec < String > ) -> Result < ( ) > {
265
+ fn extract_from_makefile ( & self , link_paths : & mut Vec < LinkSearch > , link_libs : & mut Vec < LinkLib > ) -> Result < ( ) > {
179
266
let link_cmdline = fs:: read_to_string ( self . build_dir . join ( "CMakeFiles/ocvrs_probe.dir/link.txt" ) ) ?;
180
- Self :: extract_from_cmdline ( & link_cmdline, & mut vec ! [ ] , link_paths, link_libs) ;
267
+ Self :: extract_from_cmdline ( & link_cmdline, true , & mut vec ! [ ] , link_paths, link_libs) ;
181
268
Ok ( ( ) )
182
269
}
183
270
184
271
fn extract_from_ninja (
185
272
& self ,
186
273
include_paths : & mut Vec < PathBuf > ,
187
- link_paths : & mut Vec < PathBuf > ,
188
- link_libs : & mut Vec < String > ,
274
+ link_paths : & mut Vec < LinkSearch > ,
275
+ link_libs : & mut Vec < LinkLib > ,
189
276
) -> Result < ( ) > {
190
277
let mut link_cmdline = BufReader :: new ( File :: open ( self . build_dir . join ( "build.ninja" ) ) ?) ;
191
278
let mut line = String :: with_capacity ( 2048 ) ;
@@ -208,9 +295,9 @@ impl<'r> CmakeProbe<'r> {
208
295
State :: Reading => {
209
296
let trimmed_line = line. trim_start ( ) ;
210
297
if let Some ( paths) = trimmed_line. strip_prefix ( "LINK_PATH = " ) {
211
- Self :: extract_from_cmdline ( paths, include_paths, link_paths, link_libs) ;
298
+ Self :: extract_from_cmdline ( paths, false , include_paths, link_paths, link_libs) ;
212
299
} else if let Some ( libs) = trimmed_line. strip_prefix ( "LINK_LIBRARIES = " ) {
213
- Self :: extract_from_cmdline ( libs, include_paths, link_paths, link_libs) ;
300
+ Self :: extract_from_cmdline ( libs, false , include_paths, link_paths, link_libs) ;
214
301
}
215
302
}
216
303
}
@@ -292,7 +379,7 @@ impl<'r> CmakeProbe<'r> {
292
379
if output. status . success ( ) {
293
380
let stdout = String :: from_utf8 ( output. stdout ) ?;
294
381
eprintln ! ( "=== cmake include arguments: {stdout:#?}" ) ;
295
- Self :: extract_from_cmdline ( & stdout, & mut include_paths, & mut link_paths, & mut link_libs) ;
382
+ Self :: extract_from_cmdline ( & stdout, false , & mut include_paths, & mut link_paths, & mut link_libs) ;
296
383
Ok ( ( ) )
297
384
} else {
298
385
Err (
@@ -314,7 +401,7 @@ impl<'r> CmakeProbe<'r> {
314
401
if output. status . success ( ) {
315
402
let stdout = String :: from_utf8 ( output. stdout ) ?;
316
403
eprintln ! ( "=== cmake link arguments: {stdout:#?}" ) ;
317
- Self :: extract_from_cmdline ( & stdout, & mut include_paths, & mut link_paths, & mut link_libs) ;
404
+ Self :: extract_from_cmdline ( & stdout, false , & mut include_paths, & mut link_paths, & mut link_libs) ;
318
405
Ok ( ( ) )
319
406
} else {
320
407
Err (
0 commit comments