Skip to content

Commit 4b248ce

Browse files
committed
fix: correct Rust example directory paths in CI
Replace non-existent //examples/rust_component/... with actual Rust example directories: - //examples/basic/... (contains Rust WebAssembly component) - //examples/simple_module/... (Rust module example) - //examples/cli_tool_example/... (Rust CLI tool example) This fixes the CI error 'no targets found beneath examples/rust_component' and allows proper validation of our working hermetic Go toolchain alongside the existing Rust components.
1 parent 9420da1 commit 4b248ce

File tree

7 files changed

+448
-2
lines changed

7 files changed

+448
-2
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,10 @@ jobs:
105105
# Using explicit inclusions to avoid JavaScript component issues
106106
bazel build -- \
107107
//examples/go_component/... \
108-
//examples/rust_component/... \
109-
//examples/cpp_component/... \
110108
//examples/basic/... \
109+
//examples/simple_module/... \
110+
//examples/cli_tool_example/... \
111+
//examples/cpp_component/... \
111112
//rust/... \
112113
//go/... \
113114
//cpp/... \

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,62 @@ Rust is the **ideal language** for WebAssembly components. Its zero-cost abstrac
2525
- **rules_rust Integration** - Leverages existing Bazel Rust ecosystem
2626
- **Incremental Builds** - Fast iteration with Bazel caching
2727

