Skip to content

Commit c2af27d

Browse files
committed
Include additional core headers conditionally
1 parent c43f377 commit c2af27d

File tree

7 files changed

+159
-85
lines changed

7 files changed

+159
-85
lines changed

build.rs

Lines changed: 89 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::collections::{HashMap, HashSet};
22
use std::env;
33
use std::ffi::OsStr;
4-
use std::fs::File;
5-
use std::io::{BufRead, BufReader};
64
use std::path::{Path, PathBuf};
75
use std::time::Instant;
86

@@ -169,61 +167,92 @@ fn files_with_extension<'e>(dir: &Path, extension: impl AsRef<OsStr> + 'e) -> Re
169167
})
170168
}
171169

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);
170+
mod header {
171+
use std::fs::File;
172+
use std::io::{BufRead, BufReader};
173+
use std::path::{Path, PathBuf};
174+
175+
use semver::Version;
176+
177+
pub fn get_module_header_dir(header_dir: &Path) -> Option<PathBuf> {
178+
let mut out = header_dir.join("opencv2.framework/Headers");
179+
if out.exists() {
180+
return Some(out);
181+
}
182+
out = header_dir.join("opencv2");
183+
if out.exists() {
184+
return Some(out);
185+
}
186+
None
176187
}
177-
out = header_dir.join("opencv2");
178-
if out.exists() {
179-
return Some(out);
188+
189+
pub fn get_version_header(header_dir: &Path) -> Option<PathBuf> {
190+
get_module_header_dir(header_dir)
191+
.map(|dir| dir.join("core/version.hpp"))
192+
.filter(|hdr| hdr.is_file())
180193
}
181-
None
182-
}
183194

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-
}
195+
pub fn get_config_header(header_dir: &Path) -> Option<PathBuf> {
196+
get_module_header_dir(header_dir)
197+
.map(|dir| dir.join("cvconfig.h"))
198+
.filter(|hdr| hdr.is_file())
199+
}
189200

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;
200-
}
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()?);
201+
pub fn find_version(header_dir: &Path) -> Option<Version> {
202+
let version_hpp = get_version_header(header_dir)?;
203+
let mut major = None;
204+
let mut minor = None;
205+
let mut revision = None;
206+
let mut line = String::with_capacity(256);
207+
let mut reader = BufReader::new(File::open(version_hpp).ok()?);
208+
while let Ok(bytes_read) = reader.read_line(&mut line) {
209+
if bytes_read == 0 {
210+
break;
211+
}
212+
if let Some(line) = line.strip_prefix("#define CV_VERSION_") {
213+
let mut parts = line.split_whitespace();
214+
if let (Some(ver_spec), Some(version)) = (parts.next(), parts.next()) {
215+
match ver_spec {
216+
"MAJOR" => {
217+
major = Some(version.parse().ok()?);
218+
}
219+
"MINOR" => {
220+
minor = Some(version.parse().ok()?);
221+
}
222+
"REVISION" => {
223+
revision = Some(version.parse().ok()?);
224+
}
225+
_ => {}
213226
}
214-
_ => {}
227+
}
228+
if major.is_some() && minor.is_some() && revision.is_some() {
229+
break;
215230
}
216231
}
217-
if major.is_some() && minor.is_some() && revision.is_some() {
232+
line.clear();
233+
}
234+
if let (Some(major), Some(minor), Some(revision)) = (major, minor, revision) {
235+
Some(Version::new(major, minor, revision))
236+
} else {
237+
None
238+
}
239+
}
240+
241+
pub fn find_enabled_features(header_dir: &Path) -> Option<Vec<String>> {
242+
let config_h = get_config_header(header_dir)?;
243+
let mut out = Vec::with_capacity(64);
244+
let mut line = String::with_capacity(256);
245+
let mut reader = BufReader::new(File::open(config_h).ok()?);
246+
while let Ok(bytes_read) = reader.read_line(&mut line) {
247+
if bytes_read == 0 {
218248
break;
219249
}
250+
if let Some(feature) = line.strip_prefix("#define HAVE_") {
251+
out.push(feature.trim().to_lowercase());
252+
}
253+
line.clear();
220254
}
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
255+
Some(out)
227256
}
228257
}
229258

