Skip to content

Commit f1be022

Browse files
committed
feat: add rust_wasm_binary rule and comprehensive documentation
- Create new rust_wasm_binary rule for proper CLI WebAssembly components - Builds Rust applications with main() function as executable WASM components - Exports wasi:cli/[email protected] interface for execution with wasmtime - Fixes design flaw where rust_wasm_component built library components instead of CLI binaries - Add comprehensive documentation to Rust language guide and rules reference - Includes usage examples, attribute reference, and feature explanations - Enables hermetic CLI tool development with WebAssembly components
1 parent 99b960a commit f1be022

File tree

4 files changed

+211
-17
lines changed

4 files changed

+211
-17
lines changed

docs-site/src/content/docs/languages/rust.md

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Rust is the **ideal language** for WebAssembly components. Its zero-cost abstrac
2727

2828
## Choosing the Right Rule
2929

30-
rules_wasm_component provides **two Rust rules** for different use cases:
30+
rules_wasm_component provides **three Rust rules** for different use cases:
3131

3232
### `rust_wasm_component_bindgen` (Recommended)
3333

@@ -48,38 +48,59 @@ rust_wasm_component_bindgen(
4848
- Reusable component libraries
4949
- Standard component development workflows
5050

51-
### `rust_wasm_component` (Advanced)
51+
### `rust_wasm_binary` (CLI Applications)
5252

53-
**Use for CLI tools and utilities** - WASI-only components without custom interfaces.
53+
**Use for CLI tools and applications** - builds proper WASI CLI binaries that export `wasi:cli/command`.
5454

5555
```python
56-
rust_wasm_component(
56+
rust_wasm_binary(
5757
name = "my_cli_tool",
58-
srcs = ["src/main.rs"],
58+
srcs = ["src/main.rs"], # Must have main() function
5959
deps = ["@crates//:clap"],
60-
# No 'wit' - uses WASI capabilities only
60+
edition = "2021",
61+
)
62+
```
63+
64+
**Perfect for:**
65+
- Command-line applications with `main()` function
66+
- CLI tools executable via `wasmtime run`
67+
- Standalone applications that need CLI argument parsing
68+
- Hermetic tool replacements (like ssh-keygen, file processors, etc.)
69+
70+
### `rust_wasm_component` (Advanced)
71+
72+
**Use for library components** - WASI-only components without custom interfaces.
73+
74+
```python
75+
rust_wasm_component(
76+
name = "my_lib_component",
77+
srcs = ["src/lib.rs"], # Library with exports, no main()
78+
deps = ["@crates//:serde"],
6179
rustc_flags = ["-C", "opt-level=3"], # Custom compiler flags
6280
)
6381
```
6482

6583
**Perfect for:**
66-
- Command-line tools and utilities
84+
- Library components without custom WIT interfaces
6785
- WASI-only components (filesystem, stdio, etc.)
6886
- Custom build requirements and optimization
6987
- Converting existing WASM modules
7088

7189
### Rule Selection Guide
7290

73-
| Use Case | Rule | WIT Required | Exports Interfaces |
74-
|----------|------|--------------|-------------------|
75-
| **Component Library** | `rust_wasm_component_bindgen` | ✅ Yes | ✅ Yes |
76-
| **CLI Tool** | `rust_wasm_component` | ❌ No | ❌ No |
77-
| **Microservice** | `rust_wasm_component_bindgen` | ✅ Yes | ✅ Yes |
78-
| **File Processor** | `rust_wasm_component` | ❌ No | ❌ No |
79-
| **API Server** | `rust_wasm_component_bindgen` | ✅ Yes | ✅ Yes |
80-
| **Data Converter** | `rust_wasm_component` | ❌ No | ❌ No |
81-
82-
**Quick decision**: Do other components need to call your functions? Use `rust_wasm_component_bindgen`. Building a standalone tool? Use `rust_wasm_component`.
91+
| Use Case | Rule | Entry Point | Exports Interfaces | Executable |
92+
|----------|------|-------------|-------------------|------------|
93+
| **Component Library** | `rust_wasm_component_bindgen` | `lib.rs` | ✅ Yes | ❌ No |
94+
| **CLI Application** | `rust_wasm_binary` | `main.rs` | ❌ No | ✅ Yes |
95+
| **Microservice** | `rust_wasm_component_bindgen` | `lib.rs` | ✅ Yes | ❌ No |
96+
| **File Processor Tool** | `rust_wasm_binary` | `main.rs` | ❌ No | ✅ Yes |
97+
| **API Server** | `rust_wasm_component_bindgen` | `lib.rs` | ✅ Yes | ❌ No |
98+
| **Data Converter Library** | `rust_wasm_component` | `lib.rs` | ❌ No | ❌ No |
99+
100+
**Quick decisions**:
101+
- **Has `main()` function?** → Use `rust_wasm_binary`
102+
- **Other components call your functions?** → Use `rust_wasm_component_bindgen`
103+
- **Library without custom interfaces?** → Use `rust_wasm_component`
83104

84105
## Basic Component
85106

docs-site/src/content/docs/reference/rules.mdx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Complete reference documentation for all Bazel rules provided by rules_wasm_comp
2525
- [rust_wasm_component](#rust_wasm_component)
2626
- [rust_wasm_component_bindgen](#rust_wasm_component_bindgen)
2727
- [rust_wasm_component_test](#rust_wasm_component_test)
28+
- [rust_wasm_binary](#rust_wasm_binary)
2829
- [wasm_component_from_oci](#wasm_component_from_oci)
2930
- [wasm_component_new](#wasm_component_new)
3031
- [Composition Rules](#composition-rules)
@@ -423,6 +424,82 @@ rust_wasm_component_test(
423424
)
424425
```
425426

427+
### rust_wasm_binary
428+
429+
Builds a Rust WebAssembly CLI binary that exports `wasi:cli/command`. Compiles Rust applications with a `main()` function into executable WASM components that can be run with `wasmtime run`.
430+
431+
**Load from:**
432+
```python
433+
load("@rules_wasm_component//rust:defs.bzl", "rust_wasm_binary")
434+
```
435+
436+
**Attributes:**
437+
438+
| Name | Type | Required | Description |
439+
|------|------|----------|-------------|
440+
| `crate_features` | List of Strings || Rust crate features to enable |
441+
| `deps` | List of Labels || Rust dependencies (crates) |
442+
| `edition` | String<br/>*Default: '2021'* || Rust edition |
443+
| `name` | String || A unique name for this target |
444+
| `rustc_flags` | List of Strings || Additional rustc flags |
445+
| `srcs` | List of Labels || Rust source files (must include main.rs) |
446+
447+
**Examples:**
448+
449+
#### CLI tool with clap
450+
451+
Simple command-line tool using the clap argument parser
452+
453+
```python
454+
rust_wasm_binary(
455+
name = "my_cli_tool",
456+
srcs = ["src/main.rs"],
457+
deps = [
458+
"@crates//:clap",
459+
"@crates//:anyhow",
460+
],
461+
)
462+
```
463+
464+
#### File processor application
465+
466+
Application that processes files using WASI filesystem APIs
467+
468+
```python
469+
rust_wasm_binary(
470+
name = "file_processor",
471+
srcs = [
472+
"src/main.rs",
473+
"src/processor.rs",
474+
],
475+
deps = [
476+
"@crates//:serde",
477+
"@crates//:serde_json",
478+
],
479+
crate_features = ["async"],
480+
)
481+
```
482+
483+
**Usage:**
484+
485+
Run the compiled CLI binary with Wasmtime:
486+
487+
```bash
488+
# Build the component
489+
bazel build //my_package:my_cli_tool --platforms=//platforms:wasm32-wasip2
490+
491+
# Run with Wasmtime
492+
wasmtime run bazel-bin/my_package/my_cli_tool.wasm -- --help
493+
```
494+
495+
**Key Features:**
496+
497+
- **Proper CLI Interface**: Exports `wasi:cli/[email protected]` for execution with Wasmtime
498+
- **Main Function Support**: Works with standard Rust `main()` functions
499+
- **Full WASI Support**: Access to filesystem, environment variables, stdin/stdout
500+
- **Platform Transition**: Automatically builds for `wasm32-wasip2` target
501+
- **Hermetic Execution**: Self-contained binaries with no external dependencies
502+
426503
### wasm_component_from_oci
427504

428505
Downloads WebAssembly components from OCI registries. Enables using remote components from container registries in builds.

rust/defs.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ load(
2121
"//rust:rust_wasm_component_wizer.bzl",
2222
_rust_wasm_component_wizer = "rust_wasm_component_wizer",
2323
)
24+
load(
25+
"//rust:rust_wasm_binary.bzl",
26+
_rust_wasm_binary = "rust_wasm_binary",
27+
)
2428

2529
# Re-export public rules
2630
rust_wasm_component = _rust_wasm_component
@@ -29,3 +33,4 @@ rust_wasm_component_bindgen = _rust_wasm_component_bindgen
2933
rust_wasm_component_wizer = _rust_wasm_component_wizer
3034
rust_wasm_component_clippy = _rust_wasm_component_clippy
3135
rust_clippy_all = _rust_clippy_all
36+
rust_wasm_binary = _rust_wasm_binary

rust/rust_wasm_binary.bzl

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""Rust WASM binary rule for CLI components
2+
3+
This rule builds Rust binaries as WebAssembly CLI components that export
4+
wasi:cli/command interface, suitable for execution with wasmtime.
5+
"""
6+
7+
load("@rules_rust//rust:defs.bzl", "rust_binary")
8+
load(":transitions.bzl", "wasm_transition")
9+
10+
def _wasm_rust_binary_impl(ctx):
11+
"""Implementation that forwards a rust_binary with WASM transition applied"""
12+
target_info = ctx.attr.target[0]
13+
14+
# Forward the default info and any rust-specific providers
15+
providers = [target_info[DefaultInfo]]
16+
17+
# Forward RustInfo if available
18+
if hasattr(target_info, "rust_info"):
19+
providers.append(target_info.rust_info)
20+
21+
return providers
22+
23+
_wasm_rust_binary_rule = rule(
24+
implementation = _wasm_rust_binary_impl,
25+
attrs = {
26+
"target": attr.label(
27+
cfg = wasm_transition,
28+
doc = "rust_binary target to build for WASM",
29+
),
30+
"_allowlist_function_transition": attr.label(
31+
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
32+
),
33+
},
34+
executable = False, # This rule forwards files, doesn't create executables itself
35+
)
36+
37+
def rust_wasm_binary(
38+
name,
39+
srcs,
40+
deps = [],
41+
crate_features = [],
42+
rustc_flags = [],
43+
visibility = None,
44+
edition = "2021",
45+
**kwargs):
46+
"""
47+
Builds a Rust WebAssembly CLI binary component.
48+
49+
This macro creates a Rust binary compiled to wasm32-wasip2 that automatically
50+
exports the wasi:cli/command interface, making it executable via wasmtime.
51+
52+
Args:
53+
name: Target name
54+
srcs: Rust source files (must include main.rs)
55+
deps: Rust dependencies
56+
crate_features: Rust crate features to enable
57+
rustc_flags: Additional rustc flags
58+
visibility: Target visibility
59+
edition: Rust edition (default: "2021")
60+
**kwargs: Additional arguments passed to rust_binary
61+
62+
Example:
63+
rust_wasm_binary(
64+
name = "my_cli_tool",
65+
srcs = ["src/main.rs"],
66+
deps = [
67+
"@crates//:clap",
68+
"@crates//:anyhow",
69+
],
70+
)
71+
"""
72+
73+
# Build the host-platform rust_binary first
74+
host_binary_name = name + "_host"
75+
rust_binary(
76+
name = host_binary_name,
77+
srcs = srcs,
78+
deps = deps,
79+
edition = edition,
80+
crate_features = crate_features,
81+
rustc_flags = rustc_flags,
82+
visibility = ["//visibility:private"],
83+
**kwargs
84+
)
85+
86+
# Apply WASM transition to get actual WASM binary
87+
_wasm_rust_binary_rule(
88+
name = name,
89+
target = ":" + host_binary_name,
90+
visibility = visibility,
91+
)

0 commit comments

Comments
 (0)