28+
## Choosing the Right Rule
29+
30+
rules_wasm_component provides **two Rust rules** for different use cases:
31+
32+
### `rust_wasm_component_bindgen` (Recommended)
33+
34+
**Use for most component development** - exports custom interfaces for other components to use.
35+
36+
```python
37+
rust_wasm_component_bindgen(
38+
name = "my_component",
39+
srcs = ["src/lib.rs"],
40+
wit = ":my_interfaces", # WIT interfaces automatically generate bindings
41+
profiles = ["release"], # Simple configuration
42+
)
43+
```
44+
45+
**Perfect for:**
46+
- Components with custom WIT interfaces
47+
- Inter-component communication
48+
- Reusable component libraries
49+
- Standard component development workflows
50+
51+
### `rust_wasm_component` (Advanced)
52+
53+
**Use for CLI tools and utilities** - WASI-only components without custom interfaces.
54+
55+
```python
56+
rust_wasm_component(
57+
name = "my_cli_tool",
58+
srcs = ["src/main.rs"],
59+
deps = ["@crates//:clap"],
60+
# No 'wit' - uses WASI capabilities only
61+
rustc_flags = ["-C", "opt-level=3"], # Custom compiler flags
62+
)
63+
```
64+
65+
**Perfect for:**
66+
- Command-line tools and utilities
67+
- WASI-only components (filesystem, stdio, etc.)
68+
- Custom build requirements and optimization
69+
- Converting existing WASM modules
70+
71+
### Rule Selection Guide
72+
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`.
83+
2884
## Basic Component
2985

3086
Let's build a calculator component to demonstrate the core concepts. This example shows how to:
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""Example of using rust_wasm_component for CLI tools without custom WIT interfaces"""
2+
3+
load("@rules_wasm_component//rust:defs.bzl", "rust_wasm_component", "rust_wasm_component_bindgen")
4+
load("@rules_wasm_component//wit:defs.bzl", "wit_library")
5+
6+
package(default_visibility = ["//visibility:public"])
7+
8+
# Example 1: CLI Tool using rust_wasm_component (no custom WIT interfaces)
9+
# Use case: Command-line utilities that only use WASI capabilities
10+
rust_wasm_component(
11+
name = "file_processor_cli",
12+
srcs = ["src/cli_tool.rs"],
13+
deps = [
14+
"@crates//:clap", # CLI argument parsing
15+
"@crates//:anyhow", # Error handling
16+
"@crates//:serde_json", # JSON processing
17+
],
18+
# This component doesn't need custom WIT - uses standard WASI only
19+
# No 'wit' attribute needed
20+
rustc_flags = [
21+
"-C", "opt-level=3", # Custom optimization
22+
"-C", "lto=thin", # Link-time optimization
23+
],
24+
)
25+
26+
# Example 2: Component with Custom Interface using rust_wasm_component_bindgen
27+
# Use case: Components that export custom functions to other components
28+
wit_library(
29+
name = "processor_interfaces",
30+
srcs = ["wit/processor.wit"],
31+
package_name = "example:[email protected]",
32+
)
33+
34+
rust_wasm_component_bindgen(
35+
name = "file_processor_component",
36+
srcs = ["src/component_lib.rs"],
37+
wit = ":processor_interfaces",
38+
# This automatically generates WIT bindings and handles interface exports
39+
profiles = ["release"],
40+
)
41+
42+
# Comparison test to show the difference
43+
genrule(
44+
name = "component_comparison",
45+
srcs = [
46+
":file_processor_cli",
47+
":file_processor_component",
48+
],
49+
outs = ["comparison.txt"],
50+
cmd = """
51+
echo "Component Size Comparison" > $@
52+
echo "========================" >> $@
53+
echo "" >> $@
54+
echo "CLI Tool (rust_wasm_component):" >> $@
55+
wc -c $(location :file_processor_cli) >> $@
56+
echo "" >> $@
57+
echo "Component Library (rust_wasm_component_bindgen):" >> $@
58+
wc -c $(location :file_processor_component) >> $@
59+
echo "" >> $@
60+
echo "Use rust_wasm_component for:" >> $@
61+
echo "- CLI tools and utilities" >> $@
62+
echo "- WASI-only components" >> $@
63+
echo "- Legacy WASM module conversion" >> $@
64+
echo "- Custom build requirements" >> $@
65+
echo "" >> $@
66+
echo "Use rust_wasm_component_bindgen for:" >> $@
67+
echo "- Components with custom interfaces" >> $@
68+
echo "- Inter-component communication" >> $@
69+
echo "- Standard component development" >> $@
70+
""",
71+
)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# CLI Tool Example: When to Use `rust_wasm_component` vs `rust_wasm_component_bindgen`
2+
3+
This example demonstrates the **key differences** between the two Rust WebAssembly component rules and when to use each one.
4+
5+
## Rule Comparison
6+
7+
### `rust_wasm_component` (Lower-Level Rule)
8+
**Use for**: CLI tools, utilities, WASI-only components
9+
10+
```starlark
11+
rust_wasm_component(
12+
name = "file_processor_cli",
13+
srcs = ["src/cli_tool.rs"],
14+
deps = ["@crates//:clap", "@crates//:anyhow"],
15+
# No 'wit' attribute - uses WASI only
16+
rustc_flags = ["-C", "opt-level=3"], # Custom optimization
17+
)
18+
```
19+
20+
### `rust_wasm_component_bindgen` (High-Level Rule)
21+
**Use for**: Components with custom interfaces, inter-component communication
22+
23+
```starlark
24+
rust_wasm_component_bindgen(
25+
name = "file_processor_component",
26+
srcs = ["src/component_lib.rs"],
27+
wit = ":processor_interfaces", # Custom WIT interfaces
28+
profiles = ["release"], # Simplified configuration
29+
)
30+
```
31+
32+
## When to Use Each
33+
34+
### Use `rust_wasm_component` when:
35+
36+
1. **CLI Tools & Utilities**
37+
- Command-line applications run by users
38+
- Tools that process files, manipulate data, etc.
39+
- Applications that use WASI but don't export custom functions
40+
41+
2. **WASI-Only Components**
42+
- Components that only import WASI capabilities (filesystem, stdio, etc.)
43+
- No custom interfaces to export to other components
44+
- Simple input/output processing
45+
46+
3. **Legacy WASM Module Conversion**
47+
- Converting existing `.wasm` modules to component format
48+
- Wrapping external tools for component compatibility
49+
- Migration of existing WebAssembly applications
50+
51+
4. **Custom Build Requirements**
52+
- Need specific rustc flags or optimization settings
53+
- Complex compilation pipelines
54+
- Performance-critical components requiring fine-tuned compilation
55+
56+
### Use `rust_wasm_component_bindgen` when:
57+
58+
1. **Custom Component Interfaces**
59+
- Exporting functions for other components to call
60+
- Defining custom WIT interfaces and types
61+
- Building reusable component libraries
62+
63+
2. **Inter-Component Communication**
64+
- Components designed to be composed with others
65+
- Microservices architectures using WAC
66+
- Plugin systems and modular applications
67+
68+
3. **Standard Component Development**
69+
- Most typical component development workflows
70+
- Following component model best practices
71+
- Leveraging automatic binding generation
72+
73+
4. **Simplified Development Experience**
74+
- Want automatic WIT binding generation
75+
- Prefer conventional configuration over custom flags
76+
- Building components for consumption by others
77+
78+
## Examples in This Directory
79+
80+
### CLI Tool (`file_processor_cli`)
81+
- **File**: `src/cli_tool.rs`
82+
- **Use case**: Command-line file processing utility
83+
- **Interfaces**: WASI only (filesystem, stdio)
84+
- **Usage**: `wasmtime run file_processor_cli.wasm -- upper -i input.txt -o output.txt`
85+
86+
### Component Library (`file_processor_component`)
87+
- **File**: `src/component_lib.rs` + `wit/processor.wit`
88+
- **Use case**: Reusable file processing functions for other components
89+
- **Interfaces**: Custom WIT interfaces + WASI
90+
- **Usage**: Called by other components via WIT interfaces
91+
92+
## Building and Testing
93+
94+
```bash
95+
# Build both examples
96+
bazel build //examples/cli_tool_example:file_processor_cli
97+
bazel build //examples/cli_tool_example:file_processor_component
98+
99+
# Compare component sizes and characteristics
100+
bazel build //examples/cli_tool_example:component_comparison
101+
cat bazel-bin/examples/cli_tool_example/comparison.txt
102+
103+
# Test the CLI tool
104+
echo "hello world" > test.txt
105+
wasmtime run bazel-bin/examples/cli_tool_example/file_processor_cli.wasm -- upper -i test.txt -o upper.txt
106+
cat upper.txt # Should show "HELLO WORLD"
107+
108+
# Inspect the component library
109+
wasm-tools component wit bazel-bin/examples/cli_tool_example/file_processor_component.wasm
110+
```
111+
112+
## Key Takeaways
113+
114+
- **`rust_wasm_component`**: Lower-level, more control, CLI tools, WASI-only
115+
- **`rust_wasm_component_bindgen`**: Higher-level, automatic bindings, custom interfaces
116+
- **Choose based on use case**: CLI utilities vs component libraries
117+
- **Both are valid**: Different tools for different jobs in the WebAssembly ecosystem
118+
119+
Most developers should start with `rust_wasm_component_bindgen` for typical component development, and use `rust_wasm_component` when they need the specific capabilities it provides.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*!
2+
Example CLI tool using rust_wasm_component.
3+
4+
This demonstrates a component that uses WASI capabilities but doesn't
5+
export custom interfaces - perfect for the lower-level rust_wasm_component rule.
6+
*/
7+
8+
use anyhow::{Context, Result};
9+
use clap::{Parser, Subcommand};
10+
use serde_json::{json, Value};
11+
use std::fs;
12+
use std::path::PathBuf;
13+
14+
/// File processor CLI tool
15+
#[derive(Parser)]
16+
#[command(name = "file-processor")]
17+
#[command(about = "A simple file processing tool running in WebAssembly")]
18+
#[command(version = "1.0.0")]
19+
struct Cli {
20+
#[command(subcommand)]
21+
command: Commands,
22+
23+
/// Enable verbose output
24+
#[arg(short, long, global = true)]
25+
verbose: bool,
26+
}
27+
28+
#[derive(Subcommand)]
29+
enum Commands {
30+
/// Convert file to uppercase
31+
Upper {
32+
/// Input file path
33+
#[arg(short, long)]
34+
input: PathBuf,
35+
/// Output file path
36+
#[arg(short, long)]
37+
output: PathBuf,
38+
},
39+
/// Count words in file
40+
Count {
41+
/// Input file path
42+
#[arg(short, long)]
43+
input: PathBuf,
44+
},
45+
/// Convert file to JSON
46+
JsonWrap {
47+
/// Input file path
48+
#[arg(short, long)]
49+
input: PathBuf,
50+
/// Output file path
51+
#[arg(short, long)]
52+
output: PathBuf,
53+
},
54+
}
55+
56+
fn main() -> Result<()> {
57+
let cli = Cli::parse();
58+
59+
match cli.command {
60+
Commands::Upper { input, output } => {
61+
let content = fs::read_to_string(&input)
62+
.with_context(|| format!("Failed to read file: {:?}", input))?;
63+
64+
let upper_content = content.to_uppercase();
65+
66+
fs::write(&output, upper_content)
67+
.with_context(|| format!("Failed to write file: {:?}", output))?;
68+
69+
if cli.verbose {
70+
println!("Converted {:?} to uppercase -> {:?}", input, output);
71+
}
72+
}
73+
Commands::Count { input } => {
74+
let content = fs::read_to_string(&input)
75+
.with_context(|| format!("Failed to read file: {:?}", input))?;
76+
77+
let word_count = content.split_whitespace().count();
78+
let char_count = content.chars().count();
79+
let line_count = content.lines().count();
80+
81+
println!("File: {:?}", input);
82+
println!("Lines: {}", line_count);
83+
println!("Words: {}", word_count);
84+
println!("Characters: {}", char_count);
85+
}
86+
Commands::JsonWrap { input, output } => {
87+
let content = fs::read_to_string(&input)
88+
.with_context(|| format!("Failed to read file: {:?}", input))?;
89+
90+
let json_output = json!({
91+
"source_file": input.to_string_lossy(),
92+
"content": content,
93+
"processed_at": chrono::Utc::now().to_rfc3339(),
94+
"length": content.len()
95+
});
96+
97+
fs::write(&output, serde_json::to_string_pretty(&json_output)?)
98+
.with_context(|| format!("Failed to write JSON file: {:?}", output))?;
99+
100+
if cli.verbose {
101+
println!("Wrapped {:?} as JSON -> {:?}", input, output);
102+
}
103+
}
104+
}
105+
106+
Ok(())
107+
}
108+
109+
/*
110+
This component demonstrates rust_wasm_component usage:
111+
112+
1. WASI-only: Uses filesystem, stdio - no custom component interfaces
113+
2. CLI tool: Designed to be run from command line, not called by other components
114+
3. Self-contained: Doesn't export functions for other components to use
115+
4. Simple build: No WIT binding generation needed
116+
117+
Perfect for rust_wasm_component because:
118+
- No custom WIT interfaces to generate bindings for
119+
- Uses standard WASI capabilities only
120+
- Allows custom rustc flags for optimization
121+
- Simpler build process for utilities
122+
123+
Usage:
124+
wasmtime run file_processor_cli.wasm -- upper -i input.txt -o output.txt
125+
wasmtime run file_processor_cli.wasm -- count -i document.txt
126+
wasmtime run file_processor_cli.wasm -- json-wrap -i data.txt -o data.json
127+
*/

0 commit comments

Comments
 (0)