@@ -264,13 +293,18 @@ fn make_modules_and_alises(
264293
Ok((modules, aliases))
265294
}
266295

267-
fn emit_inherent_features(opencv_version: &Version) {
296+
fn emit_inherent_features(opencv: &Library) {
268297
if VersionReq::parse(">=4.10")
269298
.expect("Static version requirement")
270-
.matches(opencv_version)
299+
.matches(&opencv.version)
271300
{
272301
println!("cargo::rustc-cfg=ocvrs_has_inherent_feature_hfloat");
273302
}
303+
for feature in &opencv.enabled_features {
304+
if feature == "opencl" {
305+
println!("cargo::rustc-cfg=ocvrs_has_inherent_feature_opencl");
306+
}
307+
}
274308
}
275309

276310
fn make_compiler(opencv: &Library, ffi_export_suffix: &str) -> cc::Build {
@@ -374,9 +408,7 @@ fn main() -> Result<()> {
374408
for module in SUPPORTED_MODULES {
375409
println!("cargo::rustc-check-cfg=cfg(ocvrs_has_module_{module})");
376410
}
377-
// MSRV: switch to #[expect] when MSRV is 1.81
378-
#[allow(clippy::single_element_loop)]
379-
for inherent_feature in ["hfloat"] {
411+
for inherent_feature in ["hfloat", "opencl"] {
380412
println!("cargo::rustc-check-cfg=cfg(ocvrs_has_inherent_feature_{inherent_feature})");
381413
}
382414

@@ -419,10 +451,10 @@ fn main() -> Result<()> {
419451
let opencv_header_dir = opencv
420452
.include_paths
421453
.iter()
422-
.find(|p| get_version_header(p).is_some())
423-
.expect("Discovered OpenCV include paths is empty or contains non-existent paths");
454+
.find(|p| header::get_version_header(p).is_some() && header::get_config_header(p).is_some())
455+
.expect("Discovered OpenCV include paths do not contain valid OpenCV headers");
424456

425-
if let Some(header_version) = get_version_from_headers(opencv_header_dir) {
457+
if let Some(header_version) = header::find_version(opencv_header_dir) {
426458
if header_version != opencv.version {
427459
panic!(
428460
"OpenCV version from the headers: {header_version} (at {}) must match version of the OpenCV library: {} (include paths: {:?})",
@@ -442,7 +474,7 @@ fn main() -> Result<()> {
442474
)
443475
}
444476

445-
let opencv_module_header_dir = get_module_header_dir(opencv_header_dir).expect("Can't find OpenCV module header dir");
477+
let opencv_module_header_dir = header::get_module_header_dir(opencv_header_dir).expect("Can't find OpenCV module header dir");
446478
eprintln!(
447479
"=== Detected OpenCV module header dir at: {}",
448480
opencv_module_header_dir.display()
@@ -452,7 +484,7 @@ fn main() -> Result<()> {
452484
println!("cargo::rustc-cfg=ocvrs_has_module_{module}");
453485
}
454486

455-
emit_inherent_features(&opencv.version);
487+
emit_inherent_features(&opencv);
456488

457489
setup_rerun()?;
458490

build/binding-generator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
55
use opencv_binding_generator::writer::RustNativeBindingWriter;
66
use opencv_binding_generator::Generator;
77

8-
use super::{get_version_from_headers, GenerateFullBindings, Result};
8+
use super::{header, GenerateFullBindings, Result};
99

1010
/// Because clang can't be used from multiple threads we run the binding generator helper for each
1111
/// module as a separate process. Building an additional helper binary from the build script is problematic,
@@ -27,7 +27,7 @@ pub fn run(mut args: impl Iterator<Item = OsString>) -> Result<()> {
2727
let out_dir = PathBuf::from(args.next().ok_or("3rd argument must be output dir")?);
2828
let module = args.next().ok_or("4th argument must be module name")?;
2929
let module = module.to_str().ok_or("Not a valid module name")?;
30-
let version = get_version_from_headers(&opencv_header_dir)
30+
let version = header::find_version(&opencv_header_dir)
3131
.ok_or("Can't find the version in the headers")?
3232
.to_string();
3333
let arg_additional_include_dirs = args.next();

build/library.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use dunce::canonicalize;
88
use semver::Version;
99

1010
use super::cmake_probe::{CmakeProbe, LinkLib, LinkSearch};
11-
use super::{get_version_from_headers, Result, MANIFEST_DIR, OUT_DIR, TARGET_VENDOR_APPLE};
11+
use super::{header, Result, MANIFEST_DIR, OUT_DIR, TARGET_VENDOR_APPLE};
1212

1313
struct PackageName;
1414

@@ -149,12 +149,19 @@ impl Linkage {
149149
pub struct Library {
150150
pub include_paths: Vec<PathBuf>,
151151
pub version: Version,
152+
pub enabled_features: Vec<String>,
152153
pub cargo_metadata: Vec<String>,
153154
}
154155

155156
impl Library {
156157
fn version_from_include_paths(include_paths: impl IntoIterator<Item = impl AsRef<Path>>) -> Option<Version> {
157-
include_paths.into_iter().find_map(|x| get_version_from_headers(x.as_ref()))
158+
include_paths.into_iter().find_map(|x| header::find_version(x.as_ref()))
159+
}
160+
161+
fn enabled_features_from_include_paths(include_paths: impl IntoIterator<Item = impl AsRef<Path>>) -> Option<Vec<String>> {
162+
include_paths
163+
.into_iter()
164+
.find_map(|x| header::find_enabled_features(x.as_ref()))
158165
}
159166

160167
fn process_env_var_list<'a, T: From<&'a str>>(env_list: Option<EnvList<'a>>, sys_list: Vec<T>) -> Vec<T> {
@@ -231,14 +238,18 @@ impl Library {
231238
let mut cargo_metadata = Vec::with_capacity(64);
232239
let include_paths: Vec<_> = include_paths.iter().map(PathBuf::from).collect();
233240

234-
let version = Self::version_from_include_paths(&include_paths).ok_or("Could not OpenCV version from include_paths")?;
241+
let version =
242+
Self::version_from_include_paths(&include_paths).ok_or("Could not get OpenCV version from include_paths")?;
243+
let enabled_features =
244+
Self::enabled_features_from_include_paths(&include_paths).ok_or("Could not get OpenCV config from include_paths")?;
235245

236246
cargo_metadata.extend(Self::process_link_paths(Some(link_paths), vec![]));
237247
cargo_metadata.extend(Self::process_link_libs(Some(link_libs), vec![]));
238248

239249
Ok(Self {
240250
include_paths,
241251
version,
252+
enabled_features,
242253
cargo_metadata,
243254
})
244255
} else {
@@ -306,10 +317,13 @@ impl Library {
306317
}
307318

308319
let include_paths = Self::process_env_var_list(include_paths, opencv.include_paths);
320+
let enabled_features =
321+
Self::enabled_features_from_include_paths(&include_paths).ok_or("Could not get OpenCV config from include_paths")?;
309322

310323
Ok(Self {
311324
include_paths,
312325
version: Version::parse(&opencv.version)?,
326+
enabled_features,
313327
cargo_metadata,
314328
})
315329
}
@@ -358,9 +372,14 @@ impl Library {
358372
cargo_metadata.extend(Self::process_link_paths(link_paths, probe_result.link_paths));
359373
cargo_metadata.extend(Self::process_link_libs(link_libs, probe_result.link_libs));
360374

375+
let include_paths = Self::process_env_var_list(include_paths, probe_result.include_paths);
376+
let enabled_features =
377+
Self::enabled_features_from_include_paths(&include_paths).ok_or("Could not get OpenCV config from include_paths")?;
378+
361379
Ok(Self {
362-
include_paths: Self::process_env_var_list(include_paths, probe_result.include_paths),
380+
include_paths,
363381
version: probe_result.version.unwrap_or_else(|| Version::new(0, 0, 0)),
382+
enabled_features,
364383
cargo_metadata,
365384
})
366385
}
@@ -414,9 +433,13 @@ impl Library {
414433
}
415434
cargo_metadata.extend(Self::process_link_libs(link_libs, vec![]));
416435

436+
let enabled_features =
437+
Self::enabled_features_from_include_paths(&include_paths).ok_or("Could not get OpenCV config from include_paths")?;
438+
417439
Ok(Self {
418440
include_paths,
419441
version: version.unwrap_or_else(|| Version::new(0, 0, 0)),
442+
enabled_features,
420443
cargo_metadata,
421444
})
422445
}

examples/opencl.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
use std::{env, time};
1+
use opencv::Result;
22

3-
use opencv::core::{Device, Size, UMat, Vector};
4-
use opencv::prelude::*;
5-
use opencv::{core, imgcodecs, imgproc, Result};
3+
#[cfg(ocvrs_has_inherent_feature_opencl)]
4+
fn main() -> Result<()> {
5+
use std::{env, time};
66

7-
opencv::not_opencv_branch_34! {
8-
use opencv::core::AccessFlag::ACCESS_READ;
9-
}
10-
opencv::opencv_branch_34! {
11-
use opencv::core::ACCESS_READ;
12-
}
7+
use opencv::core::{Device, Size, UMat, Vector};
8+
use opencv::prelude::*;
9+
use opencv::{core, imgcodecs, imgproc};
1310

14-
const ITERATIONS: usize = 100;
11+
opencv::not_opencv_branch_34! {
12+
use opencv::core::AccessFlag::ACCESS_READ;
13+
}
14+
opencv::opencv_branch_34! {
15+
use opencv::core::ACCESS_READ;
16+
}
1517

16-
fn main() -> Result<()> {
18+
const ITERATIONS: usize = 100;
1719
let img_file = env::args().nth(1).expect("Please supply image file name");
1820
let opencl_have = core::have_opencl()?;
1921
if opencl_have {
@@ -74,3 +76,10 @@ fn main() -> Result<()> {
7476
}
7577
Ok(())
7678
}
79+
80+
#[cfg(not(ocvrs_has_inherent_feature_opencl))]
81+
fn main() -> Result<()> {
82+
eprintln!("This example requires that OpenCV is build with OpenCL support:");
83+
eprintln!("{}", opencv::core::get_build_information()?);
84+
Ok(())
85+
}

src_cpp/core.hpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
#include "ocvrs_common.hpp"
22
#include <opencv2/core/affine.hpp>
3-
#include <opencv2/core/directx.hpp>
4-
#include <opencv2/core/ocl.hpp>
5-
#include <opencv2/core/va_intel.hpp>
3+
#ifdef HAVE_OPENCL
4+
#include <opencv2/core/ocl.hpp>
5+
// opengl.hpp, va_intel.hpp and directx.hpp unconditionally include ocl.hpp thus it needs to be within ifdef HAVE_OPENCL
6+
#ifdef HAVE_OPENGL
7+
#include <opencv2/core/opengl.hpp>
8+
#endif
9+
#include <opencv2/core/va_intel.hpp>
10+
#include <opencv2/core/directx.hpp>
11+
#endif
612
#include <opencv2/core/cuda.hpp>
7-
#include <opencv2/core/opengl.hpp>
8-
#include <opencv2/core/directx.hpp>
913
#if (CV_VERSION_MAJOR == 3 && CV_VERSION_MINOR == 4 && CV_VERSION_REVISION >= 4) /* 3.4.4+ */ \
1014
|| (CV_VERSION_MAJOR == 4) /* 4.0+ */ \
1115
|| (CV_VERSION_MAJOR == 5) /* 5.0+ */

0 commit comments

Comments
 (0)