1
1
use std:: collections:: { HashMap , HashSet } ;
2
2
use std:: env;
3
3
use std:: ffi:: OsStr ;
4
- use std:: fs:: File ;
5
- use std:: io:: { BufRead , BufReader } ;
6
4
use std:: path:: { Path , PathBuf } ;
7
5
use std:: time:: Instant ;
8
6
@@ -169,61 +167,137 @@ fn files_with_extension<'e>(dir: &Path, extension: impl AsRef<OsStr> + 'e) -> Re
169
167
} )
170
168
}
171
169
172
- fn get_module_header_dir ( header_dir : & Path ) -> Option < PathBuf > {
173
- let mut out = header_dir. join ( "opencv2.framework/Headers" ) ;
174
- if out. exists ( ) {
175
- return Some ( out) ;
176
- }
177
- out = header_dir. join ( "opencv2" ) ;
178
- if out. exists ( ) {
179
- return Some ( out) ;
170
+ mod header {
171
+ use std:: fs:: File ;
172
+ use std:: io:: { BufRead , BufReader } ;
173
+ use std:: path:: { Path , PathBuf } ;
174
+ use std:: process:: Command ;
175
+
176
+ use semver:: Version ;
177
+
178
+ pub fn get_module_header_dir ( header_dir : & Path ) -> Option < PathBuf > {
179
+ let mut out = header_dir. join ( "opencv2.framework/Headers" ) ;
180
+ if out. is_dir ( ) {
181
+ return Some ( out) ;
182
+ }
183
+ out = header_dir. join ( "opencv2" ) ;
184
+ if out. is_dir ( ) {
185
+ return Some ( out) ;
186
+ }
187
+ None
180
188
}
181
- None
182
- }
183
189
184
- fn get_version_header ( header_dir : & Path ) -> Option < PathBuf > {
185
- get_module_header_dir ( header_dir)
186
- . map ( |dir| dir. join ( "core/version.hpp" ) )
187
- . filter ( |hdr| hdr. is_file ( ) )
188
- }
190
+ /// Something like `/usr/include/x86_64-linux-gnu/opencv4/opencv2/` on newer Debian-derived distros
191
+ pub fn get_multiarch_module_header_dir ( ) -> Option < PathBuf > {
192
+ let try_multiarch = Command :: new ( "dpkg-architecture" )
193
+ . args ( [ "--query" , "DEB_TARGET_MULTIARCH" ] )
194
+ . output ( )
195
+ . inspect_err ( |e| eprintln ! ( "=== Failed to get DEB_TARGET_MULTIARCH: {e}" ) )
196
+ . ok ( )
197
+ . or_else ( || {
198
+ Command :: new ( "cc" )
199
+ . arg ( "-print-multiarch" )
200
+ . output ( )
201
+ . inspect_err ( |e| eprintln ! ( "=== Failed to get -print-multiarch: {e}" ) )
202
+ . ok ( )
203
+ } )
204
+ . and_then ( |output| String :: from_utf8 ( output. stdout ) . ok ( ) )
205
+ . map_or_else (
206
+ || {
207
+ eprintln ! ( "=== Failed to get DEB_TARGET_MULTIARCH, trying common multiarch paths" ) ;
208
+ vec ! [
209
+ "x86_64-linux-gnu" . to_string( ) ,
210
+ "aarch64-linux-gnu" . to_string( ) ,
211
+ "arm-linux-gnueabihf" . to_string( ) ,
212
+ ]
213
+ } ,
214
+ |multiarch| vec ! [ multiarch. trim( ) . to_string( ) ] ,
215
+ ) ;
189
216
190
- fn get_version_from_headers ( header_dir : & Path ) -> Option < Version > {
191
- let version_hpp = get_version_header ( header_dir) ?;
192
- let mut major = None ;
193
- let mut minor = None ;
194
- let mut revision = None ;
195
- let mut line = String :: with_capacity ( 256 ) ;
196
- let mut reader = BufReader :: new ( File :: open ( version_hpp) . ok ( ) ?) ;
197
- while let Ok ( bytes_read) = reader. read_line ( & mut line) {
198
- if bytes_read == 0 {
199
- break ;
217
+ eprintln ! ( "=== Trying multiarch paths: {try_multiarch:?}" ) ;
218
+
219
+ for multiarch in try_multiarch {
220
+ let header = format ! ( "/usr/include/{multiarch}/opencv4/opencv2" ) ;
221
+ if Path :: new ( & header) . is_dir ( ) {
222
+ return Some ( PathBuf :: from ( header) ) ;
223
+ }
200
224
}
201
- if let Some ( line) = line. strip_prefix ( "#define CV_VERSION_" ) {
202
- let mut parts = line. split_whitespace ( ) ;
203
- if let ( Some ( ver_spec) , Some ( version) ) = ( parts. next ( ) , parts. next ( ) ) {
204
- match ver_spec {
205
- "MAJOR" => {
206
- major = Some ( version. parse ( ) . ok ( ) ?) ;
207
- }
208
- "MINOR" => {
209
- minor = Some ( version. parse ( ) . ok ( ) ?) ;
210
- }
211
- "REVISION" => {
212
- revision = Some ( version. parse ( ) . ok ( ) ?) ;
225
+ None
226
+ }
227
+
228
+ pub fn get_version_header ( header_dir : & Path ) -> Option < PathBuf > {
229
+ get_module_header_dir ( header_dir)
230
+ . map ( |dir| dir. join ( "core/version.hpp" ) )
231
+ . filter ( |hdr| hdr. is_file ( ) )
232
+ }
233
+
234
+ pub fn get_config_header ( header_dir : & Path ) -> Option < PathBuf > {
235
+ get_module_header_dir ( header_dir)
236
+ . map ( |dir| dir. join ( "cvconfig.h" ) )
237
+ . filter ( |hdr| hdr. is_file ( ) )
238
+ . or_else ( || {
239
+ eprintln ! ( "=== Failed to find cvconfig.h in the regular header dir, trying multiarch" ) ;
240
+ get_multiarch_module_header_dir ( )
241
+ . map ( |dir| dir. join ( "cvconfig.h" ) )
242
+ . filter ( |hdr| hdr. is_file ( ) )
243
+ } )
244
+ }
245
+
246
+ pub fn find_version ( header_dir : & Path ) -> Option < Version > {
247
+ let version_hpp = get_version_header ( header_dir) ?;
248
+ let mut major = None ;
249
+ let mut minor = None ;
250
+ let mut revision = None ;
251
+ let mut line = String :: with_capacity ( 256 ) ;
252
+ let mut reader = BufReader :: new ( File :: open ( version_hpp) . ok ( ) ?) ;
253
+ while let Ok ( bytes_read) = reader. read_line ( & mut line) {
254
+ if bytes_read == 0 {
255
+ break ;
256
+ }
257
+ if let Some ( line) = line. strip_prefix ( "#define CV_VERSION_" ) {
258
+ let mut parts = line. split_whitespace ( ) ;
259
+ if let ( Some ( ver_spec) , Some ( version) ) = ( parts. next ( ) , parts. next ( ) ) {
260
+ match ver_spec {
261
+ "MAJOR" => {
262
+ major = Some ( version. parse ( ) . ok ( ) ?) ;
263
+ }
264
+ "MINOR" => {
265
+ minor = Some ( version. parse ( ) . ok ( ) ?) ;
266
+ }
267
+ "REVISION" => {
268
+ revision = Some ( version. parse ( ) . ok ( ) ?) ;
269
+ }
270
+ _ => { }
213
271
}
214
- _ => { }
272
+ }
273
+ if major. is_some ( ) && minor. is_some ( ) && revision. is_some ( ) {
274
+ break ;
215
275
}
216
276
}
217
- if major. is_some ( ) && minor. is_some ( ) && revision. is_some ( ) {
277
+ line. clear ( ) ;
278
+ }
279
+ if let ( Some ( major) , Some ( minor) , Some ( revision) ) = ( major, minor, revision) {
280
+ Some ( Version :: new ( major, minor, revision) )
281
+ } else {
282
+ None
283
+ }
284
+ }
285
+
286
+ pub fn find_enabled_features ( header_dir : & Path ) -> Option < Vec < String > > {
287
+ let config_h = get_config_header ( header_dir) ?;
288
+ let mut out = Vec :: with_capacity ( 64 ) ;
289
+ let mut line = String :: with_capacity ( 256 ) ;
290
+ let mut reader = BufReader :: new ( File :: open ( config_h) . ok ( ) ?) ;
291
+ while let Ok ( bytes_read) = reader. read_line ( & mut line) {
292
+ if bytes_read == 0 {
218
293
break ;
219
294
}
295
+ if let Some ( feature) = line. strip_prefix ( "#define HAVE_" ) {
296
+ out. push ( feature. trim ( ) . to_lowercase ( ) ) ;
297
+ }
298
+ line. clear ( ) ;
220
299
}
221
- line. clear ( ) ;
222
- }
223
- if let ( Some ( major) , Some ( minor) , Some ( revision) ) = ( major, minor, revision) {
224
- Some ( Version :: new ( major, minor, revision) )
225
- } else {
226
- None
300
+ Some ( out)
227
301
}
228
302
}
229
303
@@ -264,13 +338,18 @@ fn make_modules_and_alises(
264
338
Ok ( ( modules, aliases) )
265
339
}
266
340
267
- fn emit_inherent_features ( opencv_version : & Version ) {
341
+ fn emit_inherent_features ( opencv : & Library ) {
268
342
if VersionReq :: parse ( ">=4.10" )
269
343
. expect ( "Static version requirement" )
270
- . matches ( opencv_version )
344
+ . matches ( & opencv . version )
271
345
{
272
346
println ! ( "cargo::rustc-cfg=ocvrs_has_inherent_feature_hfloat" ) ;
273
347
}
348
+ for feature in & opencv. enabled_features {
349
+ if feature == "opencl" {
350
+ println ! ( "cargo::rustc-cfg=ocvrs_has_inherent_feature_opencl" ) ;
351
+ }
352
+ }
274
353
}
275
354
276
355
fn make_compiler ( opencv : & Library , ffi_export_suffix : & str ) -> cc:: Build {
@@ -374,9 +453,7 @@ fn main() -> Result<()> {
374
453
for module in SUPPORTED_MODULES {
375
454
println ! ( "cargo::rustc-check-cfg=cfg(ocvrs_has_module_{module})" ) ;
376
455
}
377
- // MSRV: switch to #[expect] when MSRV is 1.81
378
- #[ allow( clippy:: single_element_loop) ]
379
- for inherent_feature in [ "hfloat" ] {
456
+ for inherent_feature in [ "hfloat" , "opencl" ] {
380
457
println ! ( "cargo::rustc-check-cfg=cfg(ocvrs_has_inherent_feature_{inherent_feature})" ) ;
381
458
}
382
459
@@ -419,10 +496,10 @@ fn main() -> Result<()> {
419
496
let opencv_header_dir = opencv
420
497
. include_paths
421
498
. iter ( )
422
- . find ( |p| get_version_header ( p) . is_some ( ) )
423
- . expect ( "Discovered OpenCV include paths is empty or contains non-existent paths " ) ;
499
+ . find ( |p| header :: get_version_header ( p ) . is_some ( ) && header :: get_config_header ( p) . is_some ( ) )
500
+ . expect ( "Discovered OpenCV include paths do not contain valid OpenCV headers " ) ;
424
501
425
- if let Some ( header_version) = get_version_from_headers ( opencv_header_dir) {
502
+ if let Some ( header_version) = header :: find_version ( opencv_header_dir) {
426
503
if header_version != opencv. version {
427
504
panic ! (
428
505
"OpenCV version from the headers: {header_version} (at {}) must match version of the OpenCV library: {} (include paths: {:?})" ,
@@ -442,7 +519,7 @@ fn main() -> Result<()> {
442
519
)
443
520
}
444
521
445
- let opencv_module_header_dir = get_module_header_dir ( opencv_header_dir) . expect ( "Can't find OpenCV module header dir" ) ;
522
+ let opencv_module_header_dir = header :: get_module_header_dir ( opencv_header_dir) . expect ( "Can't find OpenCV module header dir" ) ;
446
523
eprintln ! (
447
524
"=== Detected OpenCV module header dir at: {}" ,
448
525
opencv_module_header_dir. display( )
@@ -452,7 +529,7 @@ fn main() -> Result<()> {
452
529
println ! ( "cargo::rustc-cfg=ocvrs_has_module_{module}" ) ;
453
530
}
454
531
455
- emit_inherent_features ( & opencv. version ) ;
532
+ emit_inherent_features ( & opencv) ;
456
533
457
534
setup_rerun ( ) ?;
458
535
0 commit comments