From 3aed80812e105cd99c43f11023012608aecd9f4a Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 25 Oct 2023 19:41:47 +0100 Subject: Add a component model example This adds the 'Hello World' example from here[0]. The runtime is in Rust and the component is in C. This requires some new tools - https://github.com/bytecodealliance/wit-bindgen - https://github.com/bytecodealliance/wasm-tools You can use the pre-built packages, once downloaded and untar'd, edit rust/hello_world/component/Makefile and adjust the paths for the above tools. To build the component with make you may need to specify where the WASI sysroot is $ make WASI_SYSROOT=/path/to/wasi-sysroot [0]: Signed-off-by: Andrew Clayton --- README | 32 ++++++++++++++++ rust/.gitignore | 3 ++ rust/hello_world/Cargo.toml | 10 +++++ rust/hello_world/component/.gitignore | 1 + rust/hello_world/component/Makefile | 27 ++++++++++++++ rust/hello_world/component/my-component.c | 14 +++++++ rust/hello_world/src/main.rs | 62 +++++++++++++++++++++++++++++++ rust/hello_world/wit/my-component.wit | 8 ++++ 8 files changed, 157 insertions(+) create mode 100644 rust/.gitignore create mode 100644 rust/hello_world/Cargo.toml create mode 100644 rust/hello_world/component/.gitignore create mode 100644 rust/hello_world/component/Makefile create mode 100644 rust/hello_world/component/my-component.c create mode 100644 rust/hello_world/src/main.rs create mode 100644 rust/hello_world/wit/my-component.wit diff --git a/README b/README index 822d1df..7e13c9a 100644 --- a/README +++ b/README @@ -70,3 +70,35 @@ Getting function exports... [cabi_realloc] Shutting down... Done. + + +Component Model +=============== + +You can play with the component model under rust/hello_world + +This requires some new tools + + - https://github.com/bytecodealliance/wit-bindgen + - https://github.com/bytecodealliance/wasm-tools + + You can use the pre-built packages, once downloaded and untar'd, edit + + rust/hello_world/component/Makefile + + and adjust the paths for the above tools. + + To build the component with make you may need to specify where the WASI + sysroot is + + $ make WASI_SYSROOT=/path/to/wasi-sysroot + +You can then build the Rust runtime under rust/hello_world with + + $ cargo build + +after a while (sheesh) it should finish... then you can run it with + + $ target/debug/hello_world + +it won't actually print anything, but also shouldn't give an error. diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..559d663 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,3 @@ +Cargo.lock + +target/ diff --git a/rust/hello_world/Cargo.toml b/rust/hello_world/Cargo.toml new file mode 100644 index 0000000..7051e1b --- /dev/null +++ b/rust/hello_world/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "hello_world" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wasmtime = "14.0.1" +wasmtime-wasi = "14.0.1" diff --git a/rust/hello_world/component/.gitignore b/rust/hello_world/component/.gitignore new file mode 100644 index 0000000..5e4f2b2 --- /dev/null +++ b/rust/hello_world/component/.gitignore @@ -0,0 +1 @@ +hello_world* diff --git a/rust/hello_world/component/Makefile b/rust/hello_world/component/Makefile new file mode 100644 index 0000000..a7f3f85 --- /dev/null +++ b/rust/hello_world/component/Makefile @@ -0,0 +1,27 @@ +# Look for wasi-sysroot in some common places, falling back +# to provided WASI_SYSROOT +ifneq ("$(wildcard /usr/wasm32-wasi)", "") + # Fedora + WASI_SYSROOT ?= /usr/wasm32-wasi +else ifneq ("$(wildcard /usr/local/share/wasi-sysroot)", "") + # FreeBSD + WASI_SYSROOT ?= /usr/local/share/wasi-sysroot +endif + +export WASI_SYSROOT + +CC = clang +CFLAGS = -Wall -Wextra --target=wasm32-wasi --sysroot=$(WASI_SYSROOT) +LDFLAGS = -Wl,--no-entry -mexec-model=reactor --rtlib=compiler-rt + +all: hello_world.wasm + +bindgen: + /home/andrew/src/c/wasm/wit-bindgen-v0.13.0-x86_64-linux/wit-bindgen c ../wit + +hello_world.wasm: bindgen my-component.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ hello_world.c hello_world_component_type.o my-component.c + /home/andrew/src/c/wasm/wasm-tools-1.0.48-x86_64-linux/wasm-tools component new hello_world.wasm -o hello_world-component.wasm + +clean: + rm -f hello_world* diff --git a/rust/hello_world/component/my-component.c b/rust/hello_world/component/my-component.c new file mode 100644 index 0000000..fe09975 --- /dev/null +++ b/rust/hello_world/component/my-component.c @@ -0,0 +1,14 @@ +/* + * my-component.c + */ + +#include "hello_world.h" + +void hello_world_greet(void) +{ + hello_world_string_t my_string; + + hello_world_string_set(&my_string, "Hello, world!"); + + hello_world_name(&my_string); +} diff --git a/rust/hello_world/src/main.rs b/rust/hello_world/src/main.rs new file mode 100644 index 0000000..98b16bb --- /dev/null +++ b/rust/hello_world/src/main.rs @@ -0,0 +1,62 @@ +/* + * Example from + * https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html + */ + +use wasmtime::component::*; +use wasmtime::{Config, Engine, Store}; + +bindgen!(); + +struct MyState { + name: String, +} + +// Imports into the world, like the `name` import for this world, are satisfied +// through traits. +impl HelloWorldImports for MyState { + // Note the `Result` return value here where `Ok` is returned back to + // the component and `Err` will raise a trap. + fn name(&mut self) -> wasmtime::Result { + Ok(self.name.clone()) + } +} + +fn main() -> wasmtime::Result<()> { + // Configure an `Engine` and compile the `Component` that is being run for + // the application. + let mut config = Config::new(); + config.wasm_component_model(true); + let engine = Engine::new(&config)?; + let component = Component::from_file(&engine, + "component/hello_world-component.wasm")?; + + // Instantiation of bindings always happens through a `Linker`. + // Configuration of the linker is done through a generated `add_to_linker` + // method on the bindings structure. + // + // Note that the closure provided here is a projection from `T` in + // `Store` to `&mut U` where `U` implements the `HelloWorldImports` + // trait. In this case the `T`, `MyState`, is stored directly in the + // structure so no projection is necessary here. + let mut linker = Linker::new(&engine); + HelloWorld::add_to_linker(&mut linker, |state: &mut MyState| state)?; + + // As with the core wasm API of Wasmtime instantiation occurs within a + // `Store`. The bindings structure contains an `instantiate` method which + // takes the store, component, and linker. This returns the `bindings` + // structure which is an instance of `HelloWorld` and supports typed access + // to the exports of the component. + let mut store = Store::new( + &engine, + MyState { + name: "me".to_string(), + }, + ); + let (bindings, _) = HelloWorld::instantiate(&mut store, &component, &linker)?; + + // Here our `greet` function doesn't take any parameters for the component, + // but in the Wasmtime embedding API the first argument is always a `Store`. + bindings.call_greet(&mut store)?; + Ok(()) +} diff --git a/rust/hello_world/wit/my-component.wit b/rust/hello_world/wit/my-component.wit new file mode 100644 index 0000000..5ce6b6f --- /dev/null +++ b/rust/hello_world/wit/my-component.wit @@ -0,0 +1,8 @@ +// wit/my-component.wit + +package my:project; + +world hello-world { + import name: func() -> string; + export greet: func(); +} -- cgit