introduce "contrib" feature
MODIFY
Cargo.toml
Cargo.toml
@@ -44,6 +44,7 @@ force-3rd-party-libs-discovery = []
opencv-32 = []
opencv-34 = []
opencv-41 = []
+contrib = []
[package.metadata.docs.rs]
rustc-args = [ "--cfg feature=\"docs-only\"" ]

MODIFY
README.md
README.md
@@ -22,9 +22,11 @@ The following OpenCV versions are supported at the moment:
* 4.1
You can choose the target OpenCV version with the following Cargo features:
-* opencv-32
-* opencv-34 (default)
-* opencv-41
+* `opencv-32`
+* `opencv-34` (default)
+* `opencv-41`
+
+If you need support for `contrib` modules, also enable `contrib` feature.
### Quickstart
@@ -41,6 +43,11 @@ Select OpenCV version if different from default in Cargo.toml:
opencv = {version = "0.23", default-features = false, features = ["opencv-41"]}
```
+And enable usage of `contrib` modules:
+```toml
+opencv = {version = "0.23", default-features = false, features = ["opencv-41", "contrib"]}
+```
+
Import prelude
```rust
use opencv::prelude::*;
@@ -55,6 +62,18 @@ Generally you should use the latest stable rustc to compile this crate.
Currently development and testing of the crate is only performed on Linux, please feel
free to submit support for other platforms.
+### Features
+* `opencv-32` - build against OpenCV 3.2.0, this feature is aimed primarily on stable Debian and
+ Ubuntu users who can install OpenCV from the repository without having to compile it from the
+ source
+* `opencv-34` (default) - build against OpenCV 3.4.x
+* `opencv-41` - build against OpenCV 4.1.x
+* `contrib` - enable the usage of OpenCV contrib modules for corresponding OpenCV version
+* `buildtime-bindgen` - regenerate all bindings, should only be used during the crate development
+ or when building on Windows
+* `force-3rd-party-libs-discovery` - legacy feature that enables some additional logic for
+ discovery of dependent libs, should not be needed anymore
+
### Functionality
Generally the crate tries to only wrap OpenCV API and provide some convenience functions

