Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions common/wasm_component_utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,40 @@ VALIDATE_WIT_ATTR_KWARGS = {
"doc": "Validate that the component exports match the WIT specification.",
}

# =============================================================================
# P3 Build Configuration
# =============================================================================

def get_p3_config(wasi_version):
"""Get P3-specific build configuration flags.

Returns a struct with flags that language rules should apply when
wasi_version = "p3". When wasi_version = "p2", all flags are empty/default.

Args:
wasi_version: "p2" or "p3"

Returns:
struct with:
is_p3: bool
wit_bindgen_async_args: list of --async flags for wit-bindgen
wasmtime_flags: list of flags for wasmtime invocations
wasi_cli_world: the WASI CLI world version string
"""
if wasi_version == "p3":
return struct(
is_p3 = True,
wit_bindgen_async_args = ["--async"],
wasmtime_flags = ["-Sp3", "-Wcomponent-model-async"],
wasi_cli_world = "wasi:cli/command@0.3.0",
)
return struct(
is_p3 = False,
wit_bindgen_async_args = [],
wasmtime_flags = [],
wasi_cli_world = "wasi:cli/command@0.2.0",
)

# =============================================================================
# WIT Info Normalization
# =============================================================================
Expand Down
87 changes: 71 additions & 16 deletions rust/private/rust_wasm_component_bindgen.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,56 @@ def _generate_wrapper_impl(ctx):
# verify this embedded runtime still provides the required API.
# Check generated bindings for any new runtime requirements.

# Validate CLI version compatibility
COMPATIBLE_CLI_VERSIONS = ["0.44.0", "0.45.0", "0.46.0", "0.47.0", "0.48.0", "0.48.1", "0.49.0"]
# P3 async mode: use wit-bindgen crate runtime instead of our hand-rolled stub
if ctx.attr.use_crate_runtime:
# When using the crate runtime, we don't define our own wit_bindgen::rt module.
# The wit-bindgen crate provides it. We just pass through the generated bindings.
wrapper_content = """// Generated wrapper for WIT bindings (P3 async mode)
// Runtime provided by wit-bindgen crate dependency — no embedded runtime.
#![allow(clippy::all)]
#![allow(unused_imports)]
#![allow(dead_code)]

// Generated bindings follow:
"""
temp_wrapper = ctx.actions.declare_file(ctx.label.name + "_wrapper.rs")
ctx.actions.write(
output = temp_wrapper,
content = wrapper_content + "\n",
)

# Use file_ops to concatenate wrapper + bindings
config_file = ctx.actions.declare_file(ctx.label.name + "_concat_config.json")
bindings_file = ctx.attr.bindgen.files.to_list()[0]
ctx.actions.write(
output = config_file,
content = json.encode({
"workspace_dir": ".",
"operations": [{
"type": "concatenate_files",
"src_paths": [temp_wrapper.path, bindings_file.path],
"dest_path": out_file.path,
}],
}),
)
file_ops_toolchain = ctx.toolchains["@rules_wasm_component//toolchains:file_ops_toolchain_type"]
ctx.actions.run(
executable = file_ops_toolchain.file_ops_component,
arguments = [config_file.path],
inputs = [config_file, temp_wrapper, bindings_file],
outputs = [out_file],
mnemonic = "ConcatCrateWrapper",
progress_message = "Generating crate-runtime wrapper for %s" % ctx.label,
)
return [DefaultInfo(files = depset([out_file]))]

# Validate CLI version compatibility for embedded runtime
COMPATIBLE_CLI_VERSIONS = ["0.44.0", "0.45.0", "0.46.0", "0.47.0", "0.48.0", "0.48.1", "0.49.0",
"0.50.0", "0.51.0", "0.53.1", "0.54.0"]
cli_version = get_tool_version("wit-bindgen")
if cli_version not in COMPATIBLE_CLI_VERSIONS:
fail(
"Embedded runtime incompatible with wit-bindgen CLI {}. " +
"Compatible versions: {}. " +
"Update the embedded runtime in rust_wasm_component_bindgen.bzl or downgrade CLI version.".format(
cli_version,
", ".join(COMPATIBLE_CLI_VERSIONS),
),
)
# buildifier: disable=print
print("WARNING: Embedded runtime not validated with wit-bindgen CLI {}".format(cli_version))

# Different wrapper content based on mode
if ctx.attr.mode == "native-guest":
Expand All @@ -105,7 +143,7 @@ def _generate_wrapper_impl(ctx):
#![allow(unused_imports)]
#![allow(dead_code)]

