From 4635f03b88b2b1ca271f15a2938b0985a68ea4e3 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Tue, 15 Mar 2022 09:33:16 -0400 Subject: [PATCH] Added the `uniffi_forward_scaffolding` macro This can be used to work around [rust-lang#50007](https://github.com/rust-lang/rust/issues/50007), which is getting to be more and more of an issue on the desktop JS project. --- docs/manual/src/tutorial/Rust_scaffolding.md | 33 +++++++++++++++++++ .../ui/interface_not_sync_and_send.stderr | 14 ++++---- .../templates/TopLevelFunctionTemplate.rs | 22 +++++++++++++ .../templates/scaffolding_template.rs | 2 -- 4 files changed, 62 insertions(+), 9 deletions(-) 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/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr b/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr index da55caa9c6..fb6f1d7e19 100644 --- a/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr +++ b/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr @@ -32,13 +32,13 @@ note: required by `lower` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Arc: FfiConverter` is not satisfied - --> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs - | - | as uniffi::FfiConverter>::lower(_arc) - | ^^^^ the trait `FfiConverter` is not implemented for `Arc` - | - = help: the following implementations were found: - as FfiConverter> + --> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs + | + | as uniffi::FfiConverter>::lower(_arc) + | ^^^^ the trait `FfiConverter` is not implemented for `Arc` + | + = help: the following implementations were found: + as FfiConverter> error[E0277]: the trait bound `Arc: FfiConverter` is not satisfied --> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs diff --git a/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs b/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs index f4736bf256..654b0b0e50 100644 --- a/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs +++ b/uniffi_bindgen/src/scaffolding/templates/TopLevelFunctionTemplate.rs @@ -1,3 +1,4 @@ +{%- for func in ci.iter_function_definitions() %} {# // For each top-level function declared in the UDL, we assume the caller has provided a corresponding // rust function of the same name. We provide a `pub extern "C"` wrapper that does type conversions to @@ -14,3 +15,24 @@ pub extern "C" fn {{ func.ffi_func().name() }}( uniffi::deps::log::debug!("{{ func.ffi_func().name() }}"); {% call rs::to_rs_function_call(func) %} } +{% endfor -%} + +// 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 func in ci.iter_function_definitions() %} + pub extern "C" fn {{ func.ffi_func().name() }}( + {%- call rs::arg_list_ffi_decl(func.ffi_func()) %} + ) {% call rs::return_signature(func) %} { + $crate::{{ func.ffi_func().name() }}({% for arg in func.arguments() %}{{ arg.name() }}, {% endfor %}callStatus) + } + {% endfor -%} + }; +} diff --git a/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs b/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs index d99d685b76..393ce4787e 100644 --- a/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs +++ b/uniffi_bindgen/src/scaffolding/templates/scaffolding_template.rs @@ -26,9 +26,7 @@ uniffi::assert_compatible_version!("{{ uniffi_version }}"); // Please check that {% endfor %} // Top level functions, corresponding to UDL `namespace` functions. -{%- for func in ci.iter_function_definitions() %} {% include "TopLevelFunctionTemplate.rs" %} -{% endfor -%} // Object definitions, corresponding to UDL `interface` definitions. {% for obj in ci.iter_object_definitions() %}