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