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