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