Skip to content

Commit

Permalink
Added the uniffi_forward_scaffolding macro
Browse files Browse the repository at this point in the history
This can be used to work around
[rust-lang#50007](rust-lang/rust#50007), which
is getting to be more and more of an issue on the desktop JS project.
  • Loading branch information
bendk committed Mar 28, 2022
1 parent 12fb0cd commit 4635f03
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 9 deletions.
33 changes: 33 additions & 0 deletions docs/manual/src/tutorial/Rust_scaffolding.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
14 changes: 7 additions & 7 deletions fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ note: required by `lower`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `Arc<Counter>: FfiConverter` is not satisfied
--> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs
|
| <std::sync::Arc<Counter> as uniffi::FfiConverter>::lower(_arc)
| ^^^^ the trait `FfiConverter` is not implemented for `Arc<Counter>`
|
= help: the following implementations were found:
<Arc<T> as FfiConverter>
--> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs
|
| <std::sync::Arc<Counter> as uniffi::FfiConverter>::lower(_arc)
| ^^^^ the trait `FfiConverter` is not implemented for `Arc<Counter>`
|
= help: the following implementations were found:
<Arc<T> as FfiConverter>

error[E0277]: the trait bound `Arc<Counter>: FfiConverter` is not satisfied
--> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 -%}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() %}
Expand Down

0 comments on commit 4635f03

Please sign in to comment.