diff --git a/docs/manual/src/tutorial/Rust_scaffolding.md b/docs/manual/src/tutorial/Rust_scaffolding.md index 4d0d1fbc54..507ceb3f17 100644 --- a/docs/manual/src/tutorial/Rust_scaffolding.md +++ b/docs/manual/src/tutorial/Rust_scaffolding.md @@ -108,3 +108,36 @@ uniffi_build = { path = "path/to/uniffi-rs/uniffi_build, features=["builtin-bind Note that `path/to/uniffi-rs` should be the path to the root of the `uniffi` source tree - ie, the 2 path specs above point to different sub-directories under the `uniffi` root. + +### Libraries that depend on UniFFI components + +Suppose you want to create a shared library that includes one or more +components using UniFFI. The typical way to achieve this is to create a new +crate that depends on the component crates. However, this can run into +[rust-lang#50007](https://github.com/rust-lang/rust/issues/50007). Under +certain circumstances, the scaffolding functions that the component crates +export do not get re-exported by the dependent crate. + +Use the `uniffi_forward_scaffolding!` macro to work around this issue. If your +library depends on `foo_component`, then add +`foo_component::uniffi_forward_scaffolding!();` to your `lib.rs` file. This +will generate new exported functions that forward to the scaffolding functions +in the dependent crate. The generated code will look something like this: + +```rust +#[doc(hidden)] +#[no_mangle] +pub extern "C" fn foo_component_eb69_some_function( + arg1: i64, + arg2: uniffi::RustBuffer, + call_status: &mut uniffi::RustCallStatus +) -> f64 { + foo_component::foo_component_eb69_some_function(arg1, arg2) +} + +// ... repeat for each scaffolding function +``` + +Note that each scaffolding function contains a hash that's derived from the UDL +file. This avoids name collisions when combining multiple UniFFI components +into one library. diff --git a/uniffi_bindgen/src/scaffolding/templates/ForwardUniFFIScaffolding.rs b/uniffi_bindgen/src/scaffolding/templates/ForwardUniFFIScaffolding.rs new file mode 100644 index 0000000000..f55ad758a1 --- /dev/null +++ b/uniffi_bindgen/src/scaffolding/templates/ForwardUniFFIScaffolding.rs @@ -0,0 +1,19 @@ +// Create a macro that can be used by a dependent create to forward all of the scaffolding +// functions defined here. This is used in 2 ways: +// +// - Building libraries that contain multiple UniFFI components (megazord, libxul). The combined +// library can depend on each component then call uniffi_forward_scaffolding!() for each one. +// - Test fixtures. Each binding generator needs to test against the test fixtures. To allow +// this, they call uniffi_forward_scaffolding!() for the test fixture in their test module. +#[macro_export] +macro_rules! uniffi_forward_scaffolding { + () => { + {%- for ffi_func in ci.iter_ffi_function_definitions() %} + pub extern "C" fn {{ ffi_func.name() }}( + {%- call rs::arg_list_ffi_decl(ffi_func) %} + ) {% call rs::return_signature_ffi(ffi_func) %} { + $crate::{{ ffi_func.name() }}({% for arg in ffi_func.arguments() %}{{ arg.name() }}, {% endfor %}callStatus) + } + {% endfor -%} + }; +} diff --git a/uniffi_bindgen/src/scaffolding/templates/macros.rs b/uniffi_bindgen/src/scaffolding/templates/macros.rs index 983ac4c8ee..e45c6b03e6 100644 --- a/uniffi_bindgen/src/scaffolding/templates/macros.rs +++ b/uniffi_bindgen/src/scaffolding/templates/macros.rs @@ -50,9 +50,9 @@ {%- endif %} {%- endmacro -%} -{% macro return_signature(func) %}{% match func.ffi_func().return_type() %}{% when Some with (return_type) %} -> {% call return_type_func(func) %}{%- else -%}{%- endmatch -%}{%- endmacro -%} +{% macro return_signature(func) %}{% call return_signature_ffi(func.ffi_func()) %}{% endmacro %} -{% macro return_type_func(func) %}{% match func.ffi_func().return_type() %}{% when Some with (return_type) %}{{ return_type|type_ffi }}{%- else -%}(){%- endmatch -%}{%- endmacro -%} +{% macro return_signature_ffi(ffi_func) %}{% match ffi_func.return_type() %}{% when Some with (return_type) %} -> {{ return_type|type_ffi }}{%- else -%}{%- endmatch -%}{%- endmacro -%} {% macro ret(func) %}{% match func.return_type() %}{% when Some with (return_type) %}{{ return_type|ffi_converter }}::lower(_retval){% else %}_retval{% endmatch %}{% endmacro %} diff --git a/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs b/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs index d99d685b76..d80fbaf271 100644 --- a/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs +++ b/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs @@ -43,4 +43,7 @@ uniffi::assert_compatible_version!("{{ uniffi_version }}"); // Please check that // External and Wrapped types {% include "ExternalTypesTemplate.rs" %} +// The `forward_uniffi_scaffolding` macro +{% include "ForwardUniFFIScaffolding.rs" %} + {%- import "macros.rs" as rs -%}