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,128 @@ 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. exists ( ) {
181
+ return Some ( out) ;
182
+ }
183
+ out = header_dir. join ( "opencv2" ) ;
184
+ if out. exists ( ) {
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
+ . ok ( )
196
+ . or_else ( || Command :: new ( "dpkg-architecture" ) . output ( ) . ok ( ) )
197
+ . and_then ( |output| String :: from_utf8 ( output. stdout ) . ok ( ) )
198
+ . map_or_else (
199
+ || {
200
+ eprintln ! ( "=== Failed to get DEB_TARGET_MULTIARCH, trying common multiarch paths" ) ;
201
+ vec ! [
202
+ "x86_64-linux-gnu" . to_string( ) ,
203
+ "aarch64-linux-gnu" . to_string( ) ,
204
+ "arm-linux-gnueabihf" . to_string( ) ,
205
+ ]
206
+ } ,
207
+ |multiarch| vec ! [ multiarch] ,
208
+ ) ;
189
209
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 ;
210
+ for multiarch in try_multiarch {
211
+ let header = format ! ( "/usr/include/{multiarch}/opencv4/opencv2" ) ;
212
+ if Path :: new ( & header) . exists ( ) {
213
+ return Some ( PathBuf :: from ( header) ) ;
214
+ }
200
215
}
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 ( ) ?) ;
216
+ None
217
+ }
218
+
219
+ pub fn get_version_header ( header_dir : & Path ) -> Option < PathBuf > {
220
+ get_module_header_dir ( header_dir)
221
+ . map ( |dir| dir. join ( "core/version.hpp" ) )
222
+ . filter ( |hdr| hdr. is_file ( ) )
223
+ }
224
+
225
+ pub fn get_config_header ( header_dir : & Path ) -> Option < PathBuf > {
226
+ get_module_header_dir ( header_dir)
227
+ . map ( |dir| dir. join ( "cvconfig.h" ) )
228
+ . filter ( |hdr| hdr. is_file ( ) )
229
+ . or_else ( || {
230
+ eprintln ! ( "=== Failed to find cvconfig.h in the regular header dir, trying multiarch" ) ;
231
+ get_multiarch_module_header_dir ( )
232
+ . map ( |dir| dir. join ( "cvconfig.h" ) )
233
+ . filter ( |hdr| hdr. is_file ( ) )
234
+ } )
235
+ }
236
+
237
+ pub fn find_version ( header_dir : & Path ) -> Option < Version > {
238
+ let version_hpp = get_version_header ( header_dir) ?;
239
+ let mut major = None ;
240
+ let mut minor = None ;
241
+ let mut revision = None ;
242
+ let mut line = String :: with_capacity ( 256 ) ;
243
+ let mut reader = BufReader :: new ( File :: open ( version_hpp) . ok ( ) ?) ;
244
+ while let Ok ( bytes_read) = reader. read_line ( & mut line) {
245
+ if bytes_read == 0 {
246
+ break ;
247
+ }
248
+ if let Some ( line) = line. strip_prefix ( "#define CV_VERSION_" ) {
249
+ let mut parts = line. split_whitespace ( ) ;
250
+ if let ( Some ( ver_spec) , Some ( version) ) = ( parts. next ( ) , parts. next ( ) ) {
251
+ match ver_spec {
252
+ "MAJOR" => {
253
+ major = Some ( version. parse ( ) . ok ( ) ?) ;
254
+ }
255
+ "MINOR" => {
256
+ minor = Some ( version. parse ( ) . ok ( ) ?) ;
257
+ }
258
+ "REVISION" => {
259
+ revision = Some ( version. parse ( ) . ok ( ) ?) ;
260
+ }
261
+ _ => { }
213
262
}
214
- _ => { }
263
+ }
264
+ if major. is_some ( ) && minor. is_some ( ) && revision. is_some ( ) {
265
+ break ;
215
266
}
216
267
}
217
- if major. is_some ( ) && minor. is_some ( ) && revision. is_some ( ) {
268
+ line. clear ( ) ;
269
+ }
270
+ if let ( Some ( major) , Some ( minor) , Some ( revision) ) = ( major, minor, revision) {
271
+ Some ( Version :: new ( major, minor, revision) )
272
+ } else {
273
+ None
274
+ }
275
+ }
276
+
277
+ pub fn find_enabled_features ( header_dir : & Path ) -> Option < Vec < String > > {
278
+ let config_h = get_config_header ( header_dir) ?;
279
+ let mut out = Vec :: with_capacity ( 64 ) ;
280
+ let mut line = String :: with_capacity ( 256 ) ;
281
+ let mut reader = BufReader :: new ( File :: open ( config_h) . ok ( ) ?) ;
282
+ while let Ok ( bytes_read) = reader. read_line ( & mut line) {
283
+ if bytes_read == 0 {
218
284
break ;
219
285
}
286
+ if let Some ( feature) = line. strip_prefix ( "#define HAVE_" ) {
287
+ out. push ( feature. trim ( ) . to_lowercase ( ) ) ;
288
+ }
289
+ line. clear ( ) ;
220
290
}
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
291
+ Some ( out)
227
292
}
228
293
}
229
294
@@ -264,13 +329,18 @@ fn make_modules_and_alises(
264
329
Ok ( ( modules, aliases) )
265
330
}
266
331
267
- fn emit_inherent_features ( opencv_version : & Version ) {
332
+ fn emit_inherent_features ( opencv : & Library ) {
268
333
if VersionReq :: parse ( ">=4.10" )
269
334
. expect ( "Static version requirement" )
270
- . matches ( opencv_version )
335
+ . matches ( & opencv . version )
271
336
{
272
337
println ! ( "cargo::rustc-cfg=ocvrs_has_inherent_feature_hfloat" ) ;
273
338
}
339
+ for feature in & opencv. enabled_features {
340
+ if feature == "opencl" {
341
+ println ! ( "cargo::rustc-cfg=ocvrs_has_inherent_feature_opencl" ) ;
342
+ }
343
+ }
274
344
}
275
345
276
346
fn make_compiler ( opencv : & Library , ffi_export_suffix : & str ) -> cc:: Build {
@@ -374,9 +444,7 @@ fn main() -> Result<()> {
374
444
for module in SUPPORTED_MODULES {
375
445
println ! ( "cargo::rustc-check-cfg=cfg(ocvrs_has_module_{module})" ) ;
376
446
}
377
- // MSRV: switch to #[expect] when MSRV is 1.81
378
- #[ allow( clippy:: single_element_loop) ]
379
- for inherent_feature in [ "hfloat" ] {
447
+ for inherent_feature in [ "hfloat" , "opencl" ] {
380
448
println ! ( "cargo::rustc-check-cfg=cfg(ocvrs_has_inherent_feature_{inherent_feature})" ) ;
381
449
}
382
450
@@ -419,10 +487,10 @@ fn main() -> Result<()> {
419
487
let opencv_header_dir = opencv
420
488
. include_paths
421
489
. iter ( )
422
- . find ( |p| get_version_header ( p) . is_some ( ) )
423
- . expect ( "Discovered OpenCV include paths is empty or contains non-existent paths " ) ;
490
+ . find ( |p| header :: get_version_header ( p ) . is_some ( ) && header :: get_config_header ( p) . is_some ( ) )
491
+ . expect ( "Discovered OpenCV include paths do not contain valid OpenCV headers " ) ;
424
492
425
- if let Some ( header_version) = get_version_from_headers ( opencv_header_dir) {
493
+ if let Some ( header_version) = header :: find_version ( opencv_header_dir) {
426
494
if header_version != opencv. version {
427
495
panic ! (
428
496
"OpenCV version from the headers: {header_version} (at {}) must match version of the OpenCV library: {} (include paths: {:?})" ,
@@ -442,7 +510,7 @@ fn main() -> Result<()> {
442
510
)
443
511
}
444
512
445
- let opencv_module_header_dir = get_module_header_dir ( opencv_header_dir) . expect ( "Can't find OpenCV module header dir" ) ;
513
+ let opencv_module_header_dir = header :: get_module_header_dir ( opencv_header_dir) . expect ( "Can't find OpenCV module header dir" ) ;
446
514
eprintln ! (
447
515
"=== Detected OpenCV module header dir at: {}" ,
448
516
opencv_module_header_dir. display( )
@@ -452,7 +520,7 @@ fn main() -> Result<()> {
452
520
println ! ( "cargo::rustc-cfg=ocvrs_has_module_{module}" ) ;
453
521
}
454
522
455
- emit_inherent_features ( & opencv. version ) ;
523
+ emit_inherent_features ( & opencv) ;
456
524
457
525
setup_rerun ( ) ?;
458
526
0 commit comments