From 04027d2336990f27c074afa7695a10749d870c74 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Thu, 16 Jan 2025 23:41:58 +0900 Subject: [PATCH 01/15] fix(rust): use adder class for example host Signed-off-by: Victor Adossi --- component-model/examples/example-host/add.wit | 10 ++-- .../examples/example-host/src/async_add.rs | 52 +++++++++++++----- .../examples/example-host/src/main.rs | 12 ++--- .../examples/example-host/src/state.rs | 2 +- .../examples/example-host/src/sync_add.rs | 53 ++++++++++++++----- component-model/src/language-support/rust.md | 2 +- 6 files changed, 93 insertions(+), 38 deletions(-) diff --git a/component-model/examples/example-host/add.wit b/component-model/examples/example-host/add.wit index 703ed7e1..e5777610 100644 --- a/component-model/examples/example-host/add.wit +++ b/component-model/examples/example-host/add.wit @@ -1,5 +1,9 @@ -package example:component; +package docs:adder@0.1.0; -world example { - export add: func(x: s32, y: s32) -> s32; +interface add { + add: func(a: u32, b: u32) -> u32; +} + +world adder { + export add; } diff --git a/component-model/examples/example-host/src/async_add.rs b/component-model/examples/example-host/src/async_add.rs index eb8b063d..1b497b29 100644 --- a/component-model/examples/example-host/src/async_add.rs +++ b/component-model/examples/example-host/src/async_add.rs @@ -1,35 +1,61 @@ -use anyhow::Context; use std::path::PathBuf; -use wasmtime::component::*; + +use anyhow::Context; +use wasmtime::component::{Component, Linker}; use wasmtime::{Config, Engine, Store}; + use crate::state::States; -bindgen!({ - path: "add.wit", - world: "example", - async: true -}); +mod bindings { + //! Generated code for the + wasmtime::component::bindgen!({ + path: "add.wit", + world: "adder", + async: true + }); +} -pub async fn add(path: PathBuf, x: i32, y: i32) -> wasmtime::Result { +/// Perform the add operation for a given WebAssembly component +/// +/// This operation asynchronously (as opposed to synchronously +/// without an async runtime like `tokio`). +/// +/// # Arguments +/// +/// * `path` - Path to the Wasm component bytes +/// * `x` - The left hand side of the addition +/// * `y` - The right hand side of the addition +/// +pub async fn add(path: PathBuf, x: u32, y: u32) -> wasmtime::Result { // Construct engine let mut config = Config::default(); config.async_support(true); let engine = Engine::new(&config)?; + // Construct component let component = Component::from_file(&engine, path).context("Component file not found")?; + // Construct store for storing running states of the component let wasi_view = States::new(); let mut store = Store::new(&engine, wasi_view); + // Construct linker for linking interfaces. - // For this simple adder component, no need to manually link additional interfaces. let mut linker = Linker::new(&engine); - // Add wasi exports to linker to support io interfaces + + // Add wasi exports to linker to support I/O (as in `wasi:io`) interfaces + // see: https://github.com/WebAssembly/wasi-io wasmtime_wasi::add_to_linker_async(&mut linker)?; - let instance = Example::instantiate_async(&mut store, &component, &linker) + + // Instantiate the component as an instance of the `adder` world, + // with the generated bindings + let instance = bindings::Adder::instantiate_async(&mut store, &component, &linker) .await .context("Failed to instantiate the example world")?; + + // Call the add function on instance instance + .docs_adder_add() .call_add(&mut store, x, y) .await - .context("Failed to call add function") -} \ No newline at end of file + .context("calling add function") +} diff --git a/component-model/examples/example-host/src/main.rs b/component-model/examples/example-host/src/main.rs index 1a93c050..71c187e7 100644 --- a/component-model/examples/example-host/src/main.rs +++ b/component-model/examples/example-host/src/main.rs @@ -1,19 +1,19 @@ -mod async_add; -mod sync_add; -mod state; - use clap::Parser; use std::path::PathBuf; +mod async_add; +mod state; +mod sync_add; + /// A CLI for executing WebAssembly components that /// implement the `example` world. #[derive(Parser)] #[clap(name = "add-host", version = env!("CARGO_PKG_VERSION"))] struct AddApp { /// The first operand - x: i32, + x: u32, /// The second operand - y: i32, + y: u32, /// The path to the component. #[clap(value_name = "COMPONENT_PATH")] component: PathBuf, diff --git a/component-model/examples/example-host/src/state.rs b/component-model/examples/example-host/src/state.rs index ebd20335..74819bc3 100644 --- a/component-model/examples/example-host/src/state.rs +++ b/component-model/examples/example-host/src/state.rs @@ -22,4 +22,4 @@ impl WasiView for States { fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx } -} \ No newline at end of file +} diff --git a/component-model/examples/example-host/src/sync_add.rs b/component-model/examples/example-host/src/sync_add.rs index f67c151e..9bc3e151 100644 --- a/component-model/examples/example-host/src/sync_add.rs +++ b/component-model/examples/example-host/src/sync_add.rs @@ -1,32 +1,57 @@ -use crate::state::States; -use anyhow::Context; use std::path::PathBuf; -use wasmtime::component::{bindgen, Component, Linker}; + +use anyhow::Context; +use wasmtime::component::{Component, Linker}; use wasmtime::{Engine, Store}; use wasmtime_wasi; -bindgen!({ - path: "add.wit", - world: "example", - async: false -}); +use crate::state::States; + +mod bindings { + wasmtime::component::bindgen!({ + path: "add.wit", + world: "adder", + async: false + }); +} -pub fn add(path: PathBuf, x: i32, y: i32) -> wasmtime::Result { +/// Perform a add operation for a given WebAssembly component +/// +/// This operation happens synchronously (as opposed to asynchronously +/// powered by an async runtime like `tokio`). +/// +/// # Arguments +/// +/// * `path` - Path to the Wasm component bytes +/// * `x` - The left hand side of the addition +/// * `y` - The right hand side of the addition +/// +pub fn add(path: PathBuf, x: u32, y: u32) -> wasmtime::Result { // Construct engine let engine = Engine::default(); + // Construct component let component = Component::from_file(&engine, path).context("Component file not found")?; + // Construct store for storing running states of the component let wasi_view = States::new(); let mut store = Store::new(&engine, wasi_view); + // Construct linker for linking interfaces. - // For this simple adder component, no need to manually link additional interfaces. let mut linker = Linker::new(&engine); - // Add wasi exports to linker to support io interfaces + + // Add wasi exports to linker to support I/O (as in `wasi:io`) interfaces + // see: https://github.com/WebAssembly/wasi-io wasmtime_wasi::add_to_linker_sync(&mut linker).expect("Could not add wasi to linker"); - let instance = Example::instantiate(&mut store, &component, &linker) + + // Instantiate the component as an instance of the `adder` world, + // with the generated bindings + let instance = bindings::Adder::instantiate(&mut store, &component, &linker) .context("Failed to instantiate the example world")?; + + // Call the add function on instance instance + .docs_adder_add() .call_add(&mut store, x, y) - .context("Failed to call add function") -} \ No newline at end of file + .context("calling add function") +} diff --git a/component-model/src/language-support/rust.md b/component-model/src/language-support/rust.md index 263bbaa1..5f2d9070 100644 --- a/component-model/src/language-support/rust.md +++ b/component-model/src/language-support/rust.md @@ -38,7 +38,7 @@ package = "example:component" struct Component; impl Guest for Component { - fn add(x: i32, y: i32) -> i32 { + fn add(x: u32, y: u32) -> u32 { x + y } } From e299488425d171768cb957e4ae77cedbd4d01e17 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Thu, 16 Jan 2025 23:56:44 +0900 Subject: [PATCH 02/15] chore: update adder rust tutorial Signed-off-by: Victor Adossi --- .../examples/tutorial/adder/Cargo.lock | 6 +- .../examples/tutorial/adder/Cargo.toml | 2 +- .../examples/tutorial/adder/src/bindings.rs | 72 +++++++------------ 3 files changed, 31 insertions(+), 49 deletions(-) diff --git a/component-model/examples/tutorial/adder/Cargo.lock b/component-model/examples/tutorial/adder/Cargo.lock index 90e98753..bff0abf6 100644 --- a/component-model/examples/tutorial/adder/Cargo.lock +++ b/component-model/examples/tutorial/adder/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "adder" @@ -17,9 +17,9 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "wit-bindgen-rt" -version = "0.24.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" +checksum = "fc801b991c56492f87ab3086e786468f75c285a4d73017ab0ebc2fa1aed5d82c" dependencies = [ "bitflags", ] diff --git a/component-model/examples/tutorial/adder/Cargo.toml b/component-model/examples/tutorial/adder/Cargo.toml index d710b687..7578216a 100644 --- a/component-model/examples/tutorial/adder/Cargo.toml +++ b/component-model/examples/tutorial/adder/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -wit-bindgen-rt = { version = "0.24.0", features = ["bitflags"] } +wit-bindgen-rt = { version = "0.37.0", features = ["bitflags"] } [lib] crate-type = ["cdylib"] diff --git a/component-model/examples/tutorial/adder/src/bindings.rs b/component-model/examples/tutorial/adder/src/bindings.rs index 8ab09fb2..7c3b88b1 100644 --- a/component-model/examples/tutorial/adder/src/bindings.rs +++ b/component-model/examples/tutorial/adder/src/bindings.rs @@ -1,24 +1,21 @@ -// Generated by `wit-bindgen` 0.25.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! // Options used: -#[allow(dead_code)] +// * runtime_path: "wit_bindgen_rt" +#[rustfmt::skip] +#[allow(dead_code, clippy::all)] pub mod exports { - #[allow(dead_code)] pub mod docs { - #[allow(dead_code)] pub mod adder { #[allow(dead_code, clippy::all)] pub mod add { #[used] #[doc(hidden)] - #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = - super::super::super::super::__link_custom_section_describing_imports; + static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports; use super::super::super::super::_rt; #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_add_cabi(arg0: i32, arg1: i32) -> i32 { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); + #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::add(arg0 as u32, arg1 as u32); _rt::as_i32(result0) } @@ -26,92 +23,78 @@ pub mod exports { fn add(a: u32, b: u32) -> u32; } #[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/add@0.1.0#add"] - unsafe extern "C" fn export_add(arg0: i32,arg1: i32,) -> i32 { - $($path_to_types)*::_export_add_cabi::<$ty>(arg0, arg1) - } - };); - } + macro_rules! __export_docs_adder_add_0_1_0_cabi { + ($ty:ident with_types_in $($path_to_types:tt)*) => { + const _ : () = { #[export_name = "docs:adder/add@0.1.0#add"] + unsafe extern "C" fn export_add(arg0 : i32, arg1 : i32,) -> i32 { + $($path_to_types)*:: _export_add_cabi::<$ty > (arg0, arg1) } }; + }; + } #[doc(hidden)] pub(crate) use __export_docs_adder_add_0_1_0_cabi; } } } } +#[rustfmt::skip] mod _rt { - #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { wit_bindgen_rt::run_ctors_once(); } - pub fn as_i32(t: T) -> i32 { t.as_i32() } - pub trait AsI32 { fn as_i32(self) -> i32; } - impl<'a, T: Copy + AsI32> AsI32 for &'a T { fn as_i32(self) -> i32 { (*self).as_i32() } } - impl AsI32 for i32 { #[inline] fn as_i32(self) -> i32 { self as i32 } } - impl AsI32 for u32 { #[inline] fn as_i32(self) -> i32 { self as i32 } } - impl AsI32 for i16 { #[inline] fn as_i32(self) -> i32 { self as i32 } } - impl AsI32 for u16 { #[inline] fn as_i32(self) -> i32 { self as i32 } } - impl AsI32 for i8 { #[inline] fn as_i32(self) -> i32 { self as i32 } } - impl AsI32 for u8 { #[inline] fn as_i32(self) -> i32 { self as i32 } } - impl AsI32 for char { #[inline] fn as_i32(self) -> i32 { self as i32 } } - impl AsI32 for usize { #[inline] fn as_i32(self) -> i32 { @@ -119,7 +102,6 @@ mod _rt { } } } - /// Generates `#[no_mangle]` functions to export the specified type as the /// root implementation of all generated traits. /// @@ -138,29 +120,29 @@ mod _rt { /// ``` #[allow(unused_macros)] #[doc(hidden)] - macro_rules! __export_adder_impl { - ($ty:ident) => (self::export!($ty with_types_in self);); - ($ty:ident with_types_in $($path_to_types_root:tt)*) => ( - $($path_to_types_root)*::exports::docs::adder::add::__export_docs_adder_add_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::docs::adder::add); - ) + ($ty:ident) => { + self::export!($ty with_types_in self); + }; + ($ty:ident with_types_in $($path_to_types_root:tt)*) => { + $($path_to_types_root)*:: + exports::docs::adder::add::__export_docs_adder_add_0_1_0_cabi!($ty with_types_in + $($path_to_types_root)*:: exports::docs::adder::add); + }; } #[doc(inline)] pub(crate) use __export_adder_impl as export; - #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.25.0:adder:encoded world"] +#[link_section = "component-type:wit-bindgen:0.36.0:docs:adder@0.1.0:adder:encoded world"] #[doc(hidden)] 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\x01\x14docs:adder/add@0.1.0\ -\x05\0\x04\x01\x16docs:adder/adder@0.1.0\x04\0\x0b\x0b\x01\0\x05adder\x03\0\0\0G\ -\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.208.1\x10wit-bindgen\ --rust\x060.25.0"; - +B\x02\x01@\x02\x01ay\x01by\0y\x04\0\x03add\x01\0\x04\0\x14docs:adder/add@0.1.0\x05\ +\0\x04\0\x16docs:adder/adder@0.1.0\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"; #[inline(never)] #[doc(hidden)] -#[cfg(target_arch = "wasm32")] pub fn __link_custom_section_describing_imports() { wit_bindgen_rt::maybe_link_cabi_realloc(); } From f94a487fa624c2c63985c79efbb54f74c58557bf Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Thu, 16 Jan 2025 23:59:44 +0900 Subject: [PATCH 03/15] refactor(host): use the existing adder WIT Signed-off-by: Victor Adossi --- component-model/examples/example-host/add.wit | 9 --------- component-model/examples/example-host/src/async_add.rs | 2 +- component-model/examples/example-host/src/sync_add.rs | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 component-model/examples/example-host/add.wit diff --git a/component-model/examples/example-host/add.wit b/component-model/examples/example-host/add.wit deleted file mode 100644 index e5777610..00000000 --- a/component-model/examples/example-host/add.wit +++ /dev/null @@ -1,9 +0,0 @@ -package docs:adder@0.1.0; - -interface add { - add: func(a: u32, b: u32) -> u32; -} - -world adder { - export add; -} diff --git a/component-model/examples/example-host/src/async_add.rs b/component-model/examples/example-host/src/async_add.rs index 1b497b29..e6359463 100644 --- a/component-model/examples/example-host/src/async_add.rs +++ b/component-model/examples/example-host/src/async_add.rs @@ -9,7 +9,7 @@ use crate::state::States; mod bindings { //! Generated code for the wasmtime::component::bindgen!({ - path: "add.wit", + path: "../tutorial/wit/adder/world.wit", world: "adder", async: true }); diff --git a/component-model/examples/example-host/src/sync_add.rs b/component-model/examples/example-host/src/sync_add.rs index 9bc3e151..f11758d7 100644 --- a/component-model/examples/example-host/src/sync_add.rs +++ b/component-model/examples/example-host/src/sync_add.rs @@ -9,7 +9,7 @@ use crate::state::States; mod bindings { wasmtime::component::bindgen!({ - path: "add.wit", + path: "../tutorial/wit/adder/world.wit", world: "adder", async: false }); From a0b018a36dd346ba6ed886376db6e4198d637d94 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Tue, 18 Mar 2025 17:55:31 +0900 Subject: [PATCH 04/15] wip: in-progress work Signed-off-by: Victor Adossi --- CONTRIBUTING.md | 1 + component-model/book.toml | 2 + component-model/src/language-support.md | 22 ++++++----- component-model/src/language-support/c.md | 37 ++++++++++++------- .../src/language-support/csharp.md | 22 ++++++----- 5 files changed, 52 insertions(+), 32 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d32df22d..e347452f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,7 @@ This repository also makes use of mdBook plugins. To install mdBook and the plug ```console cargo install --version 0.4.21 mdbook cargo install --version 0.6.7 mdbook-alerts +cargo install --version 0.7.7 mdbook-linkcheck ``` [cargo]: https://doc.rust-lang.org/cargo diff --git a/component-model/book.toml b/component-model/book.toml index e8c31230..72ef7f32 100644 --- a/component-model/book.toml +++ b/component-model/book.toml @@ -11,3 +11,5 @@ edit-url-template = "https://github.com/bytecodealliance/component-docs/tree/mai additional-css = ["theme/head.hbs"] [preprocessor.alerts] + +[output.linkcheck] diff --git a/component-model/src/language-support.md b/component-model/src/language-support.md index 0b71f3db..3756ba28 100644 --- a/component-model/src/language-support.md +++ b/component-model/src/language-support.md @@ -10,8 +10,8 @@ not hesitate to [contribute documentation](https://github.com/bytecodealliance/c One of the benefits of components is their portability across host runtimes. The runtime only needs to know what world the component is targeting in order to import or execute the component. This -language guide hopes to demonstrate that with a prevailing `example` world defined in -[`examples/example-host/add.wit`](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/add.wit). Furthermore, an example host that understands the `example` +language guide hopes to demonstrate that with a prevailing `adder` world defined in +[`examples/tutorial/wit/adder/world.wit`](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/wit/adder/world.wit). Furthermore, an example host that understands the `example` world has been provided in [`examples/example-host`](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/README.md) for running components. Each toolchain section walks through creating a component of this world, which can be run either in the example host or from an application of that toolchain. This aims to provide a full story for using @@ -46,17 +46,21 @@ run components for a given toolchain: [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools) provides a suite of subcommands for working with WebAssembly modules and components. -`wasm-tools` can be used to create a component from WebAssembly Text (WAT). This walks through creating a component from WAT that implements the [`example` world](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/add.wit) and simply adds two numbers. +`wasm-tools` can be used to create a component from WebAssembly Text (WAT). This walks through creating a component from WAT that implements the [`adder` world](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/wit/adder/world.wit) and simply adds two numbers. 1. Install [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools/tree/main#installation), a tool for low-level manipulation of Wasm modules and components. -2. The `add` function is defined inside the following `example` world: +2. The `add` function is defined inside the following `world` world: ```wit - package example:component; + package docs:adder@0.1.0; - world example { - export add: func(x: s32, y: s32) -> s32; + interface add { + add: func(a: u32, b: u32) -> u32; + } + + world adder { + export add; } ``` @@ -68,7 +72,7 @@ working with WebAssembly modules and components. local.get $lhs local.get $rhs i32.add) - (export "add" (func $add)) + (export "docs:adder/add@0.1.0" (func $add)) ) ``` @@ -76,7 +80,7 @@ working with WebAssembly modules and components. inside the core module and then encoding the WAT to a Wasm binary. ```sh - $ wasm-tools component embed add.wit add.wat -o add.wasm + $ wasm-tools component embed adder/world.wit add.wat -o add.wasm $ wasm-tools component new add.wasm -o add.component.wasm ``` diff --git a/component-model/src/language-support/c.md b/component-model/src/language-support/c.md index 57274e54..da619d3a 100644 --- a/component-model/src/language-support/c.md +++ b/component-model/src/language-support/c.md @@ -2,17 +2,18 @@ ### Building a Component with `wit-bindgen` and `wasm-tools` -[`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) is a tool to generate guest language bindings from a given `.wit` file. Although it is less integrated into language toolchains than other tools such as `cargo-component`, it can currently generate source-level bindings for `Rust`, `C`, `Java (TeaVM)`, and `TinyGo`, with the ability for more language generators to be added in the future. +[`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) is a tool to generate guest language bindings from a given `.wit` file. Although it is less integrated into language toolchains than other tools such as `cargo-component`, it can currently generate source-level bindings for `Rust`, `C`, `Java (TeaVM)`, and `TinyGo`, with the ability for more language generators to be added in the future. `wit-bindgen` can be used to generate C applications that can be compiled directly to Wasm modules using `clang` with a `wasm32-wasi` target. -First, install the CLI for [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen#cli-installation), [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools), and the [`WASI SDK`](https://github.com/webassembly/wasi-sdk). +First, install the CLI for [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen#cli-installation), [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools), and the [`WASI SDK`](https://github.com/webassembly/wasi-sdk). The WASI SDK will install a local version of `clang` configured with a wasi-sysroot. Follow [these instructions](https://github.com/WebAssembly/wasi-sdk#use) to configure it for use. Note that you can also use your installed system or emscripten `clang` by building with `--target=wasm32-wasi` but you will need some artifacts from WASI SDK to enable and link that build target (more information is available in WASI SDK's docs). -Start by generating a C skeleton from `wit-bindgen` using the [sample `add.wit` file](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit): -```sh ->wit-bindgen c add.wit +Start by generating a C skeleton from `wit-bindgen` using the [sample `adder/world.wit` file](https://github.com/bytecodealliance/component-docs/tree/main/examples/tutorial/wit/adder/world.wit): + +``` +> wit-bindgen c path/to/adder/world.wit Generating "example.c" Generating "example.h" Generating "example_component_type.o" @@ -21,6 +22,7 @@ Generating "example_component_type.o" This has generated several files - an `example.h` (based on the name of your `world`) with the prototype of the `add` function (prefixed by `exports_`) - `int32_t exports_example_add(int32_t x, int32_t y);`, as well as some generated code in `example.c` that interfaces with the component model ABI to call your function. Additionally, `example_component_type.o` contains object code referenced in `example.c` from an `extern` that must be linked via clang. Next, create an `add.c` that implements your function defined in `example.h`: + ```c #include "example.h" @@ -31,20 +33,23 @@ int32_t exports_example_add(int32_t x, int32_t y) ``` Now, you can compile the function into a Wasm module via clang: -```sh + +```console clang add.c example.c example_component_type.o -o add-core.wasm -mexec-model=reactor ``` > Use the `clang` included in the WASI SDK installation, for example at `/bin/clang`. Next, you need to transform the module into a component. For this example, you can use `wasm-tools component new`: -```sh + +```console wasm-tools component new ./add-core.wasm -o add-component.wasm ``` -Do note this will fail if your code references any WASI APIs that must be imported. This requires an additional step as the WASI SDK still references `wasi_snapshot_preview1` APIs that are not compatible directly with components. +Do note this will fail if your code references any WASI APIs that must be imported. This requires an additional step as the WASI SDK still references `wasi_snapshot_preview1` APIs that are not compatible directly with components. For example, modifying the above to reference `printf()` would compile: + ```c #include "example.h" #include @@ -58,25 +63,28 @@ int32_t exports_example_add(int32_t x, int32_t y) ``` However, the module would fail to transform to a component: -```sh + +``` >wasm-tools component new ./add-core.wasm -o add-component.wasm error: failed to encode a component from module Caused by: 0: failed to decode world from module 1: module was not valid - 2: module requires an import interface named `wasi_snapshot_preview1` + 2: module requires an import interface named `wasi_snapshot_preview1` ``` Install the appropriate reactor adapter module [as documented here](https://github.com/bytecodealliance/wit-bindgen#creating-components-wasi) - you can either get the linked release of `wasi_snapshot_preview1.reactor.wasm` and rename it to `wasi_snapshot_preview1.wasm`, or build it directly from source in `wasmtime` following the [instructions here](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-preview1-component-adapter) (make sure you `git submodule update --init` first). Now, you can adapt preview1 to preview2 to build a component: -```sh + +```console wasm-tools component new add-core.wasm --adapt wasi_snapshot_preview1.wasm -o add-component.wasm ``` Finally, you can inspect the embedded wit to see your component (including any WASI imports if necessary): -```sh + +``` >wasm-tools component wit add-component.wasm package root:component; @@ -101,7 +109,8 @@ world root { ``` You must use the `wasi_snapshot_preview1.wasm` from the same version of wasmtime that the host is using to ensure the WASI interface versions match. Additionally, the host must explicitly [add WASI to the linker](https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html) to run the app. If these are not configured correctly, you may see errors like the following: -```sh + +``` cargo run --release -- 1 2 add-component.wasm Compiling example-host v0.1.0 (/Users/sean/code/component-docs/component-model/examples/example-host) Finished `release` profile [optimized] target(s) in 7.85s @@ -116,7 +125,7 @@ Caused by: ### Running a Component from C/C++ Applications -It is not yet possible to run a Component using the `wasmtime` `c-api` - [see this issue](https://github.com/bytecodealliance/wasmtime/issues/6987). The c-api is preferred to trying to directly use the Rust crate in C++. +It is not yet possible to run a Component using the `wasmtime` `c-api` - [see this issue](https://github.com/bytecodealliance/wasmtime/issues/6987). The c-api is preferred to trying to directly use the Rust crate in C++. However, C/C++ language guest components can be composed with components written in any other language and run by their toolchains, or even composed with a C language command component and run via the `wasmtime` CLI or any other host. diff --git a/component-model/src/language-support/csharp.md b/component-model/src/language-support/csharp.md index 0b2bd982..4dc5930d 100644 --- a/component-model/src/language-support/csharp.md +++ b/component-model/src/language-support/csharp.md @@ -27,15 +27,19 @@ cd adder ``` Next, create or download the WIT world you would like to target. For this example we will use an -[`example` -world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit) +[`adder` +world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit) with an `add` function: ```wit -package example:component; +package docs:adder@0.1.0; -world example { - export add: func(x: u32, y: u32) -> u32; +interface add { + add: func(a: u32, b: u32) -> u32; +} + +world adder { + export add; } ``` @@ -43,7 +47,7 @@ In the `adder.csproj` project file, add a new ``: ```xml - + ``` @@ -95,7 +99,7 @@ in [the next section](#building-a-component-that-imports-an-interface) to demons component that *imports* an interface. ```wit -// add.wit +// adder/world.wit package example:component; interface add { @@ -160,7 +164,7 @@ cd host-app Copy the same WIT file as before into your project: ```wit -// add.wit +// adder/world.wit package example:component; interface add { @@ -180,7 +184,7 @@ Add it to your `host-app.csproj` project file as a new `ItemGroup`: ```xml - + ``` From 6d5f564053e31c780b006c349592c8d0078912e4 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Tue, 18 Mar 2025 18:15:44 +0900 Subject: [PATCH 05/15] refactor: tutorial prose Signed-off-by: Victor Adossi --- component-model/src/tutorial.md | 97 ++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/component-model/src/tutorial.md b/component-model/src/tutorial.md index 5d4a9513..6ed78176 100644 --- a/component-model/src/tutorial.md +++ b/component-model/src/tutorial.md @@ -2,21 +2,35 @@ If you like to learn by doing, this tutorial will walk through how to build, compose, and run components through a calculator example. Calculators can conduct many operations: add, subtract, -multiply, and so on. In this example, each operation will be a component, that will be composed with +multiply, and so on. + +In this example, each operation will be a component, that will be composed with an `eval-expression` component that will evaluate the expression using the expected operator. With one operation per component, this calculator is exaggeratedly granular to show how independent logic -of an application can be contained in a component. In production, components will likely have a -larger scope than a simple mathematical operation. +of an application can be contained in a component. + +In production, components will likely have a larger scope than a simple mathematical operation. + +Our eventual solution will involve three components: + +1. A calculator engine, +2. An addition operation +3. A command-line interface. -Our eventual solution will involve three components: one for the calculator engine, one for the -addition operation, and one for the command-line interface. Once we have built these as separate -Wasm components, we will compose them into a single runnable component, and test it using the -`wasmtime` CLI. +Once we have built these as separate Wasm components, we will compose them into a single runnable +component, and test it using the [`wasmtime` CLI][wasmtime]. + +[wasmtime]: https://wasmtime.dev/ ## The calculator interface -For tutorial purposes, we are going to put our "calculator engine" and "addition operation" interfaces into two separate WIT packages, each containing one WIT file. This may seem excessive, but the reason is to illustrate real-world use cases where components come from different authors and packages. -These files can be found in the component book repository in the [`wit` directory](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit) under `wit/adder/world.wit` and `wit/calculator/world.wit`: +For tutorial purposes, we are going to put our "calculator engine" and "addition operation" interfaces into two separate WIT packages, each containing one WIT file. + +This setup may seem excessive, but it illustrates a real-world use case where components come +from different authors and packages. + +These files can be found in the component book repository in the [`examples/tutorial/wit` directory](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit) under `wit/adder/world.wit` and `wit/calculator/world.wit`: + ```wit // wit/adder/world.wit package docs:adder@0.1.0; @@ -28,7 +42,9 @@ These files can be found in the component book repository in the [`wit` director world adder { export add; } +``` +```wit // wit/calculator/world.wit package docs:calculator@0.1.0; @@ -47,7 +63,6 @@ These files can be found in the component book repository in the [`wit` director world app { import calculate; } - ``` These files define: @@ -58,42 +73,52 @@ These files define: (such as `add`), meaning it relies on other components to perform those operations. * An interface `calculate` that contains an evaluate function and an enum that delineates the operations that can be involved in a calculation. In this tutorial, the only operation is `add`. -* A world `app` describing the "primary" app component, which imports the `calculate` interface. +* A world `app` describing the "primary" app component, which imports the `calculate` interface. This component will take in command line arguments and pass them to the `eval-expression` function of the calculator component. - ## Create an `add` component Reference the [language guide](language-support.md) and [authoring components documentation](creating-and-consuming/authoring.md) to create a component that implements the -`adder` world of `adder/wit/world.wit`. For reference, see the completed +`adder` world of `adder/wit/world.wit`. + +For reference, see the completed [example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/adder/). ## Create a `calculator` component Reference the [language guide](language-support.md) and [authoring components documentation](creating-and-consuming/authoring.md) to create a component that implements the -`calculator` world of `wit/calculator/world.wit`. For reference, see the completed -[example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/calculator/). The component should import the `add` function from the -`adder` world and call it if the `op` enum matches `add`. +`calculator` world of `wit/calculator/world.wit`. + +For reference, see the completed +[example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/calculator/). + +Once complete, the component should import the `add` function from the `adder` world and call it if the `op` enum matches `add`. ## Create a `command` component A _command_ is a component with a specific export that allows it to be executed directly by -`wasmtime` (or other `wasi:cli` hosts). The host expects it to export the [`wasi:cli/run` +`wasmtime` (or other `wasi:cli` hosts). + +The WebAssembly host expects it to export the [`wasi:cli/run` interface](https://github.com/WebAssembly/wasi-cli/blob/main/wit/run.wit), which is the equivalent -of the `main` function to WASI. `cargo-component` will automatically resolve a Rust `bin` package +of the [`main` function][wiki-entrypoint] to WASI. + +[`cargo-component`][cargo-component] will automatically resolve a Rust `bin` package with a `main` function to a component with `wasi:cli/run` exported. Scaffold a new Wasm application with a `command` component: -```sh +```console cargo component new command --command ``` This component will implement the [`app`](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/calculator.wit) world, which -imports the `calculate` interface. In `Cargo.toml`, point `cargo-component` to the WIT file and -specify that it should pull in bindings for the `app` world from the path to `calculator.wit`: +imports the `calculate` interface. + +In `Cargo.toml`, point `cargo-component` to the WIT file and specify that it should pull in bindings +for the `app` world from the path to `calculator.wit`: ```toml [package.metadata.component.target] @@ -108,19 +133,27 @@ Since the calculator world imports the `add` interface, the command component ne ``` Now, implement a command line application that: -1. takes in three arguments: two operands and the name of an operator ("1 2 add") -2. parses the operator name and ensures it is supported in the `op` enum -3. calls the `calculate` interface's `eval_expression`, passing in the arguments. +1. Takes in three arguments: two operands and the name of an operator ("1 2 add") +2. Parses the operator name and ensures it is supported in the `op` enum +3. Calls the `calculate` interface's `eval_expression`, passing in the arguments. For reference, see a completed [example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/command/). +[wiki-entrypoint]: https://en.wikipedia.org/wiki/Entry_point +[cargo-component]: https://crates.io/crates/cargo-component + ## Composing the calculator Now, we are ready to bring our components together into one runnable calculator component, using -`wac`. We will first compose the calculator component with the add component to satisfy it's -imports. We then compose that resolved calculator component with the command component to satisfy -its `calculate` imports. The result is a command component that has all its imports satisfied and -exports the `wasi:cli/run` function, which can be executed by `wasmtime`. +`wac`. + +We will: + +1. Compose the calculator component with the add component to satisfy the calculator component's `adder` import +2. Compose that resolved calculator component once more with the command component to satisfy the command component's `calculate` import. + +The result is a fully-formed command component that has all its imports satisfied and has a single +export (the `wasi:cli/run` interface), which can be executed by [`wasmtime`][wasmtime]. ```sh wac plug calculator.wasm --plug adder.wasm -o composed.wasm @@ -132,14 +165,16 @@ wac plug command.wasm --plug composed.wasm -o final.wasm ## Running the calculator Now it all adds up! Run the final component with the `wasmtime` CLI, ensuring you are using a -[`v14.0.0` or greater release](https://github.com/bytecodealliance/wasmtime/releases), as earlier releases of -the `wasmtime` command line do not include component model support. +[recent release][wasmtime-releases] (`v14.0.0` or greater), as earlier releases of +the `wasmtime` CLI do not include component model support. -```sh +``` wasmtime run final.wasm 1 2 add 1 + 2 = 3 ``` +[wasmtime-releases]: https://github.com/bytecodealliance/wasmtime/releases + ## To infinity and beyond! To expand the exercise to add more components, modify `calculator.wit` to add another operator world From 3eee31e91eea82b33fefee9a88012acbb7f2dd21 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Tue, 18 Mar 2025 18:17:22 +0900 Subject: [PATCH 06/15] chore(host): add note about async-std, update host Signed-off-by: Victor Adossi --- .../examples/example-host/Cargo.toml | 5 +- .../examples/example-host/README.md | 46 ++++++++++++++----- .../examples/example-host/src/async_add.rs | 2 +- .../examples/example-host/src/sync_add.rs | 2 +- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/component-model/examples/example-host/Cargo.toml b/component-model/examples/example-host/Cargo.toml index c25b417b..b364a905 100644 --- a/component-model/examples/example-host/Cargo.toml +++ b/component-model/examples/example-host/Cargo.toml @@ -2,10 +2,13 @@ name = "example-host" version = "0.1.0" edition = "2021" +description = """ +Example Rust-based WebAssembly host that executes WebAssembly components +""" [dependencies] +anyhow = "1.0.72" async-std = { version = "1.13", features = ["attributes"] } clap = { version = "4", features = ["derive"] } wasmtime = "27.0" wasmtime-wasi = "27.0" -anyhow = "1.0.72" diff --git a/component-model/examples/example-host/README.md b/component-model/examples/example-host/README.md index be423e54..085cc33c 100644 --- a/component-model/examples/example-host/README.md +++ b/component-model/examples/example-host/README.md @@ -1,25 +1,47 @@ # Rust Host Application for Example Components -This is a native Rust CLI application that can run components of the following -`example` world, which simply defines an add function that takes in two -parameters. +This is a native Rust CLI application that can run [WebAssembly Components][wasm-components] of the `adder` world, +defined in [`examples/tutorial/wit/adder/world.wit`][adder-wit], using [WebAssembly Interface Types ("WIT")][wit]. + +The `adder` world exports an interface called `add` which defines an function that takes two unsigned and adds them: ```wit -package example:component; +package docs:adder@0.1.0; + +interface add { + add: func(a: u32, b: u32) -> u32; +} -world example { - export add: func(x: s32, y: s32) -> s32; +world adder { + export add; } ``` -The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime) -crates to generate Rust bindings, bring in WASI worlds, and executes the `add` -function of the component. +The application uses WebAssembly ecosystem crates (e.g. [`wasmtime`][wasmtime]) to generate Rust bindings, instantiate WASI worlds, and +executes the exported `add` function (`docs:adder/add.add`) of a provided component. + +This host binary takes in two unsigned 32bit integers (`u32`) operands and a path to a component. This host then: -It takes in two operands and a path to a component. It passes the operands to -the component and prints the result generated by the component. +1. Loads the component from the given path +2. Instantiates it as an implementer of the `adder` world +3. Executes the `add` function exported by the component +4. Prints the result -```sh +If running with [`cargo`][cargo] (part of the [Rust toolchain][rust-toolchain]), then you should see output like the following: + +``` $ cargo run --release -- 1 2 add.wasm 1 + 2 = 3 ``` + +> [!NOTE] +> `add.wasm` is available in thsi folder, but can be replaced with your own built WebAssembly component +> at any time (written in any language that supports WebAssembly Components), given that it satisfies +> the `adder` world described above. + +[wasmtime]: https://github.com/bytecodealliance/wasmtime +[wasm-components]: https://component-model.bytecodealliance.org/design/components.html +[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit +[wit]: https://component-model.bytecodealliance.org/design/wit.html +[cargo]: https://doc.rust-lang.org/cargo/ +[rust-toolchain]: https://www.rust-lang.org/tools/install diff --git a/component-model/examples/example-host/src/async_add.rs b/component-model/examples/example-host/src/async_add.rs index e6359463..48ec9cea 100644 --- a/component-model/examples/example-host/src/async_add.rs +++ b/component-model/examples/example-host/src/async_add.rs @@ -18,7 +18,7 @@ mod bindings { /// Perform the add operation for a given WebAssembly component /// /// This operation asynchronously (as opposed to synchronously -/// without an async runtime like `tokio`). +/// without an async runtime like `tokio` or `async-std`). /// /// # Arguments /// diff --git a/component-model/examples/example-host/src/sync_add.rs b/component-model/examples/example-host/src/sync_add.rs index f11758d7..fa940f13 100644 --- a/component-model/examples/example-host/src/sync_add.rs +++ b/component-model/examples/example-host/src/sync_add.rs @@ -18,7 +18,7 @@ mod bindings { /// Perform a add operation for a given WebAssembly component /// /// This operation happens synchronously (as opposed to asynchronously -/// powered by an async runtime like `tokio`). +/// powered by an async runtime like `tokio` or `async-std`). /// /// # Arguments /// From b0e3f25577062d9d7e4dac29b1c2ff3c62ece70c Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Tue, 18 Mar 2025 19:28:03 +0900 Subject: [PATCH 07/15] chore(c): update C language guide Signed-off-by: Victor Adossi --- .../examples/tutorial/wit/adder/world.wit | 2 +- component-model/src/language-support/c.md | 141 ++++++++++++++---- 2 files changed, 112 insertions(+), 31 deletions(-) diff --git a/component-model/examples/tutorial/wit/adder/world.wit b/component-model/examples/tutorial/wit/adder/world.wit index 328f7f49..59a46856 100644 --- a/component-model/examples/tutorial/wit/adder/world.wit +++ b/component-model/examples/tutorial/wit/adder/world.wit @@ -1,7 +1,7 @@ package docs:adder@0.1.0; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world adder { diff --git a/component-model/src/language-support/c.md b/component-model/src/language-support/c.md index da619d3a..7e419a37 100644 --- a/component-model/src/language-support/c.md +++ b/component-model/src/language-support/c.md @@ -1,62 +1,107 @@ -## C/C++ Tooling +# C/C++ Tooling -### Building a Component with `wit-bindgen` and `wasm-tools` +WebAssembly components can be built from C and C++ using [`clang`][clang], the C language family frontend for [LLVM][llvm]. -[`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) is a tool to generate guest language bindings from a given `.wit` file. Although it is less integrated into language toolchains than other tools such as `cargo-component`, it can currently generate source-level bindings for `Rust`, `C`, `Java (TeaVM)`, and `TinyGo`, with the ability for more language generators to be added in the future. +[`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) is a tool to generate guest language bindings from a +given `.wit` file. -`wit-bindgen` can be used to generate C applications that can be compiled directly to Wasm modules using `clang` with a `wasm32-wasi` target. +Although `wit-bindgen` is a standalone tool (whereas some languages have more integrated toolchains [`cargo-component`][cargo-component]), +`wit-bindgen` can generate source-level bindings for `Rust`, `C`, `Java (TeaVM)`, and `TinyGo`, with the ability for more +language generators to be added in the future. + +`wit-bindgen` can be used to build C applications that can be compiled directly to Wasm modules using [`clang`][clang] with a [`wasm32-wasi`][clang-tgt-wasm32-wasi] target. + +[clang]: https://clang.llvm.org/ +[clang-tgt-wasm32-wasi]: https://clang.llvm.org/docs/ClangCommandLineReference.html#webassembly +[llvm]: https://llvm.org/ + +## 1. Download dependencies First, install the CLI for [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen#cli-installation), [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools), and the [`WASI SDK`](https://github.com/webassembly/wasi-sdk). The WASI SDK will install a local version of `clang` configured with a wasi-sysroot. Follow [these instructions](https://github.com/WebAssembly/wasi-sdk#use) to configure it for use. Note that you can also use your installed system or emscripten `clang` by building with `--target=wasm32-wasi` but you will need some artifacts from WASI SDK to enable and link that build target (more information is available in WASI SDK's docs). +## 2. Generate program skeleton from WIT + Start by generating a C skeleton from `wit-bindgen` using the [sample `adder/world.wit` file](https://github.com/bytecodealliance/component-docs/tree/main/examples/tutorial/wit/adder/world.wit): ``` > wit-bindgen c path/to/adder/world.wit -Generating "example.c" -Generating "example.h" -Generating "example_component_type.o" +Generating "adder.c" +Generating "adder.h" +Generating "adder_component_type.o" ``` -This has generated several files - an `example.h` (based on the name of your `world`) with the prototype of the `add` function (prefixed by `exports_`) - `int32_t exports_example_add(int32_t x, int32_t y);`, as well as some generated code in `example.c` that interfaces with the component model ABI to call your function. Additionally, `example_component_type.o` contains object code referenced in `example.c` from an `extern` that must be linked via clang. +This has generated several files: -Next, create an `add.c` that implements your function defined in `example.h`: +1.`adder.h` (based on the `adder` world) with the prototype of the `add` function (prefixed by `exports_`) - `uint32_t exports_docs_adder_add_add(uint32_t x, uint32_t y);`. +2. `adder.c` that interfaces with the component model ABI to call your function. +3. `adder_component_type.o` which contains object code referenced in `adder.c` from an `extern` that must be linked via `clang`. + +## 3. Write program code + +Next, create an `component.c` that implements the `adder` world (i.e. the interface defined in `adder.h`): ```c -#include "example.h" +#include "adder.h" -int32_t exports_example_add(int32_t x, int32_t y) +uint32_t exports_docs_adder_add_add(uint32_t x, uint32_t y) { return x + y; } ``` +## 4. Compile a WebAssembly module (P1) with `clang` + Now, you can compile the function into a Wasm module via clang: ```console -clang add.c example.c example_component_type.o -o add-core.wasm -mexec-model=reactor +clang component.c adder.c adder_component_type.o -o adder.wasm -mexec-model=reactor ``` > Use the `clang` included in the WASI SDK installation, for example at `/bin/clang`. +> +> Alternatively, you can also use the published [`ghcr.io/webassembly/wasi-sdk` container images][wasi-sdk-images] +> for performing builds. +> +> For example, to enter a container with `wasi-sdk` installed: +> +> ``` +> docker run --rm -it --mount type=bind,src=path/to/app/src,dst=/app ghcr.io/webassembly/wasi-sdk:wasi-sdk-25 +> ``` +> +> See also: [`Dockerfile` in `wasi-sdk`][wasi-sdk-dockerfile] -Next, you need to transform the module into a component. For this example, you can use `wasm-tools component new`: +[wasi-sdk-images]: https://github.com/WebAssembly/wasi-sdk/pkgs/container/wasi-sdk +[wasi-sdk-dockerfile]: https://github.com/WebAssembly/wasi-sdk/blob/main/docker/Dockerfile + +## 5. Convert the P1 component to a P2 component with `wasm-tools` + +Next, we need to transform the P1 component to a P2 component. To do this, we can use `wasm-tools component new`: ```console -wasm-tools component new ./add-core.wasm -o add-component.wasm +wasm-tools component new ./adder.wasm -o adder.component.wasm ``` -Do note this will fail if your code references any WASI APIs that must be imported. This requires an additional step as the WASI SDK still references `wasi_snapshot_preview1` APIs that are not compatible directly with components. +> [!NOTE] +> The `.component.` extension has no special meaning -- `.wasm` files can be either modules or components. + +## 6. (optional) Build a WASI-enabled WebAssembly (P2) component with `wasm-tools` + +Do note `wasm-tools component new` may fail if your code references any [WASI][wasi] APIs that must be imported, for +example via standard library imports like `stdio.h`. + +Using WASI interfaces requires an additional step as the WASI SDK still references `wasi_snapshot_preview1` APIs that are not compatible directly with components. For example, modifying the above to reference `printf()` would compile: ```c -#include "example.h" +#include "adder.h" #include -int32_t exports_example_add(int32_t x, int32_t y) +uint32_t exports_docs_adder_add_add(uint32_t x, uint32_t y) { - int32_t result = x + y; + uint32_t result = x + y; printf("%d", result); return result; } @@ -65,7 +110,7 @@ int32_t exports_example_add(int32_t x, int32_t y) However, the module would fail to transform to a component: ``` ->wasm-tools component new ./add-core.wasm -o add-component.wasm +>wasm-tools component new ./adder.wasm -o adder.component.wasm error: failed to encode a component from module Caused by: @@ -74,18 +119,24 @@ Caused by: 2: module requires an import interface named `wasi_snapshot_preview1` ``` -Install the appropriate reactor adapter module [as documented here](https://github.com/bytecodealliance/wit-bindgen#creating-components-wasi) - you can either get the linked release of `wasi_snapshot_preview1.reactor.wasm` and rename it to `wasi_snapshot_preview1.wasm`, or build it directly from source in `wasmtime` following the [instructions here](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-preview1-component-adapter) (make sure you `git submodule update --init` first). +To build a P2 component that uses [WASI][wasi] interfaces from a P1 component, we'll need to make use of adapter modules. + +Install the appropriate reactor adapter module [as documented here](https://github.com/bytecodealliance/wit-bindgen#creating-components-wasi). + +You can either get [the linked release][wasmtime-releases] of `wasi_snapshot_preview1.reactor.wasm` and rename it to `wasi_snapshot_preview1.wasm`, or build it directly from source in `wasmtime` following the [instructions here](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-preview1-component-adapter) (make sure you `git submodule update --init` first). Now, you can adapt preview1 to preview2 to build a component: ```console -wasm-tools component new add-core.wasm --adapt wasi_snapshot_preview1.wasm -o add-component.wasm +wasm-tools component new adder.wasm --adapt wasi_snapshot_preview1.wasm -o adder.component.wasm ``` +## 7. Inspect the built component + Finally, you can inspect the embedded wit to see your component (including any WASI imports if necessary): ``` ->wasm-tools component wit add-component.wasm +>wasm-tools component wit adder.component.wasm package root:component; world root { @@ -108,13 +159,34 @@ world root { ... ``` -You must use the `wasi_snapshot_preview1.wasm` from the same version of wasmtime that the host is using to ensure the WASI interface versions match. Additionally, the host must explicitly [add WASI to the linker](https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html) to run the app. If these are not configured correctly, you may see errors like the following: +### 8. Running the component from the example host + +> [!WARNING] +> You must be careful to use a version of the adapter (`wasi_snapshot_preview1.wasm`) that is compatible with the version of +> `wasmtime` that will be used, to ensure that WASI interface versions (and relevant implementation) match. + +This repository contains an [example WebAssembly host][example-host] written in Rust that can run components that implement the `adder` world. + +> [!NOTE] +> When hosts run components that use WASI interfaces, they must *explicitly* [add WASI to the linker][add-to-linker] to run the built component. + +A successful run should show the following output: ``` -cargo run --release -- 1 2 add-component.wasm - Compiling example-host v0.1.0 (/Users/sean/code/component-docs/component-model/examples/example-host) +cargo run --release -- 1 2 adder.component.wasm + Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) Finished `release` profile [optimized] target(s) in 7.85s - Running `target/release/example-host 1 2 add-component.wasm` + Running `target/debug/example-host 1 2 /tmp/docs/c/adder.component.wasm` +1 + 2 = 3 +``` + +If *not* configured correctly, you may see errors like the following: + +``` +cargo run --release -- 1 2 adder.component.wasm + Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) + Finished `release` profile [optimized] target(s) in 7.85s + Running `target/release/example-host 1 2 adder.component.wasm` Error: Failed to instantiate the example world Caused by: @@ -123,10 +195,19 @@ Caused by: 2: resource implementation is missing ``` -### Running a Component from C/C++ Applications +This kind of error normally indicates that the host in question does not contain satisfy WASI imports. + +[host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host +[add-to-linker]: https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html + +## 9. Running a Component from C/C++ Applications -It is not yet possible to run a Component using the `wasmtime` `c-api` - [see this issue](https://github.com/bytecodealliance/wasmtime/issues/6987). The c-api is preferred to trying to directly use the Rust crate in C++. +It is not yet possible to run a WebAssembly Component using the C API of `wasmtime` `c-api`. See [`wasmtime` issue #6987](https://github.com/bytecodealliance/wasmtime/issues/6987) for more details. +The c-api is preferred over directly using the example host Rust crate in C++. -However, C/C++ language guest components can be composed with components written in any other language and run by their toolchains, or even composed with a C language command component and run via the `wasmtime` CLI or any other host. +However, C/C++ language guest components can be composed with components written in any other language and +run by their toolchains, or even composed with a C language command component and run via the `wasmtime` CLI +or any other host. -See the [Rust Tooling guide](../language-support/rust.md#running-a-component-from-rust-applications) for instructions on how to run this component from the Rust `example-host` (replacing the path to `add.wasm` with your `add-component` above). +See the [Rust Tooling guide](../language-support/rust.md#running-a-component-from-rust-applications) for instructions on how to run this component from +the Rust `example-host` (replacing the path to `add.wasm` with your `add-component` above). From 84cd086ed051f76b8368873a169c6dd474a67143 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Tue, 18 Mar 2025 19:46:36 +0900 Subject: [PATCH 08/15] chore(lang/go): update Golang lang docs Signed-off-by: Victor Adossi --- component-model/src/language-support/go.md | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/component-model/src/language-support/go.md b/component-model/src/language-support/go.md index 79962f79..15b1a1f4 100644 --- a/component-model/src/language-support/go.md +++ b/component-model/src/language-support/go.md @@ -1,7 +1,11 @@ # Go Tooling -The [TinyGo compiler](https://tinygo.org/) v0.34.0 and above has native support for the WebAssembly Component Model and WASI 0.2.0. This guide walks through building a component that implements `example` world defined in the [`add.wit` -package](../../examples/example-host/add.wit). The component will implement a simple add function. +The [TinyGo compiler](https://tinygo.org/) v0.34.0 and above has native support for the WebAssembly Component Model and WASI 0.2.0. + +This guide walks through building a component that implements `adder` world defined in the [`adder/world.wit` package][adder-wit]. +The component will implement the `adder` world, which contains `add` interface with a `add` function. + +[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit ## 1. Install the tools @@ -20,7 +24,7 @@ Optional: Install the `wkg` CLI tool to resolve the imports in the WIT file. The ## 2. Determine which World the Component will Implement -The `wasip2` target of TinyGo assumes that the component is targeting `wasi:cli/command@0.2.0` world so it requires the imports of `wasi:cli/imports@0.2.0`. We need to include them in the `add.wit`. +The `wasip2` target of TinyGo assumes that the component is targeting `wasi:cli/command@0.2.0` world so it requires the imports of `wasi:cli/imports@0.2.0`. We need to include them in the `add.wit`. Tools like `wkg` can be convenient to build a complete WIT package by resolving the imports. @@ -36,7 +40,7 @@ world adder { Running the `wkg wit build` command will resolve the imports and generate the complete WIT file encoded as a Wasm component. ```console -$ wkg wit build +$ wkg wit build WIT package written to docs:adder@0.1.0.wasm ``` @@ -123,7 +127,7 @@ Under the hood, TinyGo invokes `wasm-tools` to embed the WIT file to the module $ tinygo build -target=wasip2 -o add.wasm --wit-package docs:adder@0.1.0.wasm --wit-world adder main.go ``` -We now have an add component that satisfies our `adder` world, exporting the `add` function, which +We now have an add component that satisfies our `adder` world, exporting the `add` function, which we can confirm using the `wasm-tools component wit` command: ```console @@ -144,12 +148,16 @@ world root { ## 5. Testing the `add` Component To run our add component, we need to use a host program with a WASI runtime that understands the -`example` world. We've provided an [`example-host`](../../examples/example-host/README.md) to do -just that. It calls the `add` function of a passed in component providing two operands. To use it, -clone this repository and run the Rust program: +`example` world -- we've provided an [`example-host`][example-host] that does just that. + +The example host calls the `add` function of a passed in component providing two operands. + +To use the example host, clone this repository and run the Rust program: ```console git clone git@github.com:bytecodealliance/component-docs.git cd component-docs/component-model/examples/example-host cargo run --release -- 1 2 /path/to/add.wasm ``` + +[example-host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host From cdb4a51c34f69463727df508e5f1224fe5b8b754 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Tue, 18 Mar 2025 19:46:56 +0900 Subject: [PATCH 09/15] fix(lang/py): update python lang docs Signed-off-by: Victor Adossi --- component-model/src/language-support/python.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/component-model/src/language-support/python.md b/component-model/src/language-support/python.md index a651cff7..0f606eeb 100644 --- a/component-model/src/language-support/python.md +++ b/component-model/src/language-support/python.md @@ -97,8 +97,8 @@ Component built successfully ## Running components from Python Applications -Wasm components can also be invoked from Python applications. This walks through using tooling -to call the [`app.wasm` component from the examples](../../examples/example-host/add.wasm). +Wasm components can also be invoked from Python applications. This section walks through using tooling +to call the [pre-built `app.wasm` component][add-wasm] in the examples. > `wasmtime-py` is only able to run components built with `componentize-py` when the `--stub-wasi` option is used at build time. This is because `wasmtime-py` does not yet support [resources](../design/wit.md#resources), and `componentize-py` by default generates components which use resources from the `wasi:cli` world. See [this example](https://github.com/bytecodealliance/componentize-py/tree/main/examples/sandbox) of using the `--stub-wasi` option to generate a `wasmtime-py`-compatible component. @@ -141,3 +141,5 @@ Run the Python host program: $ python3 host.py 1 + 2 = 3 ``` + +[add-wasm]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/add.wasm From bf164cd79647499ff6f5dac6d95b2ec2eb22a7f3 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Fri, 21 Mar 2025 22:31:49 +0900 Subject: [PATCH 10/15] refactor(lang/csharp): update csharp docs Signed-off-by: Victor Adossi --- .../src/language-support/csharp.md | 136 +++++++++++++----- 1 file changed, 104 insertions(+), 32 deletions(-) diff --git a/component-model/src/language-support/csharp.md b/component-model/src/language-support/csharp.md index 4dc5930d..d94df794 100644 --- a/component-model/src/language-support/csharp.md +++ b/component-model/src/language-support/csharp.md @@ -1,13 +1,14 @@ # C# Tooling -## Building a Component with `componentize-dotnet` +WebAssembly components in C# can be built with [componentize-dotnet][componentize-dotnet], +a a NuGet package that can be used to create a fully AOT-compiled +component, giving .NET developers a component experience comparable to those in Rust and TinyGo. + +[componentize-dotnet]: https://github.com/bytecodealliance/componentize-dotnet -[componentize-dotnet](https://github.com/bytecodealliance/componentize-dotnet) makes it easy to -compile your code to WebAssembly components using a single tool. This Bytecode Alliance project is a -NuGet package that can be used to create a fully AOT-compiled component, giving .NET developers a -component experience comparable to those in Rust and TinyGo. +## Building a Component with `componentize-dotnet` -componentize-dotnet serves as a one-stop shop for .NET developers, wrapping several tools into one: +[`componentize-dotnet`][componentize-dotnet] serves as a one-stop shop, wrapping several tools into one: - [NativeAOT-LLVM](https://github.com/dotnet/runtimelab/tree/feature/NativeAOT-LLVM) (compilation) - [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen) (WIT imports and exports) @@ -15,9 +16,14 @@ componentize-dotnet serves as a one-stop shop for .NET developers, wrapping seve - [WASI SDK](https://github.com/WebAssembly/wasi-sdk) (SDK used by NativeAOT-LLVM) - [Wac](https://github.com/bytecodealliance/wac) (used to compose components) -First, install the .NET SDK. For this walkthrough, we’ll use the [.NET 10 SDK preview](https://dotnet.microsoft.com/en-us/download/dotnet/10.0). +First, install the .NET SDK. For this walkthrough, we’ll use the [.NET 10 SDK preview][dotnet-sdk]. You should also have [wasmtime](https://wasmtime.dev/) installed so you can run the binary that you produce. +[dotnet-sdk]: https://dotnet.microsoft.com/en-us/download/dotnet/10.0 +[wasmtime]: https://wasmtime.dev/ + +## 1. Create a new project + Once you have the .NET SDK installed, create a new project: ```sh @@ -26,10 +32,11 @@ dotnet new componentize.wasi.cli -o adder cd adder ``` -Next, create or download the WIT world you would like to target. For this example we will use an -[`adder` -world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit) -with an `add` function: +## 2. Create or download your WIT world + +Next, create or download the WIT world you would like to target. + +For this example we will use the [`adder` world][adder-world], with an `add` function (e.g. to `wit/component.wit`): ```wit package docs:adder@0.1.0; @@ -47,32 +54,48 @@ In the `adder.csproj` project file, add a new ``: ```xml - + ``` -Since this component will only export a function dotnet considers this a library project. Let's update -the `` to be a library in the `adder.csproj`: +Since this component will only export a function dotnet considers this a library project. +Let's update the `` to be a library in the `adder.csproj`: -```xml -Library +```diff +- Exe ++ Library ``` -And remove the `Program.cs` file: +And remove the automatically generated `Program.cs` file: ```bash rm Program.cs ``` -At this point, if you try to build the project with `dotnet build`, you'll get an error like "The name -'ExampleWorldImpl' does not exist in the current context". This is because you've said you'll -provide an implementation, but haven't yet done so. To fix this, add the following code to your -project in a file called `adder.cs`: +[adder-world]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit + +## 3. Write the implementation for the `adder` world + +If you try to build the project with `dotnet build`, you'll get an error like the following: + +``` +➜ dotnet build +Restore complete (8.6s) +You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy + adder failed with 1 error(s) (25.6s) + /path/to/adder/obj/Debug/net10.0/wasi-wasm/wit_bindgen/AdderWorld.wit.exports.docs.adder.v0_1_0.AddInterop.cs(15,19): error CS0103: The name 'AddImpl' does not exist in the current context + +Build failed with 1 error(s) in 34.6s +``` + +This is because we've promised an implementation, but haven't yet written one for the `adder` world. + +To fix this, add the following code to your in a file called `Component.cs`: ```csharp -namespace ExampleWorld; +namespace AdderWorld; -public class ExampleWorldImpl : IExampleWorld +public class AdderWorldImpl : IAdderWorld { public static uint Add(uint x, uint y) { @@ -81,7 +104,7 @@ public class ExampleWorldImpl : IExampleWorld } ``` -If we build it: +Then, we can build our component: ```sh dotnet build @@ -89,9 +112,50 @@ dotnet build The component will be available at `bin/Debug/net10.0/wasi-wasm/native/adder.wasm`. +### 5. (optional) the component from the example host + +> [!WARNING] +> You must be careful to use a version of the adapter (`wasi_snapshot_preview1.wasm`) that is compatible with the version of +> `wasmtime` that will be used, to ensure that WASI interface versions (and relevant implementation) match. + +This repository contains an [example WebAssembly host][example-host] written in Rust that can run components that implement the `adder` world. + +> [!NOTE] +> When hosts run components that use WASI interfaces, they must *explicitly* [add WASI to the linker][add-to-linker] to run the built component. + +A successful run should show the following output: + +``` +cargo run --release -- 1 2 adder.component.wasm + Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) + Finished `release` profile [optimized] target(s) in 7.85s + Running `target/debug/example-host 1 2 /tmp/docs/c/adder.component.wasm` +1 + 2 = 3 +``` + +If *not* configured correctly, you may see errors like the following: + +``` +cargo run --release -- 1 2 adder.component.wasm + Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) + Finished `release` profile [optimized] target(s) in 7.85s + Running `target/release/example-host 1 2 adder.component.wasm` +Error: Failed to instantiate the example world + +Caused by: + 0: component imports instance `wasi:io/error@0.2.2`, but a matching implementation was not found in the linker + 1: instance export `error` has the wrong type + 2: resource implementation is missing +``` + +This kind of error normally indicates that the host in question does not contain satisfy WASI imports. + +[host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host +[add-to-linker]: https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html + ## Building a component that exports an interface -The previous example uses a WIT file that exports a function. However, you'll often prefer to export an interface, +The previous example uses a WIT file that exports a function. However, you'll often prefer to export an interface, either to comply with an existing specification or to capture a set of functions and types that tend to go together. Let's expand our `example` world to export an interface rather than directly export the function. We are also adding the `hostapp` world to our WIT file which we will implement @@ -116,8 +180,10 @@ world hostapp { ``` If you peek at the bindings, you'll notice that we now implement a class for the `add` interface -rather than for the `example` world. This is a consistent pattern. As you export more interfaces -from your world, you implement more classes. Our `adder.cs` example gets the slight update of: +rather than for the `example` world -- this is a consistent pattern. As you export more interfaces +from your world, you implement more classes. + +Our `Component.cs` example gets the slight update of: ```csharp namespace ExampleWorld.wit.exports.example.component; @@ -152,7 +218,9 @@ our `adder` component and call the `add` function. We will later compose this co the `adder` library component we just built. Now we will be taking the `adder` component and executing it from another WebAssembly component. -`dotnet new componentize.wasi.cli` creates a new project that creates an executable. + +`dotnet new componentize.wasi.cli` creates a new project that creates an executable. + Back out of the current project and create a new one: ```sh @@ -211,7 +279,7 @@ Once again, compile your component with `dotnet build`: $ dotnet build Restore complete (0.4s) You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy - host-app succeeded (1.1s) → bin/Debug/net9.0/wasi-wasm/host-app.dll + host-app succeeded (1.1s) → bin/Debug/net10.0/wasi-wasm/host-app.dll Build succeeded in 2.5s ``` @@ -226,7 +294,10 @@ world, it needs to be composed the first component. You can compose your `host-a your `adder` component by running [`wac plug`](https://github.com/bytecodealliance/wac): ```sh -wac plug bin/Debug/net10.0/wasi-wasm/native/host-app.wasm --plug ../adder/bin/Debug/net10.0/wasi-wasm/native/adder.wasm -o main.wasm +wac plug \ + bin/Debug/net10.0/wasi-wasm/native/host-app.wasm \ + --plug ../adder/bin/Debug/net10.0/wasi-wasm/native/adder.wasm \ + -o main.wasm ``` You can also automate the process by adding the following to your `host-app.csproj`: @@ -237,7 +308,6 @@ You can also automate the process by adding the following to your `host-app.cspr bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/host-app.wasm ../adder/bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/adder.wasm - @@ -252,4 +322,6 @@ wasmtime run ./dist/main.wasm 1 + 2 = 3 ``` -Checkout the [componentize-dotnet docs](https://github.com/bytecodealliance/componentize-dotnet) for more configurations options. +Check out the [componentize-dotnet docs][componentize-dotnet-docs] for more configurations options. + +[componentize-dotnet-docs]: https://github.com/bytecodealliance/componentize-dotnet From b0def7682d8211f8f9f3d7d957862eea2c9bb3df Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Fri, 21 Mar 2025 23:04:22 +0900 Subject: [PATCH 11/15] refactor(lang/go): update go lang docs Signed-off-by: Victor Adossi --- component-model/src/language-support/go.md | 256 +++++++++++++++++---- 1 file changed, 208 insertions(+), 48 deletions(-) diff --git a/component-model/src/language-support/go.md b/component-model/src/language-support/go.md index 15b1a1f4..5274529a 100644 --- a/component-model/src/language-support/go.md +++ b/component-model/src/language-support/go.md @@ -9,103 +9,262 @@ The component will implement the `adder` world, which contains `add` interface w ## 1. Install the tools -Follow the [TinyGo installation instructions](https://tinygo.org/getting-started/) to install the TinyGo compiler. Additionally, install the `wasm-tools` CLI tool from the [wasm-tools repository](https://github.com/bytecodealliance/wasm-tools/releases). +Follow the [TinyGo installation instructions](https://tinygo.org/getting-started/) to install the TinyGo compiler. + +Additionally, install the `wasm-tools` CLI tool from the [wasm-tools repository](https://github.com/bytecodealliance/wasm-tools/releases). + +> [!WARNING] +> Due to some upstream issues, only `wasm-tools` versions 1.225.0 or earlier can be used with `wit-bindgen-go` +> +> If using the Rust toolchain to install `wasm-tools`, it can be installed like so: +> `cargo install --locked wasm-tools@1.225.0 --force` To verify the installation, run the following commands: -```console +``` $ tinygo version tinygo version 0.34.0 ... $ wasm-tools -V -wasm-tools 1.219.1 ... +wasm-tools 1.255.0 ... ``` -Optional: Install the `wkg` CLI tool to resolve the imports in the WIT file. The `wkg` CLI is a part of the [Wasm Component package manager](https://github.com/bytecodealliance/wasm-pkg-tools/releases) +Optional: Install the [`wkg`][wkg] CLI tool to resolve the imports in the WIT file. The `wkg` CLI is a part of the [Wasm Component package manager](https://github.com/bytecodealliance/wasm-pkg-tools/releases) -## 2. Determine which World the Component will Implement +[wkg]: https://github.com/bytecodealliance/wasm-pkg-tools/tree/main/crates/wkg -The `wasip2` target of TinyGo assumes that the component is targeting `wasi:cli/command@0.2.0` world so it requires the imports of `wasi:cli/imports@0.2.0`. We need to include them in the `add.wit`. +## 2. Create your Go project -Tools like `wkg` can be convenient to build a complete WIT package by resolving the imports. +Now, create your Go project: + +```console +mkdir add && cd add +go mod init example.com +``` + +Ensure that the following `tool`s are installed: + +``` +tool ( + go.bytecodealliance.org/cmd/wit-bindgen-go +) +``` + +> [!NOTE] +> `go tool` was introduced in [Golang 1.24][go-1-24-release] and can be used to manage tooling in Go projects. + +Consider also running `go mod tidy` after adding the above tool. + +[go-1-24-release]: https://go.dev/blog/go1.24 + +## 2. Determine which World the Component will Implement + +Since we will be implementing the [`adder` world][adder-wit], we can copy the WIT to our project, +under the `wit` folder (e.g. `wit/component.wit`): ```wit -# wit/add.wit package docs:adder@0.1.0; + +interface add { + add: func(x: u32, y: u32) -> u32; +} + +world adder { + export add; +} +``` + +The `wasip2` target of TinyGo assumes that the component is targeting `wasi:cli/command@0.2.0` world +(part of [`wasi:cli`][wasi-cli]) so it requires the imports of `wasi:cli/imports@0.2.0`. + +We need to include those interfaces as well in `component.wit`, by editing the `adder` world: + +```wit world adder { include wasi:cli/imports@0.2.0; - export add: func(x: s32, y: s32) -> s32; + export add; } ``` -Running the `wkg wit build` command will resolve the imports and generate the complete WIT file encoded as a Wasm component. +### Using `wkg` to automatically resolve and download imports -```console +Tools like [`wkg`][wkg] can be convenient to build a complete WIT package by resolving the imports. + +Running the `wkg wit fetch` command will resolve the imports and populate your `wit` folder with all relevant +imported namespaces and packages. + +``` $ wkg wit build WIT package written to docs:adder@0.1.0.wasm ``` -Now, create your Go project: +[wasi-cli]: https://github.com/WebAssembly/wasi-cli -```console -$ mkdir add && cd add -$ go mod init example.com -``` +## 3. Generate bindings for the Wasm component -Next, we can generate the bindings for the Wasm component: +Now that we have our WIT definitions bundled together into a WASM file, +we can generate the bindings for our Wasm component, by adding a build directive: ```console -$ go get go.bytecodealliance.org/cmd/wit-bindgen-go -$ go run go.bytecodealliance.org/cmd/wit-bindgen-go generate -o internal/ ./docs:adder@0.1.0.wasm +go tool wit-bindgen-go generate --world adder --out internal ./docs:adder@0.1.0.wasm ``` +> [!NOTE] +> The `go tool` directive (added in [Golang 1.24][go-1-24-release]) installs and enables use of `wit-bindgen-go`, +> part of the Bytecode Alliance suite of Golang tooling. + The `internal` directory will contain the generated Go code that WIT package. ```console $ tree internal internal ├── docs -│ └── adder -│ └── adder -│ ├── adder.exports.go -│ ├── adder.wasm.go -│ ├── adder.wit -│ ├── adder.wit.go -│ └── empty.s +│   └── adder +│   ├── add +│   │   ├── add.exports.go +│   │   ├── add.wasm.go +│   │   ├── add.wit.go +│   │   └── empty.s +│   └── adder +│   └── adder.wit.go └── wasi ├── cli - │ └── stdout - │ ├── empty.s - │ ├── stdout.wasm.go - │ └── stdout.wit.go + │   ├── environment + │   │   ├── empty.s + │   │   ├── environment.wasm.go + │   │   └── environment.wit.go + │   ├── exit + │   │   ├── empty.s + │   │   ├── exit.wasm.go + │   │   └── exit.wit.go + │   ├── stderr + │   │   ├── empty.s + │   │   ├── stderr.wasm.go + │   │   └── stderr.wit.go + │   ├── stdin + │   │   ├── empty.s + │   │   ├── stdin.wasm.go + │   │   └── stdin.wit.go + │   ├── stdout + │   │   ├── empty.s + │   │   ├── stdout.wasm.go + │   │   └── stdout.wit.go + │   ├── terminal-input + │   │   ├── empty.s + │   │   ├── terminal-input.wasm.go + │   │   └── terminal-input.wit.go + │   ├── terminal-output + │   │   ├── empty.s + │   │   ├── terminal-output.wasm.go + │   │   └── terminal-output.wit.go + │   ├── terminal-stderr + │   │   ├── empty.s + │   │   ├── terminal-stderr.wasm.go + │   │   └── terminal-stderr.wit.go + │   ├── terminal-stdin + │   │   ├── empty.s + │   │   ├── terminal-stdin.wasm.go + │   │   └── terminal-stdin.wit.go + │   └── terminal-stdout + │   ├── empty.s + │   ├── terminal-stdout.wasm.go + │   └── terminal-stdout.wit.go + ├── clocks + │   ├── monotonic-clock + │   │   ├── empty.s + │   │   ├── monotonic-clock.wasm.go + │   │   └── monotonic-clock.wit.go + │   └── wall-clock + │   ├── empty.s + │   ├── wall-clock.wasm.go + │   └── wall-clock.wit.go + ├── filesystem + │   ├── preopens + │   │   ├── empty.s + │   │   ├── preopens.wasm.go + │   │   └── preopens.wit.go + │   └── types + │   ├── abi.go + │   ├── empty.s + │   ├── types.wasm.go + │   └── types.wit.go ├── io - │ ├── error - │ │ ├── empty.s - │ │ ├── error.wit.go - │ │ └── ioerror.wasm.go - │ └── streams - │ ├── empty.s - │ ├── streams.wasm.go - │ └── streams.wit.go - └── random - └── random + │   ├── error + │   │   ├── empty.s + │   │   ├── error.wasm.go + │   │   └── error.wit.go + │   ├── poll + │   │   ├── empty.s + │   │   ├── poll.wasm.go + │   │   └── poll.wit.go + │   └── streams + │   ├── empty.s + │   ├── streams.wasm.go + │   └── streams.wit.go + ├── random + │   ├── insecure + │   │   ├── empty.s + │   │   ├── insecure.wasm.go + │   │   └── insecure.wit.go + │   ├── insecure-seed + │   │   ├── empty.s + │   │   ├── insecure-seed.wasm.go + │   │   └── insecure-seed.wit.go + │   └── random + │   ├── empty.s + │   ├── random.wasm.go + │   └── random.wit.go + └── sockets + ├── instance-network + │   ├── empty.s + │   ├── instance-network.wasm.go + │   └── instance-network.wit.go + ├── ip-name-lookup + │   ├── abi.go + │   ├── empty.s + │   ├── ip-name-lookup.wasm.go + │   └── ip-name-lookup.wit.go + ├── network + │   ├── abi.go + │   ├── empty.s + │   ├── network.wasm.go + │   └── network.wit.go + ├── tcp + │   ├── abi.go + │   ├── empty.s + │   ├── tcp.wasm.go + │   └── tcp.wit.go + ├── tcp-create-socket + │   ├── empty.s + │   ├── tcp-create-socket.wasm.go + │   └── tcp-create-socket.wit.go + ├── udp + │   ├── abi.go + │   ├── empty.s + │   ├── udp.wasm.go + │   └── udp.wit.go + └── udp-create-socket ├── empty.s - ├── random.wasm.go - └── random.wit.go + ├── udp-create-socket.wasm.go + └── udp-create-socket.wit.go + +39 directories, 91 files ``` The `adder.exports.go` file contains the exported functions that need to be implemented in the Go code called `Exports`. -## 3. Implement the `add` Function +## 4. Implement the `add` Function ```Go +//go:generate go tool wit-bindgen-go generate --world adder --out internal ./docs:adder@0.1.0.wasm + package main import ( - "example.com/internal/docs/adder/adder" + "example.com/internal/docs/adder/add" ) func init() { - adder.Exports.Add = func(x int32, y int32) int32 { + add.Exports.Add = func(x uint32, y uint32) uint32 { return x + y } } @@ -117,18 +276,19 @@ func main() {} Go's `init` functions are used to do initialization tasks that should be done before any other tasks. In this case, we are using it to export the `Add` function. -## 4. Build the Component +## 5. Build the Component We can build our component using TinyGo by specifying the wit-package to be `add.wit` and the WIT world to be `adder`. Under the hood, TinyGo invokes `wasm-tools` to embed the WIT file to the module and componentize it. ```console -$ tinygo build -target=wasip2 -o add.wasm --wit-package docs:adder@0.1.0.wasm --wit-world adder main.go +tinygo build -target=wasip2 -o add.wasm --wit-package docs:adder@0.1.0.wasm --wit-world adder main.go ``` We now have an add component that satisfies our `adder` world, exporting the `add` function, which -we can confirm using the `wasm-tools component wit` command: + +We can confirm using the `wasm-tools component wit` command: ```console $ wasm-tools component wit add.wasm From 246a61042d9376aff1980801fe384cbeb0dbdfa7 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Fri, 21 Mar 2025 23:14:36 +0900 Subject: [PATCH 12/15] refactor(lang/rust): update rust language guide Signed-off-by: Victor Adossi --- component-model/src/language-support/rust.md | 81 ++++++++++++++++---- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/component-model/src/language-support/rust.md b/component-model/src/language-support/rust.md index 5f2d9070..ffb24a38 100644 --- a/component-model/src/language-support/rust.md +++ b/component-model/src/language-support/rust.md @@ -1,9 +1,11 @@ # Components in Rust -Rust has first-class support for the component model via [the `cargo component` tool](https://github.com/bytecodealliance/cargo-component). It is a `cargo` subcommand for -creating WebAssembly components using Rust as the component's implementation language. +Rust has first-class support for the component model via [the `cargo component` tool](https://github.com/bytecodealliance/cargo-component). -## Installing `cargo component` +`cargo component` is is a `cargo` subcommand for creating WebAssembly components +using Rust as the component's implementation language. + +## 1. Installing `cargo component` To install `cargo component`, run: @@ -13,58 +15,101 @@ cargo install cargo-component > You can find more details about `cargo component` in its [crates.io page](https://crates.io/crates/cargo-component). -## Building a Component with `cargo component` +## 2. Scaffold a Component with `cargo component` + +Create a Rust library that implements the `add` function in the [`adder`world][adder-wit]. -Create a Rust library that implements the `add` function in the [`example` -world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit). First scaffold a project: +First scaffold a project: ```sh $ cargo component new add --lib && cd add ``` -Note that `cargo component` generates the necessary bindings as a module called `bindings`. +Note that `cargo component` generates the necessary bindings as a module called `bindings`. + +## 3. Add the WIT world the Component will implement -Next, update `wit/world.wit` to match `add.wit` and modify the component package reference to change the -package name to `example:component`. The `component` section of `Cargo.toml` should look like the following: +Next, update `wit/world.wit` to match `add.wit`: + +``` +package docs:adder@0.1.0; + +interface add { + add: func(x: u32, y: u32) -> u32; +} + +world adder { + export add; +} +``` + +The `component` section of `Cargo.toml` should look like the following: ```toml [package.metadata.component] -package = "example:component" +package = "docs:adder" ``` -`cargo component` will generate bindings for the world specified in a package's `Cargo.toml`. In particular, it will create a `Guest` trait that a component should implement. Since our `example` world has no interfaces, the trait lives directly under the bindings module. Implement the `Guest` trait in `add/src/lib.rs` such that it satisfies the `example` world, adding an `add` function: +## 4. Generate bindings for our component + +After performing these changes, we can re-generate bindings with `cargo component bindings`: + +```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. + +## 5. Implement the generated `Guest` trait + +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 { - x + y + return x + y; } } + +bindings::export!(Component with_types_in bindings); ``` +## 6. Build the component + Now, use `cargo component` to build the component, being sure to optimize with a release build. -```sh -$ cargo component build --release +```console +cargo component build --release ``` You can use `wasm-tools component wit` to output the WIT package of the component: -```sh +``` $ wasm-tools component wit target/wasm32-wasip1/release/add.wasm package root:component; world root { - export add: func(x: s32, y: s32) -> s32; + export docs:adder/add@0.1.0; +} +package docs:adder@0.1.0 { + interface add { + add: func(x: u32, y: u32) -> u32; + } } ``` ### Running a Component from Rust Applications To verify that our component works, lets run it from a Rust application that knows how to run a -component targeting the [`example` world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit). +component targeting the [`adder` world][adder-wit]. The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime) crates to generate Rust bindings, bring in WASI worlds, and execute the component. @@ -534,3 +579,5 @@ If you are hosting a Wasm runtime, you can export a resource from your host for // etc. } ``` + +[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit From d5a64e506b0cc44b432ee8e8af4b537d1d75611a Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Fri, 21 Mar 2025 23:24:58 +0900 Subject: [PATCH 13/15] refactor(lang/py): update python docs Signed-off-by: Victor Adossi --- .../src/language-support/python.md | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/component-model/src/language-support/python.md b/component-model/src/language-support/python.md index 0f606eeb..a2dfe241 100644 --- a/component-model/src/language-support/python.md +++ b/component-model/src/language-support/python.md @@ -11,41 +11,51 @@ First, install [Python 3.10 or later](https://www.python.org/) and [pip](https:/ pip install componentize-py ``` -Next, create or download the WIT world you would like to target. For this example we will use an [`example` -world](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit) with an `add` function: +Next, create or download the WIT world you would like to target. For this example we will use an [`adder` +world][adder-wit] with an `add` function (e.g. `wit/component.wit`): ```wit -package example:component; +package docs:adder@0.1.0; -world example { - export add: func(x: s32, y: s32) -> s32; +interface add { + add: func(x: u32, y: u32) -> u32; +} + +world adder { + export add; } ``` If you want to generate bindings produced for the WIT world (for an IDE or typechecker), you can generate them using the `bindings` subcommand. Specify the path to the WIT interface with the world you are targeting: ```sh -$ componentize-py --wit-path /path/to/examples/example-host/add.wit --world example bindings . +$ componentize-py --wit-path wit/component/wit --world adder bindings . ``` +> [!NOTE] > You do not need to generate the bindings in order to `componentize` in the next step. `componentize` will generate bindings on-the-fly and bundle them into the produced component. -You can see that bindings were created in an `example` package which contains an `Example` protocol with an `add` method that we can implement: +Bindings were created in an `adder` package which contains an `Add` protocol with an `add` method that we can implement. -```sh -$ cat<> app.py -import example +To implement this interface, put the following code in a file called `app.py`: + +```py +import adder -class Example(example.Example): +class Add(adder.Adder): def add(self, x: int, y: int) -> int: return x + y -EOT ``` We now can compile our application to a Wasm component using the `componentize` subcommand: -```sh -$ componentize-py --wit-path /path/to/examples/example-host/add.wit --world example componentize app -o add.wasm +```console +componentize-py \ + --wit-path wit/component.wit \ + --world adder \ + componentize \ + app \ + -o add.wasm Component built successfully ``` @@ -59,7 +69,6 @@ $ cargo run --release -- 1 2 ../path/to/add.wasm See [`componentize-py`'s examples](https://github.com/bytecodealliance/componentize-py/tree/main/examples) to try out build HTTP, CLI, and TCP components from Python applications. - ### Building a Component that Exports an Interface The [sample `add.wit` file](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit) exports a function. However, you'll often prefer to export an interface, either to comply with an existing specification or to capture a set of functions and types that tend to go together. Let's expand our example world to export an interface rather than directly export the function. @@ -143,3 +152,5 @@ $ python3 host.py ``` [add-wasm]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/add.wasm + +[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit From 12b0eebd1cecea0c444315eaaa6162a16ab52df4 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Fri, 21 Mar 2025 23:27:14 +0900 Subject: [PATCH 14/15] refactor: a/b -> x/y Signed-off-by: Victor Adossi --- component-model/examples/example-host/README.md | 2 +- component-model/examples/tutorial/README.md | 2 +- component-model/examples/tutorial/adder/src/bindings.rs | 2 +- component-model/examples/tutorial/adder/src/lib.rs | 2 +- component-model/examples/tutorial/calculator/src/bindings.rs | 2 +- component-model/src/language-support.md | 2 +- component-model/src/language-support/csharp.md | 2 +- component-model/src/language-support/python.md | 2 +- component-model/src/language-support/rust.md | 5 +++-- component-model/src/tutorial.md | 2 +- 10 files changed, 12 insertions(+), 11 deletions(-) diff --git a/component-model/examples/example-host/README.md b/component-model/examples/example-host/README.md index 085cc33c..651d66ca 100644 --- a/component-model/examples/example-host/README.md +++ b/component-model/examples/example-host/README.md @@ -9,7 +9,7 @@ The `adder` world exports an interface called `add` which defines an function th package docs:adder@0.1.0; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world adder { diff --git a/component-model/examples/tutorial/README.md b/component-model/examples/tutorial/README.md index 7d7344be..54c37f2e 100644 --- a/component-model/examples/tutorial/README.md +++ b/component-model/examples/tutorial/README.md @@ -10,7 +10,7 @@ package docs:adder@0.1.0; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world adder { diff --git a/component-model/examples/tutorial/adder/src/bindings.rs b/component-model/examples/tutorial/adder/src/bindings.rs index 7c3b88b1..245617df 100644 --- a/component-model/examples/tutorial/adder/src/bindings.rs +++ b/component-model/examples/tutorial/adder/src/bindings.rs @@ -20,7 +20,7 @@ pub mod exports { _rt::as_i32(result0) } pub trait Guest { - fn add(a: u32, b: u32) -> u32; + fn add(x: u32, y: u32) -> u32; } #[doc(hidden)] macro_rules! __export_docs_adder_add_0_1_0_cabi { diff --git a/component-model/examples/tutorial/adder/src/lib.rs b/component-model/examples/tutorial/adder/src/lib.rs index ecab073e..ec5d396d 100644 --- a/component-model/examples/tutorial/adder/src/lib.rs +++ b/component-model/examples/tutorial/adder/src/lib.rs @@ -6,7 +6,7 @@ use bindings::exports::docs::adder::add::Guest; struct Component; impl Guest for Component { - fn add(a: u32, b: u32) -> u32 { + fn add(x: u32, y: u32) -> u32 { a + b } } diff --git a/component-model/examples/tutorial/calculator/src/bindings.rs b/component-model/examples/tutorial/calculator/src/bindings.rs index 43f23228..8511915b 100644 --- a/component-model/examples/tutorial/calculator/src/bindings.rs +++ b/component-model/examples/tutorial/calculator/src/bindings.rs @@ -13,7 +13,7 @@ pub mod docs { super::super::super::__link_custom_section_describing_imports; use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] - pub fn add(a: u32, b: u32) -> u32 { + pub fn add(x: u32, y: u32) -> u32 { unsafe { #[cfg(target_arch = "wasm32")] #[link(wasm_import_module = "docs:adder/add@0.1.0")] diff --git a/component-model/src/language-support.md b/component-model/src/language-support.md index 3756ba28..1eb02c53 100644 --- a/component-model/src/language-support.md +++ b/component-model/src/language-support.md @@ -56,7 +56,7 @@ working with WebAssembly modules and components. package docs:adder@0.1.0; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world adder { diff --git a/component-model/src/language-support/csharp.md b/component-model/src/language-support/csharp.md index d94df794..de2009a3 100644 --- a/component-model/src/language-support/csharp.md +++ b/component-model/src/language-support/csharp.md @@ -42,7 +42,7 @@ For this example we will use the [`adder` world][adder-world], with an `add` fun package docs:adder@0.1.0; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world adder { diff --git a/component-model/src/language-support/python.md b/component-model/src/language-support/python.md index a2dfe241..aae92677 100644 --- a/component-model/src/language-support/python.md +++ b/component-model/src/language-support/python.md @@ -78,7 +78,7 @@ The [sample `add.wit` file](https://github.com/bytecodealliance/component-docs/t package example:component; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world example { diff --git a/component-model/src/language-support/rust.md b/component-model/src/language-support/rust.md index ffb24a38..f03e531b 100644 --- a/component-model/src/language-support/rust.md +++ b/component-model/src/language-support/rust.md @@ -128,7 +128,7 @@ The [sample `add.wit` file](https://github.com/bytecodealliance/component-docs/t package docs:adder@0.1.0; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world adder { @@ -140,13 +140,14 @@ you would write the following Rust code: ```rust mod bindings; + // Separating out the interface puts it in a sub-module use bindings::exports::docs::adder::add::Guest; struct Component; impl Guest for Component { - fn add(a: u32, b: u32) -> u32 { + fn add(x: u32, y: u32) -> u32 { a + b } } diff --git a/component-model/src/tutorial.md b/component-model/src/tutorial.md index 6ed78176..c4f35428 100644 --- a/component-model/src/tutorial.md +++ b/component-model/src/tutorial.md @@ -36,7 +36,7 @@ These files can be found in the component book repository in the [`examples/tuto package docs:adder@0.1.0; interface add { - add: func(a: u32, b: u32) -> u32; + add: func(x: u32, y: u32) -> u32; } world adder { From 980c0f76dd00c5b1e12cae1bd7139c19025c69f6 Mon Sep 17 00:00:00 2001 From: Victor Adossi Date: Sat, 22 Mar 2025 11:03:01 +0900 Subject: [PATCH 15/15] fix(lang/c): add missing links Signed-off-by: Victor Adossi --- component-model/src/language-support/c.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/component-model/src/language-support/c.md b/component-model/src/language-support/c.md index 7e419a37..153eeb4d 100644 --- a/component-model/src/language-support/c.md +++ b/component-model/src/language-support/c.md @@ -5,7 +5,7 @@ WebAssembly components can be built from C and C++ using [`clang`][clang], the C [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) is a tool to generate guest language bindings from a given `.wit` file. -Although `wit-bindgen` is a standalone tool (whereas some languages have more integrated toolchains [`cargo-component`][cargo-component]), +Although `wit-bindgen` is a standalone tool (whereas some languages have more integrated toolchains like Rust's [`cargo-component`][cargo-component]), `wit-bindgen` can generate source-level bindings for `Rust`, `C`, `Java (TeaVM)`, and `TinyGo`, with the ability for more language generators to be added in the future. @@ -14,6 +14,8 @@ language generators to be added in the future. [clang]: https://clang.llvm.org/ [clang-tgt-wasm32-wasi]: https://clang.llvm.org/docs/ClangCommandLineReference.html#webassembly [llvm]: https://llvm.org/ +[wasi]: https://wasi.dev/ +[cargo-component]: https://crates.io/crates/cargo-component ## 1. Download dependencies @@ -131,6 +133,8 @@ Now, you can adapt preview1 to preview2 to build a component: wasm-tools component new adder.wasm --adapt wasi_snapshot_preview1.wasm -o adder.component.wasm ``` +[wasmtime-releases]: https://github.com/bytecodealliance/wasmtime/releases + ## 7. Inspect the built component Finally, you can inspect the embedded wit to see your component (including any WASI imports if necessary): @@ -199,6 +203,7 @@ This kind of error normally indicates that the host in question does not contain [host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host [add-to-linker]: https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html +[example-host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host ## 9. Running a Component from C/C++ Applications