MODIFY
build.rs
build.rs
@@ -4,7 +4,7 @@ use std::{
env,
ffi::OsString,
fs::{self, File, OpenOptions},
- io::{self, Write},
+ io::{self, BufRead, BufReader, Write},
iter::FromIterator,
path::{self, PathBuf},
process::Command,
@@ -19,6 +19,7 @@ use which_crate::which;
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
+static CORE_MODULES: OnceCell<HashSet<&'static str>> = OnceCell::new();
static MODULES: OnceCell<Vec<(String, Vec<PathBuf>)>> = OnceCell::new();
trait CompilerFlagSetter {
@@ -59,6 +60,42 @@ fn get_versioned_hub_dir() -> PathBuf {
hub_dir
}
+fn is_core_module(module: &str) -> bool {
+ let core_modules = match CORE_MODULES.get() {
+ None => {
+ CORE_MODULES.set(HashSet::from_iter([
+ "calib3d",
+ "core",
+ #[cfg(not(feature = "opencv-32"))]
+ "dnn",
+ "features2d",
+ "flann",
+ #[cfg(feature = "opencv-41")]
+ "gapi",
+ "highgui",
+ "imgcodecs",
+ "imgproc",
+ "ml",
+ "objdetect",
+ "photo",
+ #[cfg(any(feature = "opencv-32", feature = "opencv-34"))]
+ "shape",
+ "stitching",
+ #[cfg(any(feature = "opencv-32", feature = "opencv-34"))]
+ "superres",
+ "video",
+ "videoio",
+ #[cfg(any(feature = "opencv-32", feature = "opencv-34"))]
+ "videostab",
+ "viz",
+ ].iter().map(|x| *x))).expect("Cannot set CORE_MODULES cache");
+ CORE_MODULES.get().unwrap()
+ },
+ Some(modules) => modules,
+ };
+ core_modules.contains(module)
+}
+
fn get_modules(opencv_dir_as_string: &str) -> Result<&'static Vec<(String, Vec<PathBuf>)>> {
if let Some(modules) = MODULES.get() {
return Ok(modules);
@@ -167,6 +204,17 @@ fn get_modules(opencv_dir_as_string: &str) -> Result<&'static Vec<(String, Vec<P
Ok(MODULES.get().unwrap())
}
+fn copy_indent(mut read: impl BufRead, mut write: impl Write, level: usize, indent: &str) -> Result<()> {
+ let full_indent = indent.repeat(level);
+ let mut line = Vec::with_capacity(100);
+ while read.read_until(b'\n', &mut line)? != 0 {
+ write.write(full_indent.as_bytes())?;
+ write.write(&line)?;
+ line.clear();
+ }
+ Ok(())
+}
+
fn build_pkg_config_args((opencv, pkg_name): (&pkg_config::Library, &str)) -> Vec<String> {
let mut out = Vec::with_capacity(60);
out.extend(opencv.link_paths.iter().map(|x| format!("-L{}", x.to_string_lossy())));
@@ -467,46 +515,77 @@ fn gen_wrapper((opencv, pkg_name): (&pkg_config::Library, &str), opencv_header_d
{
let mut hub = File::create(hub_dir.join("hub.rs"))?;
- for (module, ..) in modules {
- writeln!(&mut hub, "pub mod {};", module)?;
- let module_filename = format!("{}.rs", module);
- let target_file = module_dir.join(&module_filename);
- fs::rename(out_dir.join(&module_filename), &target_file)?;
- let mut f = OpenOptions::new().append(true).open(&target_file)?;
- add_manual(&mut f, &module)?;
- }
- writeln!(&mut hub, "pub mod types;")?;
- writeln!(&mut hub, "#[doc(hidden)]")?;
- writeln!(&mut hub, "pub mod sys;")?;
- }
- {
let mut types = File::create(module_dir.join("types.rs"))?;
writeln!(&mut types, "use std::os::raw::{{c_char, c_void}};")?;
writeln!(&mut types, "use libc::size_t;")?;
writeln!(&mut types, "use crate::{{core, types, sys, Result}};")?;
writeln!(&mut types, "")?;
- for entry in glob(&format!("{}/*.type.rs", out_dir_as_str))? {
- let entry = entry?;
- io::copy(&mut File::open(&entry)?, &mut types)?;
- }
- add_manual(&mut types, "types")?;
- }
- {
let mut sys = File::create(module_dir.join("sys.rs"))?;
writeln!(&mut sys, "use std::os::raw::{{c_char, c_void}};")?;
writeln!(&mut sys, "use libc::{{ptrdiff_t, size_t}};")?;
writeln!(&mut sys, "use crate::core;")?;
writeln!(&mut sys, "")?;
- for entry in glob(&format!("{}/*.rv.rs", out_dir_as_str))? {
+ for (module, ..) in modules {
+ let is_contrib_module = !is_core_module(module);
+ let write_if_contrib = |write: &mut File| -> Result<()> {
+ if is_contrib_module {
+ writeln!(write, r#"#[cfg(feature = "contrib")]"#)?;
+ }
+ Ok(())
+ };
+ // hub
+ write_if_contrib(&mut hub)?;
+ writeln!(&mut hub, "pub mod {};", module)?;
+ let module_filename = format!("{}.rs", module);
+ let target_file = module_dir.join(&module_filename);
+ fs::rename(out_dir.join(&module_filename), &target_file)?;
+ let mut f = OpenOptions::new().append(true).open(&target_file)?;
+ add_manual(&mut f, module)?;
+
+ // types
+ let mut write_header = true;
+ for entry in glob(&format!("{}/{}-*.type.rs", out_dir_as_str, module))? {
let entry = entry?;
- io::copy(&mut File::open(&entry)?, &mut sys)?;
+ if write_header {
+ write_if_contrib(&mut types)?;
+ writeln!(&mut types, "mod {}_types {{", module)?;
+ writeln!(&mut types, " use super::*;")?;
+ writeln!(&mut types, "")?;
+ write_header = false;
}
- for (module, ..) in modules {
+ copy_indent(BufReader::new(File::open(&entry)?), &mut types, 1, " ")?;
+ }
+ if !write_header {
+ writeln!(&mut types, "}}")?;
+ write_if_contrib(&mut types)?;
+ writeln!(&mut types, "pub use {}_types::*;", module)?;
+ writeln!(&mut types, "")?;
+ }
+
+ // sys
let path = out_dir.join(format!("{}.externs.rs", module));
- io::copy(&mut File::open(&path)?, &mut sys)?;
+ write_if_contrib(&mut sys)?;
+ writeln!(&mut sys, "mod {}_sys {{", module)?;
+ writeln!(&mut sys, " use super::*;")?;
+ writeln!(&mut sys, "")?;
+ for entry in glob(&format!("{}/{}-*.rv.rs", out_dir_as_str, module))? {
+ let entry: PathBuf = entry?;
+ copy_indent(BufReader::new(File::open(entry)?), &mut sys, 1, " ")?;
+ }
+ copy_indent(BufReader::new(File::open(&path)?), &mut sys, 1, " ")?;
+ writeln!(&mut sys, "}}")?;
+ write_if_contrib(&mut sys)?;
+ writeln!(&mut sys, "pub use {}_sys::*;", module)?;
+ writeln!(&mut sys, "")?;
}
+ writeln!(&mut hub, "pub mod types;")?;
+ writeln!(&mut hub, "#[doc(hidden)]")?;
+ writeln!(&mut hub, "pub mod sys;")?;
+
+ add_manual(&mut types, "types")?;
+
add_manual(&mut sys, "sys")?;
}

MODIFY
gen_rust.py
gen_rust.py
@@ -1944,8 +1944,13 @@ class TypeInfo(object):
"{}/{}.type.h".format(cpp_dir, template_vars["return_wrapper_type"]),
lambda f: f.write(self.base_templates["cpp_void" if self.cpp_extern == "void" else "cpp_non_void"].substitute(template_vars))
)
+ if self.rust_extern in ("*mut c_void", "*const c_void"):
+ # otherwise it leads to duplication of type definitions
+ rust_module = "core"
+ else:
+ rust_module = self.rust_module()
write_exc(
- "{}/{}.rv.rs".format(rust_dir, template_vars["return_wrapper_type"]),
+ "{}/{}-{}.rv.rs".format(rust_dir, rust_module, template_vars["return_wrapper_type"]),
lambda f: f.write(self.base_templates["rust_void" if self.cpp_extern == "void" else "rust_non_void"].substitute(template_vars))
)
@@ -1964,6 +1969,16 @@ class TypeInfo(object):
out += "Trait"
return out
+ def rust_module(self):
+ if self.inner is not None:
+ if self.inner.inner is not None and self.inner.inner.ci is not None:
+ return self.inner.inner.ci.module
+ elif self.inner.ci is not None:
+ return self.inner.ci.module
+ elif self.ci is not None:
+ return self.ci.module
+ return "core"
+
def rust_generic_decl(self):
return ""
@@ -2889,7 +2904,7 @@ class VectorTypeInfo(TypeInfo):
impls += VectorTypeInfo.TEMPLATES["input_output_array"].substitute(template_vars)
self.inner.gen_return_wrappers(self.gen.cpp_dir, self.gen.rust_dir)
- write_exc("{}/{}.type.rs".format(self.gen.rust_dir, self.rust_safe_id), lambda f: f.write(VectorTypeInfo.TEMPLATES["rust_common"].substitute(combine_dicts(template_vars, {
+ write_exc("{}/{}-{}.type.rs".format(self.gen.rust_dir, self.rust_module(), self.rust_safe_id), lambda f: f.write(VectorTypeInfo.TEMPLATES["rust_common"].substitute(combine_dicts(template_vars, {
"vector_methods": indent(vector_methods),
"inherent_methods": indent(inherent_methods),
"impls": impls,
@@ -2972,7 +2987,7 @@ class SmartPtrTypeInfo(TypeInfo):
"base_cpp_type": cibase.cpptype,
})))
- write_exc("{}/{}.type.rs".format(self.gen.rust_dir, self.rust_safe_id), write_rust)
+ write_exc("{}/{}-{}.type.rs".format(self.gen.rust_dir, self.rust_module(), self.rust_safe_id), write_rust)
def __str__(self):
return "SmartPtr[%s]" % (self.inner)

MODIFY
src/manual/mod.rs
src/manual/mod.rs
pub mod core;
+#[cfg(any(not(feature = "opencv-32"), feature = "contrib"))]
pub mod dnn;
pub mod sys;
pub mod types;

introduce "contrib" feature