-
Notifications
You must be signed in to change notification settings - Fork 68
docs: Clarify setup for "Rust Components" tutorial #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
2e207e4
d22b430
97d6d55
262d47f
661ac99
d23903d
a2228c0
02e4760
4c1c58c
b8c77c6
1db8a29
5bbe8e4
688bbbd
e63b3d1
917d2b0
ec05fac
9102bc9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! | ||
// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! | ||
// Options used: | ||
// * runtime_path: "wit_bindgen_rt" | ||
#[rustfmt::skip] | ||
#[allow(dead_code, clippy::all)] | ||
pub mod exports { | ||
pub mod docs { | ||
pub mod adder { | ||
#[allow(dead_code, clippy::all)] | ||
#[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] | ||
pub mod add { | ||
#[used] | ||
#[doc(hidden)] | ||
|
@@ -25,9 +25,10 @@ pub mod exports { | |
#[doc(hidden)] | ||
macro_rules! __export_docs_adder_add_0_1_0_cabi { | ||
($ty:ident with_types_in $($path_to_types:tt)*) => { | ||
const _ : () = { #[export_name = "docs:adder/[email protected]#add"] | ||
unsafe extern "C" fn export_add(arg0 : i32, arg1 : i32,) -> i32 { | ||
$($path_to_types)*:: _export_add_cabi::<$ty > (arg0, arg1) } }; | ||
const _ : () = { #[unsafe (export_name = | ||
"docs:adder/[email protected]#add")] unsafe extern "C" fn export_add(arg0 | ||
: i32, arg1 : i32,) -> i32 { unsafe { $($path_to_types)*:: | ||
_export_add_cabi::<$ty > (arg0, arg1) } } }; | ||
}; | ||
} | ||
#[doc(hidden)] | ||
|
@@ -38,6 +39,7 @@ pub mod exports { | |
} | ||
#[rustfmt::skip] | ||
mod _rt { | ||
#![allow(dead_code, clippy::all)] | ||
#[cfg(target_arch = "wasm32")] | ||
pub fn run_ctors_once() { | ||
wit_bindgen_rt::run_ctors_once(); | ||
|
@@ -102,8 +104,8 @@ mod _rt { | |
} | ||
} | ||
} | ||
/// Generates `#[no_mangle]` functions to export the specified type as the | ||
/// root implementation of all generated traits. | ||
/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as | ||
/// the root implementation of all generated traits. | ||
/// | ||
/// For more information see the documentation of `wit_bindgen::generate!`. | ||
/// | ||
|
@@ -133,14 +135,17 @@ macro_rules! __export_adder_impl { | |
#[doc(inline)] | ||
pub(crate) use __export_adder_impl as export; | ||
#[cfg(target_arch = "wasm32")] | ||
#[link_section = "component-type:wit-bindgen:0.36.0:docs:[email protected]:adder:encoded world"] | ||
#[unsafe( | ||
link_section = "component-type:wit-bindgen:0.41.0:docs:[email protected]:adder:encoded world" | ||
)] | ||
#[doc(hidden)] | ||
#[allow(clippy::octal_escapes)] | ||
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 203] = *b"\ | ||
\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07P\x01A\x02\x01A\x02\x01\ | ||
B\x02\x01@\x02\x01ay\x01by\0y\x04\0\x03add\x01\0\x04\0\x14docs:adder/[email protected]\x05\ | ||
B\x02\x01@\x02\x01xy\x01yy\0y\x04\0\x03add\x01\0\x04\0\x14docs:adder/[email protected]\x05\ | ||
\0\x04\0\x16docs:adder/[email protected]\x04\0\x0b\x0b\x01\0\x05adder\x03\0\0\0G\x09pr\ | ||
oducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.220.0\x10wit-bindgen-rust\x06\ | ||
0.36.0"; | ||
oducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.227.1\x10wit-bindgen-rust\x06\ | ||
0.41.0"; | ||
#[inline(never)] | ||
#[doc(hidden)] | ||
pub fn __link_custom_section_describing_imports() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,103 @@ | ||
# Components in Rust | ||
|
||
Rust has first-class support for the component model via [the `cargo component` tool](https://github.com/bytecodealliance/cargo-component). | ||
Rust has first-class support for the component model via the [`cargo-component` | ||
tool][cargo-component]. We will be using | ||
the `cargo component` subcommand to create WebAssembly components using Rust as | ||
the component's implementation language. | ||
|
||
`cargo component` is is a `cargo` subcommand for creating WebAssembly components | ||
using Rust as the component's implementation language. | ||
> [!NOTE] | ||
> You can find more details about `cargo-component` on [crates.io](https://crates.io/crates/cargo-component). | ||
|
||
## 1. Installing `cargo component` | ||
## 1. Setup | ||
|
||
To install `cargo component`, run: | ||
|
||
```sh | ||
cargo install cargo-component | ||
Install [`cargo-component`][cargo-component-install]: | ||
```console | ||
cargo install --locked cargo-component | ||
``` | ||
|
||
> You can find more details about `cargo component` in its [crates.io page](https://crates.io/crates/cargo-component). | ||
|
||
## 2. Scaffold a Component with `cargo component` | ||
|
||
Create a Rust library that implements the `add` function in the [`adder` world][adder-wit]. | ||
|
||
First scaffold a project: | ||
|
||
```sh | ||
$ cargo component new adder --lib && cd adder | ||
Install [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools#installation): | ||
```console | ||
cargo install --locked wasm-tools | ||
``` | ||
Install [`wasmtime`](https://github.com/bytecodealliance/wasmtime#installation): | ||
```console | ||
curl https://wasmtime.dev/install.sh -sSf | bash | ||
``` | ||
Clone the [component-docs](https://github.com/bytecodealliance/component-docs) repo: | ||
```console | ||
git clone https://github.com/bytecodealliance/component-docs | ||
``` | ||
|
||
Note that `cargo component` generates the necessary bindings as a module called `bindings`. | ||
## 2. Scaffolding a Component | ||
|
||
## 3. Add the WIT world the Component will implement | ||
We will create a component in Rust that implements the `add` function exported | ||
by the [`adder` world][docs-adder] world in the | ||
`docs:adder` | ||
[package](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#package-names). | ||
|
||
Next, update `wit/world.wit` to match the [`adder` world][adder-wit]: | ||
First `cd` into the `tutorial` directory found in the repo we just cloned: | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
```console | ||
cd component-docs/component-model/examples/tutorial | ||
``` | ||
|
||
Now create a new WebAssembly component package called `add`: | ||
```console | ||
cargo component new add --lib && cd add | ||
``` | ||
package docs:[email protected]; | ||
|
||
interface add { | ||
add: func(x: u32, y: u32) -> u32; | ||
} | ||
## 3. Adding the WIT world | ||
|
||
world adder { | ||
export add; | ||
} | ||
We now need to change our generated `wit/world.wit` to match `docs:adder`: | ||
```wit | ||
{{#include ../../examples/tutorial/wit/adder/world.wit}} | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
The `component` section of `Cargo.toml` should look like the following: | ||
The `package.metadata.component` section of our `Cargo.toml` should be changed | ||
to the following: | ||
|
||
```toml | ||
[package.metadata.component] | ||
package = "docs:adder" | ||
``` | ||
|
||
## 4. Generate bindings for our component | ||
## 4. Generating bindings | ||
|
||
After performing these changes, we can re-generate bindings with `cargo component bindings`: | ||
Now that we've updated our `world.wit` and `Cargo.toml`, we can re-generate | ||
bindings with the command below: | ||
|
||
```console | ||
cargo component bindings | ||
``` | ||
|
||
`cargo component bindings` will generate bindings for the world specified in a package's `Cargo.toml`. In particular, | ||
`cargo component` will create a `Guest` trait that a component should implement. | ||
`cargo-component` will generate bindings for our | ||
world and create a `Guest` trait that a component should | ||
implement. | ||
|
||
## 5. Implement the generated `Guest` trait | ||
## 5. Implementing the `Guest` trait | ||
|
||
Implement the `Guest` trait in `src/lib.rs`, using the scaffolded code. Your code should look something like the following: | ||
Implement the `Guest` trait in `src/lib.rs`, using the scaffolded code. Your | ||
code should look something like the following: | ||
|
||
```rs | ||
#[allow(warnings)] | ||
mod bindings; | ||
|
||
use bindings::exports::docs::adder::add::Guest; | ||
|
||
struct Component; | ||
|
||
impl Guest for Component { | ||
fn add(x: u32, y: u32) -> u32 { | ||
return x + y; | ||
} | ||
} | ||
|
||
bindings::export!(Component with_types_in bindings); | ||
{{#include ../../examples/tutorial/adder/src/lib.rs}} | ||
``` | ||
|
||
## 6. Build the component | ||
## 6. Building a Component | ||
|
||
Now, use `cargo component` to build the component, being sure to optimize with a release build. | ||
Now, let's build our component, being sure to optimize with a release build: | ||
kate-goldenring marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```console | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
cargo component build --release | ||
``` | ||
|
||
> **WARNING:** Building with `--release` removes all debug-related information from the resulting .wasm file. When prototyping or testing locally, you might want to avoid `--release` to obtain useful backtraces in case of errors (for example, with `wasmtime::WasmBacktraceDetails::Enable`). Note: the resulting .wasm file will be considerably larger (likely 4MB+). | ||
|
||
You can use `wasm-tools component wit` to output the WIT package of the component: | ||
You can use `wasm-tools` to output the WIT package of the component: | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```console | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
wasm-tools component wit target/wasm32-wasip1/release/add.wasm | ||
``` | ||
$ wasm-tools component wit target/wasm32-wasip1/release/adder.wasm | ||
|
||
The command above should produce the output below: | ||
|
||
```wit | ||
package root:component; | ||
|
||
world root { | ||
|
@@ -108,10 +110,15 @@ package docs:[email protected] { | |
} | ||
``` | ||
|
||
### Running a Component from Rust Applications | ||
> [!WARNING] | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
> Building with `--release` removes all debug-related information from the resulting .wasm file. | ||
> | ||
> When prototyping or testing locally, you might want to avoid `--release` to obtain useful backtraces in case of errors (for example, with `wasmtime::WasmBacktraceDetails::Enable`). Note: the resulting .wasm file will be considerably larger (likely 4MB+). | ||
|
||
### Running a Component | ||
|
||
To verify that our component works, lets run it from a Rust application that knows how to run a | ||
component targeting the [`adder` world][adder-wit]. | ||
component targeting the [`adder` world](#adding-the-wit-world). | ||
|
||
The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime) crates to generate | ||
Rust bindings, bring in WASI worlds, and execute the component. | ||
|
@@ -122,13 +129,31 @@ $ cargo run --release -- 1 2 ../add/target/wasm32-wasip1/release/adder.wasm | |
1 + 2 = 3 | ||
``` | ||
|
||
## Importing an interface with `cargo component` | ||
## Exporting an interface | ||
|
||
Notice how our `root` world in the `wasm-tools` output exports `add` as part of an _interface_. | ||
It's often preferable to export an interface rather than a function, either to | ||
comply with an existing specification or to capture several functions and types | ||
at once. | ||
|
||
For example, to implement the [`adder` world](#adding-the-wit-world), you would | ||
write the following Rust code: | ||
|
||
```rust | ||
{{#include ../../examples/tutorial/adder/src/lib.rs}} | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
## Importing an interface | ||
|
||
The world file (`wit/world.wit`) generated for you by `cargo component new --lib` doesn't specify any imports. | ||
The world file (`wit/world.wit`) we generated doesn't specify any imports. If | ||
your component consumes other components, you can edit the `world.wit` file to | ||
import their interfaces. | ||
|
||
> `cargo component build`, by default, uses the Rust `wasm32-wasi` target, and therefore automatically imports any required WASI interfaces - no action is needed from you to import these. This section is about importing custom WIT interfaces from library components. | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
> [!NOTE] | ||
> This section is about importing custom WIT interfaces from library components. | ||
> By default, `cargo-component` imports any required [WASI interfaces](https://wasi.dev/interfaces) | ||
> for us without needing to explicitly declare them. | ||
|
||
If your component consumes other components, you can edit the `world.wit` file to import their interfaces. | ||
|
||
For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: | ||
|
||
|
@@ -150,14 +175,19 @@ world calculator { | |
|
||
### Referencing the package to import | ||
|
||
Because the `docs:adder` package is in a different project, we must first tell `cargo component` how to find it. To do this, add the following to the `Cargo.toml` file: | ||
Because the `docs:adder` package is in a different project, we must first tell | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
`cargo component` how to find it. To do this, add the following to the | ||
`Cargo.toml` file: | ||
|
||
```toml | ||
[package.metadata.component.target.dependencies] | ||
"docs:adder" = { path = "../adder/wit" } # directory containing the WIT package | ||
"docs:adder" = { path = "../wit/adder" } # directory containing the WIT package | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assumes they cloned the repo rather than just fetching the wit. I'd prefer to stay with the approach of fetching the one wit file rather than cloning the repo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Following up on #218 (comment), if we don't specify cloning the repo then we'd have to specify fetching the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think fetching a file is the most analogous to what the wkg experience will be like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed that that's what we should aim for, however, calling Should I instead add an instruction to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that the example host is an exception -- for now, they will need to clone the repo to run it. Maybe this line should be The main thing i am thinking about is that the fact that the example host is not fetchable should not define how we instruct users to build components. We can push the host up in a container for example or release it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a great and useful example that demonstrantes using external logic to execute the user's code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to have a wit in ghcr that they can depend on? Then there would be no question about file paths because they wouldn't be needed when specifying the dependency. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is definitely the plan: upload the packages to ghcr and update the tutorial and guides to use wkg. We just need the work to be prioritized There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created an issue to get them in GHCR: #243 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the meantime for this PR, should I revert the path changes and references to |
||
``` | ||
|
||
Note that the path is to the adder project's WIT _directory_, not to the `world.wit` file. A WIT package may be spread across multiple files in the same directory; `cargo component` will look at all the files. | ||
> [!NOTE] | ||
> The path for `docs:adder` is relative to the `wit` _directory_, not to the `world.wit` file. | ||
> | ||
> A WIT package may be spread across multiple files in the same directory; `cargo component` will search them all. | ||
|
||
### Calling the import from Rust | ||
|
||
|
@@ -262,8 +292,8 @@ As mentioned above, `cargo component build` doesn't generate a WIT file for a co | |
|
||
```toml | ||
[package.metadata.component.target.dependencies] | ||
"docs:calculator" = { path = "../calculator/wit" } | ||
"docs:adder" = { path = "../adder/wit" } | ||
"docs:calculator" = { path = "../wit/calculator" } | ||
mkatychev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"docs:adder" = { path = "../wit/adder" } | ||
``` | ||
|
||
> If the external package refers to other packages, you need to provide the paths to them as well. | ||
|
@@ -551,4 +581,6 @@ If you are hosting a Wasm runtime, you can export a resource from your host for | |
} | ||
``` | ||
|
||
[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit | ||
[cargo-component]: https://github.com/bytecodealliance/cargo-component | ||
[cargo-component-install]: https://github.com/bytecodealliance/cargo-component#install | ||
[docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit |
Uh oh!
There was an error while loading. Please reload this page.