Skip to content

Commit bd89607

Browse files
author
Peter Huene
authored
Update wit-bindgen-rust code generation to support cargo component. (#394)
* wit-bindgen-rust: add optional prefix for calls from export macro. This commit adds an option for specifying a prefix to use for calls within the generated `export!` macro. It enables bindings to be generated into a submodule or crate and still have the exports to call the appropriate generated functions. * wit-bindgen-rust: add option to control generated export macro name. This commit adds an option to control the export macro name. * wit-bindgen-rust: force a reference to the component type section. This commit changes the generated `export!` macro to ensure a reference to the object containing the component type section. It is necessary to do this to prevent the linker from GC'ing the section's static when building the bindings into a separate crate.
1 parent c5f82a4 commit bd89607

4 files changed

Lines changed: 130 additions & 10 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ package-lock.json
44
node_modules
55
ace
66
*.wasm
7+
__pycache__

crates/gen-guest-rust/src/lib.rs

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ pub struct Opts {
4747
/// validation if it doesn't already have a `&str`.
4848
#[cfg_attr(feature = "clap", arg(long))]
4949
pub raw_strings: bool,
50+
51+
/// The prefix to use when calling functions from within the generated
52+
/// `export!` macro.
53+
///
54+
/// This enables the generated `export!` macro to reference code from
55+
/// another mod/crate.
56+
#[cfg_attr(feature = "clap", arg(long))]
57+
pub macro_call_prefix: Option<String>,
58+
59+
/// The name of the generated `export!` macro to use.
60+
///
61+
/// If `None`, the name is derived from the name of the world in the
62+
/// format `export_{world_name}!`.
63+
#[cfg_attr(feature = "clap", arg(long))]
64+
pub export_macro_name: Option<String>,
5065
}
5166

5267
impl Opts {
@@ -111,7 +126,11 @@ impl WorldGenerator for RustWasm {
111126

112127
fn finish(&mut self, name: &str, interfaces: &ComponentInterfaces, files: &mut Files) {
113128
if !self.exports.is_empty() {
114-
let snake = name.to_snake_case();
129+
let macro_name = if let Some(name) = self.opts.export_macro_name.as_ref() {
130+
name.to_snake_case()
131+
} else {
132+
format!("export_{}", name.to_snake_case())
133+
};
115134
let macro_export = if self.opts.macro_export {
116135
"#[macro_export]"
117136
} else {
@@ -123,9 +142,8 @@ impl WorldGenerator for RustWasm {
123142
/// Declares the export of the component's world for the
124143
/// given type.
125144
{macro_export}
126-
macro_rules! export_{snake}(($t:ident) => {{
145+
macro_rules! {macro_name}(($t:ident) => {{
127146
const _: () = {{
128-
129147
"
130148
);
131149
for src in self.exports.iter() {
@@ -135,12 +153,23 @@ impl WorldGenerator for RustWasm {
135153
self.src,
136154
"
137155
}};
156+
157+
#[used]
158+
#[doc(hidden)]
159+
#[cfg(target_arch = \"wasm32\")]
160+
static __FORCE_SECTION_REF: fn() = __force_section_ref;
161+
#[doc(hidden)]
162+
#[cfg(target_arch = \"wasm32\")]
163+
fn __force_section_ref() {{
164+
{prefix}__link_section()
165+
}}
138166
}});
139-
"
167+
",
168+
prefix = self.opts.macro_call_prefix.as_deref().unwrap_or("")
140169
);
141170
}
142171

143-
self.src.push_str("#[cfg(target_arch = \"wasm32\")]\n");
172+
self.src.push_str("\n#[cfg(target_arch = \"wasm32\")]\n");
144173

145174
// The custom section name here must start with "component-type" but
146175
// otherwise is attempted to be unique here to ensure that this doesn't get
@@ -167,6 +196,15 @@ impl WorldGenerator for RustWasm {
167196
));
168197
self.src.push_str(&format!("{:?};\n", component_type));
169198

199+
self.src.push_str(
200+
"
201+
#[inline(never)]
202+
#[doc(hidden)]
203+
#[cfg(target_arch = \"wasm32\")]
204+
pub fn __link_section() {}
205+
",
206+
);
207+
170208
let mut src = mem::take(&mut self.src);
171209
if self.opts.rustfmt {
172210
let mut child = Command::new("rustfmt")
@@ -322,8 +360,9 @@ impl InterfaceGenerator<'_> {
322360
uwrite!(
323361
macro_src,
324362
"
363+
#[doc(hidden)]
325364
#[export_name = \"{export_name}\"]
326-
unsafe extern \"C\" fn export_{iface_snake}_{name_snake}(\
365+
unsafe extern \"C\" fn __export_{iface_snake}_{name_snake}(\
327366
",
328367
);
329368

@@ -351,7 +390,11 @@ impl InterfaceGenerator<'_> {
351390

352391
// Finish out the macro-generated export implementation.
353392
macro_src.push_str(" {\n");
354-
uwrite!(macro_src, "{module_name}::call_{name_snake}::<$t>(");
393+
uwrite!(
394+
macro_src,
395+
"{prefix}{module_name}::call_{name_snake}::<$t>(",
396+
prefix = self.gen.opts.macro_call_prefix.as_deref().unwrap_or("")
397+
);
355398
for param in params.iter() {
356399
uwrite!(macro_src, "{param},");
357400
}
@@ -386,8 +429,9 @@ impl InterfaceGenerator<'_> {
386429
uwrite!(
387430
macro_src,
388431
"
432+
#[doc(hidden)]
389433
#[export_name = \"cabi_post_{export_name}\"]
390-
pub unsafe extern \"C\" fn post_return_{iface_snake}_{name_snake}(\
434+
unsafe extern \"C\" fn __post_return_{iface_snake}_{name_snake}(\
391435
"
392436
);
393437
let mut params = Vec::new();
@@ -401,7 +445,11 @@ impl InterfaceGenerator<'_> {
401445
macro_src.push_str(") {\n");
402446

403447
// Finish out the macro here
404-
uwrite!(macro_src, "{module_name}::post_return_{name_snake}::<$t>(");
448+
uwrite!(
449+
macro_src,
450+
"{prefix}{module_name}::post_return_{name_snake}::<$t>(",
451+
prefix = self.gen.opts.macro_call_prefix.as_deref().unwrap_or("")
452+
);
405453
for param in params.iter() {
406454
uwrite!(macro_src, "{param},");
407455
}

crates/gen-guest-rust/tests/codegen.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,57 @@ mod raw_strings {
120120
let _t: Vec<u8> = cat::bar();
121121
}
122122
}
123+
124+
// This is a static compilation test to ensure that
125+
// export bindings can go inside of another mod/crate
126+
// and still compile.
127+
mod prefix {
128+
mod bindings {
129+
wit_bindgen_guest_rust::generate!({
130+
export_str["exports1"]: "
131+
foo: func(x: string)
132+
bar: func() -> string
133+
",
134+
name: "baz",
135+
macro_call_prefix: "bindings::"
136+
});
137+
138+
pub(crate) use export_baz;
139+
}
140+
141+
struct Component;
142+
143+
impl bindings::exports1::Exports1 for Component {
144+
fn foo(x: String) {
145+
println!("foo: {}", x);
146+
}
147+
148+
fn bar() -> String {
149+
"bar".to_string()
150+
}
151+
}
152+
153+
bindings::export_baz!(Component);
154+
}
155+
156+
// This is a static compilation test to check that
157+
// the export macro name can be overridden.
158+
mod macro_name {
159+
wit_bindgen_guest_rust::generate!({
160+
export_str["exports2"]: "
161+
foo: func(x: string)
162+
",
163+
name: "baz",
164+
export_macro_name: "jam"
165+
});
166+
167+
struct Component;
168+
169+
impl exports2::Exports2 for Component {
170+
fn foo(x: String) {
171+
println!("foo: {}", x);
172+
}
173+
}
174+
175+
jam!(Component);
176+
}

crates/guest-rust-macro/src/lib.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use proc_macro::TokenStream;
2-
use syn::parse::{Parse, ParseStream, Result};
2+
use syn::{
3+
parse::{Parse, ParseStream, Result},
4+
LitStr, Token,
5+
};
36
use wit_bindgen_gen_guest_rust::Opts;
47

58
#[proc_macro]
@@ -11,12 +14,16 @@ mod kw {
1114
syn::custom_keyword!(unchecked);
1215
syn::custom_keyword!(no_std);
1316
syn::custom_keyword!(raw_strings);
17+
syn::custom_keyword!(macro_call_prefix);
18+
syn::custom_keyword!(export_macro_name);
1419
}
1520

1621
enum Opt {
1722
Unchecked,
1823
NoStd,
1924
RawStrings,
25+
MacroCallPrefix(LitStr),
26+
ExportMacroName(LitStr),
2027
}
2128

2229
impl Parse for Opt {
@@ -31,6 +38,14 @@ impl Parse for Opt {
3138
} else if l.peek(kw::raw_strings) {
3239
input.parse::<kw::raw_strings>()?;
3340
Ok(Opt::RawStrings)
41+
} else if l.peek(kw::macro_call_prefix) {
42+
input.parse::<kw::macro_call_prefix>()?;
43+
input.parse::<Token![:]>()?;
44+
Ok(Opt::MacroCallPrefix(input.parse()?))
45+
} else if l.peek(kw::export_macro_name) {
46+
input.parse::<kw::export_macro_name>()?;
47+
input.parse::<Token![:]>()?;
48+
Ok(Opt::ExportMacroName(input.parse()?))
3449
} else {
3550
Err(l.error())
3651
}
@@ -43,6 +58,8 @@ impl wit_bindgen_rust_macro_shared::Configure<Opts> for Opt {
4358
Opt::Unchecked => opts.unchecked = true,
4459
Opt::NoStd => opts.no_std = true,
4560
Opt::RawStrings => opts.raw_strings = true,
61+
Opt::MacroCallPrefix(prefix) => opts.macro_call_prefix = Some(prefix.value()),
62+
Opt::ExportMacroName(name) => opts.export_macro_name = Some(name.value()),
4663
}
4764
}
4865
}

0 commit comments

Comments
 (0)