Skip to content

Commit

Permalink
Add a suffix to rust functions exported to C++ side to avoid duplicat…
Browse files Browse the repository at this point in the history
…e link symbols
  • Loading branch information
twistedfall committed Sep 19, 2024
1 parent c2feda9 commit 6accf68
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 19 deletions.
15 changes: 9 additions & 6 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ fn make_modules(opencv_dir: &Path) -> Result<()> {
Ok(())
}

fn build_compiler(opencv: &Library) -> cc::Build {
fn build_compiler(opencv: &Library, ffi_export_suffix: &str) -> cc::Build {
let mut out = cc::Build::new();
out.cpp(true)
.std("c++14") // clang says error: 'auto' return without trailing return type; deduced return types are a C++14 extension
Expand Down Expand Up @@ -227,6 +227,7 @@ fn build_compiler(opencv: &Library) -> cc::Build {
} else {
out.flag_if_supported("-Wa,-mbig-obj");
}
out.define("OCVRS_FFI_EXPORT_SUFFIX", ffi_export_suffix);
out
}

Expand All @@ -247,8 +248,7 @@ fn setup_rerun() -> Result<()> {
Ok(())
}

fn build_wrapper(opencv: &Library) {
let mut cc = build_compiler(opencv);
fn build_wrapper(mut cc: cc::Build) {
eprintln!("=== Compiler information: {:#?}", cc.get_compiler());
let modules = MODULES.get().expect("MODULES not initialized");
static SUPPORTED_MODULES: [&str; 67] = [
Expand Down Expand Up @@ -347,7 +347,8 @@ fn main() -> Result<()> {
return Ok(());
}

eprintln!("=== Crate version: {:?}", env::var_os("CARGO_PKG_VERSION"));
let pkg_version = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "unknown_crate_version".to_string());
eprintln!("=== Crate version: {pkg_version}");
eprintln!("=== Environment configuration:");
for v in AFFECTING_ENV_VARS.into_iter().chain(DEBUG_ENV_VARS) {
eprintln!("=== {v} = {:?}", env::var_os(v));
Expand Down Expand Up @@ -421,9 +422,11 @@ fn main() -> Result<()> {

setup_rerun()?;

let ffi_export_suffix = format!("_{}", pkg_version.replace(".", "_"));
let binding_generator = BindingGenerator::new(build_script_path);
binding_generator.generate_wrapper(opencv_header_dir, &opencv)?;
build_wrapper(&opencv);
binding_generator.generate_wrapper(opencv_header_dir, &opencv, &ffi_export_suffix)?;
let cc = build_compiler(&opencv, &ffi_export_suffix);
build_wrapper(cc);
// -l linker args should be emitted after -l static
opencv.emit_cargo_metadata();
Ok(())
Expand Down
5 changes: 2 additions & 3 deletions build/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl BindingGenerator {
Self { build_script_path }
}

pub fn generate_wrapper(&self, opencv_header_dir: &Path, opencv: &Library) -> Result<()> {
pub fn generate_wrapper(&self, opencv_header_dir: &Path, opencv: &Library, ffi_export_suffix: &str) -> Result<()> {
let target_docs_dir = env::var_os("OCVRS_DOCS_GENERATE_DIR").map(PathBuf::from);
let target_module_dir = OUT_DIR.join("opencv");
let manual_dir = SRC_DIR.join("manual");
Expand All @@ -48,8 +48,7 @@ impl BindingGenerator {

self.run(modules, opencv_header_dir, opencv)?;

let collector = Collector::new(modules, &target_module_dir, &manual_dir, &OUT_DIR);
collector.collect_bindings()?;
Collector::new(modules, &ffi_export_suffix, &target_module_dir, &manual_dir, &OUT_DIR).collect_bindings()?;

if let Some(target_docs_dir) = target_docs_dir {
if !target_docs_dir.exists() {
Expand Down
35 changes: 34 additions & 1 deletion build/generator/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@ use super::super::{files_with_extension, Result};

pub struct Collector<'r> {
modules: &'r [String],
ffi_export_suffix: &'r str,
target_module_dir: &'r Path,
manual_dir: &'r Path,
out_dir: &'r Path,
}

impl<'r> Collector<'r> {
pub fn new(modules: &'r [String], target_module_dir: &'r Path, manual_dir: &'r Path, out_dir: &'r Path) -> Self {
pub fn new(
modules: &'r [String],
ffi_export_suffix: &'r str,
target_module_dir: &'r Path,
manual_dir: &'r Path,
out_dir: &'r Path,
) -> Self {
Self {
modules,
ffi_export_suffix,
target_module_dir,
manual_dir,
out_dir,
Expand Down Expand Up @@ -80,6 +88,7 @@ impl<'r> Collector<'r> {
writeln!(hub_rs, "\tpub use super::{module}::prelude::*;")?;
}
writeln!(hub_rs, "}}")?;
self.inject_ffi_exports(&mut hub_rs)?;
eprintln!("=== Total binding collection time: {:?}", start.elapsed());
Ok(())
}
Expand Down Expand Up @@ -172,6 +181,30 @@ impl<'r> Collector<'r> {
Ok(())
}

/// The #no_mangle function in the bindings cause duplicate export names when 2 different version of the crate are used
/// (https://github.com/twistedfall/opencv-rust/issues/597). This function injects the version of the exported functions with
/// a crate version suffix to avoid this conflict. On the C++ side it works with the help of the `OCVRS_FFI_EXPORT_SUFFIX`
/// macro which is passed in `build_compiler()`.
fn inject_ffi_exports(&self, hub_rs: &mut impl Write) -> Result<()> {
writeln!(hub_rs, "\nmod ffi_exports {{")?;
writeln!(hub_rs, "\tuse crate::mod_prelude_sys::*;")?;
write!(hub_rs, "\t")?;
writeln!(
hub_rs,
r#"#[no_mangle] unsafe extern "C" fn ocvrs_create_string{}(s: *const c_char) -> *mut String {{ crate::templ::ocvrs_create_string(s) }}"#,
self.ffi_export_suffix
)?;
write!(hub_rs, "\t")?;
writeln!(
hub_rs,
r#"#[no_mangle] unsafe extern "C" fn ocvrs_create_byte_string{}(v: *const u8, len: size_t) -> *mut Vec<u8> {{ crate::templ::ocvrs_create_byte_string(v, len) }}"#,
self.ffi_export_suffix
)?;
writeln!(hub_rs, "}}")?;

Ok(())
}

fn write_use_manual(&self, file: &mut BufWriter<File>, module: &str) -> Result<bool> {
if self.manual_dir.join(format!("{module}.rs")).exists() {
writeln!(file, "pub use crate::manual::{module}::*;")?;
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ pub mod platform_types {
}

/// Prelude for sys (externs) module and types
pub(crate) mod mod_prelude_sys {
pub mod mod_prelude_sys {
pub use std::ffi::{c_char, c_void};

pub use crate::platform_types::*;
pub use crate::traits::{Boxed, OpenCVFromExtern, OpenCVIntoExternContainer, OpenCVTypeExternContainer};
}

/// Prelude for generated modules and types
pub(crate) mod mod_prelude {
pub mod mod_prelude {
pub use crate::boxed_ref::{BoxedRef, BoxedRefMut};
pub use crate::core::{ToInputArray, ToInputOutputArray, ToOutputArray};
pub use crate::hub_prelude::*;
Expand Down
8 changes: 4 additions & 4 deletions src/templ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,15 @@ macro_rules! return_receive {
}

/// The return type of this function goes into `receive_string`
#[export_name = "ocvrs_create_string"]
unsafe extern "C" fn ocvrs_create_string(s: *const c_char) -> *mut String {
#[inline]
pub unsafe fn ocvrs_create_string(s: *const c_char) -> *mut String {
let s = CStr::from_ptr(s).to_string_lossy().into_owned();
Box::into_raw(Box::new(s))
}

/// The return type of this function goes into `receive_byte_string`
#[export_name = "ocvrs_create_byte_string"]
unsafe extern "C" fn ocvrs_create_byte_string(v: *const u8, len: size_t) -> *mut Vec<u8> {
#[inline]
pub unsafe fn ocvrs_create_byte_string(v: *const u8, len: size_t) -> *mut Vec<u8> {
let byte_slice = if v.is_null() {
&[]
} else {
Expand Down
12 changes: 9 additions & 3 deletions src_cpp/ocvrs_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#ifdef OCVRS_PARSING_HEADERS
#define CV_DNN_DONT_ADD_EXPERIMENTAL_NS
#define CV_DNN_DONT_ADD_INLINE_NS
// the FFI export suffix only matters during actual linking
#define OCVRS_FFI_EXPORT_SUFFIX ""
#endif

#include <memory>
Expand All @@ -29,9 +31,13 @@ catch (cv::Exception& e) { \
OCVRS_HANDLE(cv::Error::StsError, "Unspecified error, neither from OpenCV nor from std", return_name); \
}

// defined in src/templ.rs
extern "C" void* ocvrs_create_string(const char*);
extern "C" void* ocvrs_create_byte_string(const char*, size_t);
// double-expansion stringification macro trick
#define STRINGIFY1(x) #x
#define STRINGIFY(x) STRINGIFY1(x)

// defined in build/generator/collector.rs Collector::inject_ffi_exports, `__asm` is used to rename imported function
extern "C" void* ocvrs_create_string(const char*) __asm ("ocvrs_create_string" STRINGIFY(OCVRS_FFI_EXPORT_SUFFIX));
extern "C" void* ocvrs_create_byte_string(const char*, size_t) __asm ("ocvrs_create_byte_string" STRINGIFY(OCVRS_FFI_EXPORT_SUFFIX));

template<typename T> struct Result {
int error_code;
Expand Down

0 comments on commit 6accf68

Please sign in to comment.