// Minimal wit_bindgen::rt runtime compatible with CLI-generated code
// Minimal wit_bindgen::rt runtime for native-guest mode (host compilation)
pub mod wit_bindgen {
pub mod rt {
use core::alloc::Layout;
Expand Down Expand Up @@ -359,6 +397,10 @@ _generate_wrapper = rule(
default = "guest",
doc = "Generation mode: 'guest' for WASM component, 'native-guest' for native application",
),
"use_crate_runtime": attr.bool(
default = False,
doc = "Use wit-bindgen crate runtime instead of embedded stub (required for P3 async)",
),
},
toolchains = ["@rules_wasm_component//toolchains:file_ops_toolchain_type"],
)
Expand Down Expand Up @@ -457,6 +499,10 @@ def rust_wasm_component_bindgen(
**kwargs: Additional arguments passed to rust_wasm_component
"""

# Determine P3 async settings from wasi_version
wasi_version = kwargs.get("wasi_version", "p2")
p3_async_interfaces = ["all"] if wasi_version == "p3" else []

# Generate WIT bindings based on symmetric flag
if symmetric:
# Symmetric mode: Generate symmetric bindings for both native and WASM from same source
Expand Down Expand Up @@ -484,6 +530,7 @@ def rust_wasm_component_bindgen(
wit = wit,
language = "rust",
generation_mode = "guest",
async_interfaces = p3_async_interfaces,
visibility = ["//visibility:private"],
)

Expand All @@ -493,41 +540,49 @@ def rust_wasm_component_bindgen(
wit = wit,
language = "rust",
generation_mode = "native-guest",
async_interfaces = p3_async_interfaces,
visibility = ["//visibility:private"],
)

# Create separate wrappers for guest and native-guest bindings
# Guest (WASM): use wit-bindgen crate runtime for full async support
# Native-guest (host): keep embedded runtime to avoid linker issues on non-WASM targets
wrapper_guest_target = name + "_wrapper_guest"
wrapper_native_guest_target = name + "_wrapper_native_guest"

_generate_wrapper(
name = wrapper_guest_target,
bindgen = ":" + bindgen_guest_target,
mode = "guest",
use_crate_runtime = True,
visibility = ["//visibility:private"],
)

_generate_wrapper(
name = wrapper_native_guest_target,
bindgen = ":" + bindgen_native_guest_target,
mode = "native-guest",
use_crate_runtime = False, # Host build uses embedded runtime
visibility = ["//visibility:private"],
)

# Create a rust_library from the generated bindings
bindings_lib = name + "_bindings"
bindings_lib_host = bindings_lib + "_host"

# WASM bindings: wit-bindgen crate for runtime + async_support
wasm_bindings_deps = [bitflags_dep, "@crates//:wit-bindgen"]
# Host bindings: embedded runtime only (no WASM-specific crate deps)
host_bindings_deps = [bitflags_dep]

# Create the bindings library for native platform (host) using native-guest wrapper
# The native-guest wrapper includes a no-op export! macro that does nothing,
# since native applications don't export WASM functions
rust_library(
name = bindings_lib_host,
srcs = [":" + wrapper_native_guest_target],
crate_name = name.replace("-", "_") + "_bindings",
edition = "2021",
deps = [bitflags_dep], # Required for WASI filesystem interfaces
visibility = visibility, # Make native bindings publicly available
deps = host_bindings_deps,
visibility = visibility,
)

# Create a separate WASM bindings library using guest wrapper
Expand All @@ -537,7 +592,7 @@ def rust_wasm_component_bindgen(
srcs = [":" + wrapper_guest_target],
crate_name = name.replace("-", "_") + "_bindings",
edition = "2021",
deps = [bitflags_dep], # Required for WASI filesystem interfaces
deps = wasm_bindings_deps,
visibility = ["//visibility:private"],
)

Expand Down
2 changes: 1 addition & 1 deletion test/p3/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ build_test(
)

# ============================================================================
# P3 build (experimental — validates attribute is accepted)
# P3 build (experimental — async bindings via wit-bindgen crate runtime)
# ============================================================================

rust_wasm_component_bindgen(
Expand Down
6 changes: 3 additions & 3 deletions test/p3/src_p3/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// P3 test component
// P3 test component — async interface
use hello_p3_bindings::exports::hello::interfaces::greeting::Guest;

struct Component;

impl Guest for Component {
fn greet(name: String) -> String {
format!("Hello, {}! (P3)", name)
async fn greet(name: String) -> String {
format!("Hello, {}! (P3 async)", name)
}
}

Expand Down
Loading
Loading