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,131 @@ 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/` on newer Debian-derived distros
191
+ pub fn get_multiarch_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_dir = PathBuf :: from ( format ! ( "/usr/include/{multiarch}/opencv4" ) ) ;
221
+ if header_dir. is_dir ( ) {
222
+ return Some ( header_dir) ;
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
+ }
239
+
240
+ pub fn find_version ( header_dir : & Path ) -> Option < Version > {
241
+ let version_hpp = get_version_header ( header_dir) ?;
242
+ let mut major = None ;
243
+ let mut minor = None ;
244
+ let mut revision = None ;
245
+ let mut line = String :: with_capacity ( 256 ) ;
246
+ let mut reader = BufReader :: new ( File :: open ( version_hpp) . ok ( ) ?) ;
247
+ while let Ok ( bytes_read) = reader. read_line ( & mut line) {
248
+ if bytes_read == 0 {
249
+ break ;
250
+ }
251
+ if let Some ( line) = line. strip_prefix ( "#define CV_VERSION_" ) {
252
+ let mut parts = line. split_whitespace ( ) ;
253
+ if let ( Some ( ver_spec) , Some ( version) ) = ( parts. next ( ) , parts. next ( ) ) {
254
+ match ver_spec {
255
+ "MAJOR" => {
256
+ major = Some ( version. parse ( ) . ok ( ) ?) ;
257
+ }
258
+ "MINOR" => {
259
+ minor = Some ( version. parse ( ) . ok ( ) ?) ;
260
+ }
261
+ "REVISION" => {
262
+ revision = Some ( version. parse ( ) . ok ( ) ?) ;
263
+ }
264
+ _ => { }
213
265
}
214
- _ => { }
266
+ }
267
+ if major. is_some ( ) && minor. is_some ( ) && revision. is_some ( ) {
268
+ break ;
215
269
}
216
270
}
217
- if major. is_some ( ) && minor. is_some ( ) && revision. is_some ( ) {
271
+ line. clear ( ) ;
272
+ }
273
+ if let ( Some ( major) , Some ( minor) , Some ( revision) ) = ( major, minor, revision) {
274
+ Some ( Version :: new ( major, minor, revision) )
275
+ } else {
276
+ None
277
+ }
278
+ }
279
+
280
+ pub fn find_enabled_features ( header_dir : & Path ) -> Option < Vec < String > > {
281
+ let config_h = get_config_header ( header_dir) ?;
282
+ let mut out = Vec :: with_capacity ( 64 ) ;
283
+ let mut line = String :: with_capacity ( 256 ) ;
284
+ let mut reader = BufReader :: new ( File :: open ( config_h) . ok ( ) ?) ;
285
+ while let Ok ( bytes_read) = reader. read_line ( & mut line) {
286
+ if bytes_read == 0 {
218
287
break ;
219
288
}
289
+ if let Some ( feature) = line. strip_prefix ( "#define HAVE_" ) {
290
+ out. push ( feature. trim ( ) . to_lowercase ( ) ) ;
291
+ }
292
+ line. clear ( ) ;
220
293
}
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
294
+ Some ( out)
227
295
}
228
296
}
229
297
@@ -264,13 +332,18 @@ fn make_modules_and_alises(
264
332
Ok ( ( modules, aliases) )
265
333
}
266
334
267
- fn emit_inherent_features ( opencv_version : & Version ) {
335
+ fn emit_inherent_features ( opencv : & Library ) {
268
336
if VersionReq :: parse ( ">=4.10" )
269
337
. expect ( "Static version requirement" )
270
- . matches ( opencv_version )
338
+ . matches ( & opencv . version )
271
339
{
272
340
println ! ( "cargo::rustc-cfg=ocvrs_has_inherent_feature_hfloat" ) ;
273
341
}
342
+ for feature in & opencv. enabled_features {
343
+ if feature == "opencl" {
344
+ println ! ( "cargo::rustc-cfg=ocvrs_has_inherent_feature_opencl" ) ;
345
+ }
346
+ }
274
347
}
275
348
276
349
fn make_compiler ( opencv : & Library , ffi_export_suffix : & str ) -> cc:: Build {
@@ -374,9 +447,7 @@ fn main() -> Result<()> {
374
447
for module in SUPPORTED_MODULES {
375
448
println ! ( "cargo::rustc-check-cfg=cfg(ocvrs_has_module_{module})" ) ;
376
449
}
377
- // MSRV: switch to #[expect] when MSRV is 1.81
378
- #[ allow( clippy:: single_element_loop) ]
379
- for inherent_feature in [ "hfloat" ] {
450
+ for inherent_feature in [ "hfloat" , "opencl" ] {
380
451
println ! ( "cargo::rustc-check-cfg=cfg(ocvrs_has_inherent_feature_{inherent_feature})" ) ;
381
452
}
382
453
@@ -419,10 +490,10 @@ fn main() -> Result<()> {
419
490
let opencv_header_dir = opencv
420
491
. include_paths
421
492
. iter ( )
422
- . find ( |p| get_version_header ( p) . is_some ( ) )
423
- . expect ( "Discovered OpenCV include paths is empty or contains non-existent paths " ) ;
493
+ . find ( |p| header :: get_version_header ( p) . is_some ( ) )
494
+ . expect ( "Discovered OpenCV include paths do not contain valid OpenCV headers " ) ;
424
495
425
- if let Some ( header_version) = get_version_from_headers ( opencv_header_dir) {
496
+ if let Some ( header_version) = header :: find_version ( opencv_header_dir) {
426
497
if header_version != opencv. version {
427
498
panic ! (
428
499
"OpenCV version from the headers: {header_version} (at {}) must match version of the OpenCV library: {} (include paths: {:?})" ,
@@ -442,7 +513,7 @@ fn main() -> Result<()> {
442
513
)
443
514
}
444
515
445
- let opencv_module_header_dir = get_module_header_dir ( opencv_header_dir) . expect ( "Can't find OpenCV module header dir" ) ;
516
+ let opencv_module_header_dir = header :: get_module_header_dir ( opencv_header_dir) . expect ( "Can't find OpenCV module header dir" ) ;
446
517
eprintln ! (
447
518
"=== Detected OpenCV module header dir at: {}" ,
448
519
opencv_module_header_dir. display( )
@@ -452,7 +523,7 @@ fn main() -> Result<()> {
452
523
println ! ( "cargo::rustc-cfg=ocvrs_has_module_{module}" ) ;
453
524
}
454
525
455
- emit_inherent_features ( & opencv. version ) ;
526
+ emit_inherent_features ( & opencv) ;
456
527
457
528
setup_rerun ( ) ?;
458
529
0 commit comments