forked from bytecodealliance/wasmtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revamp wasi example and related docs (bytecodealliance#9788)
* update examples/wasi/main.rs and related doc with a runtime issue * simplify building instruction * fix runtime issue * add a comment and cargo fmt * revamped wasi-async * remove the example of custom host states and add references * add calling function dynamically * cargo fmt * add post_return * update wording * add expects * change wording from "module" to "component" * fix CMakeLists.txt * compile wasi example with wasip1 to make the outdated WASIp1 C example happy * rename wasi examples * fix wording * fix format * add wasm32-wasip2 target * Revert "add wasm32-wasip2 target" This reverts commit 0aa610f. * add wasm32-wasip2 target for test_capi job
- Loading branch information
Showing
12 changed files
with
179 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,61 @@ | ||
# WASI | ||
# WASIp2 | ||
|
||
You can also [browse this source code online][code] and clone the wasmtime | ||
repository to run the example locally. | ||
|
||
[code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/wasi/main.rs | ||
|
||
This example shows how to use the [`wasi-common`] crate to define WASI | ||
This example shows how to use the [`wasmtime-wasi`] crate to define WASI | ||
functions within a [`Linker`] which can then be used to instantiate a | ||
WebAssembly module. | ||
WebAssembly component. | ||
|
||
[`wasi-common`]: https://crates.io/crates/wasi-common | ||
[`wasmtime-wasi`]: https://crates.io/crates/wasmtime-wasi | ||
[`Linker`]: https://docs.rs/wasmtime/*/wasmtime/struct.Linker.html | ||
|
||
### WebAssembly module source code | ||
## WebAssembly Component Source Code | ||
|
||
For this WASI example, this Hello World program is compiled to a WebAssembly module using the WASI Preview 1 API. | ||
For this WASI example, this Hello World program is compiled to a WebAssembly component using the WASIp2 API. | ||
|
||
`wasi.rs` | ||
```rust | ||
{{#include ../examples/wasi/wasm/wasi.rs}} | ||
{{#include ../examples/wasm/wasi.rs}} | ||
``` | ||
|
||
Building this program generates `target/wasm32-wasip1/debug/wasi.wasm`, used below. | ||
> Building instructions: | ||
> 1. Have Rust installed | ||
> 2. Add WASIp2 target if you haven't already: `rustup target add wasm32-wasip2` | ||
> 3. `cargo build --target wasm32-wasip2` | ||
### Invoke the WASM module | ||
Building this program generates `target/wasm32-wasip2/debug/wasi.wasm`, used below. | ||
|
||
This example shows adding and configuring the WASI imports to invoke the above WASM module. | ||
### Invoke the WASM component | ||
|
||
This example shows adding and configuring the WASI imports to invoke the above WASM component. | ||
|
||
`main.rs` | ||
```rust,ignore | ||
{{#include ../examples/wasi/main.rs}} | ||
``` | ||
|
||
## WASI state with other custom host state | ||
|
||
The [`add_to_linker`] takes a second argument which is a closure to access `&mut | ||
WasiCtx` from within the `T` stored in the `Store<T>` itself. In the above | ||
example this is trivial because the `T` in `Store<T>` is `WasiCtx` itself, but | ||
you can also store other state in `Store` like so: | ||
|
||
[`add_to_linker`]: https://docs.rs/wasi-common/*/wasi_common/sync/fn.add_to_linker.html | ||
[`Store`]: https://docs.rs/wasmtime/*/wasmtime/struct.Store.html | ||
[`BorrowMut<WasiCtx>`]: https://doc.rust-lang.org/stable/std/borrow/trait.BorrowMut.html | ||
[`WasiCtx`]: https://docs.rs/wasi-common/*/wasi_common/struct.WasiCtx.html | ||
|
||
```rust | ||
# extern crate wasmtime; | ||
# extern crate wasi_common; | ||
# extern crate anyhow; | ||
use anyhow::Result; | ||
use std::borrow::{Borrow, BorrowMut}; | ||
use wasmtime::*; | ||
use wasi_common::{WasiCtx, sync::WasiCtxBuilder}; | ||
|
||
struct MyState { | ||
message: String, | ||
wasi: WasiCtx, | ||
} | ||
|
||
fn main() -> Result<()> { | ||
let engine = Engine::default(); | ||
let mut linker = Linker::new(&engine); | ||
wasi_common::sync::add_to_linker(&mut linker, |state: &mut MyState| &mut state.wasi)?; | ||
|
||
let wasi = WasiCtxBuilder::new() | ||
.inherit_stdio() | ||
.inherit_args()? | ||
.build(); | ||
let mut store = Store::new(&engine, MyState { | ||
message: format!("hello!"), | ||
wasi, | ||
}); | ||
|
||
// ... | ||
|
||
# let _linker: Linker<MyState> = linker; | ||
Ok(()) | ||
} | ||
``` | ||
|
||
## WASI Preview 2 | ||
|
||
An experimental implementation of the WASI Preview 2 API is also available, along with an adapter layer for WASI Preview 1 WebAssembly modules. In future this `preview2` API will become the default. There are some features which are currently only accessible through the `preview2` API such as async support and overriding the clock and random implementations. | ||
|
||
### Async example | ||
|
||
This [async example code][code2] shows how to use the [wasmtime-wasi::preview2][`preview2`] module to | ||
execute the same WASI Preview 1 WebAssembly module from the example above. This example requires the `wasmtime` crate `async` feature to be enabled. | ||
This [async example code][code2] shows how to use the [wasmtime-wasi][`wasmtime-wasi`] crate to | ||
execute the same WASIp2 component from the example above. This example requires the `wasmtime` crate `async` feature to be enabled. | ||
|
||
This does not require any change to the WebAssembly module, it's just the WASI API host functions which are implemented to be async. See [wasmtime async support](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support). | ||
This does not require any change to the WASIp2 component, it's just the WASIp2 API host functions which are implemented to be async. See [wasmtime async support](https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.async_support). | ||
|
||
[code2]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/wasi-async/main.rs | ||
[`preview2`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/preview2/index.html | ||
[`wasmtime-wasi`]: https://docs.rs/wasmtime-wasi/*/wasmtime_wasi/preview2/index.html | ||
|
||
```rust,ignore | ||
{{#include ../examples/wasi-async/main.rs}} | ||
``` | ||
|
||
You can also [browse this source code online][code2] and clone the wasmtime | ||
repository to run the example locally. | ||
|
||
## Beyond Basics | ||
|
||
Please see these references: | ||
* The [book](https://component-model.bytecodealliance.org) for understanding the component model of WASIp2. | ||
* [Bindgen Examples](https://docs.rs/wasmtime/latest/wasmtime/component/bindgen_examples/index.html) for implementing WASIp2 hosts and guests. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
//! Example of instantiating a wasm module which uses WASI preview1 imports | ||
//! implemented through the async preview2 WASI implementation. | ||
/* | ||
You can execute this example with: | ||
cmake examples/ | ||
cargo run --example wasip2-async | ||
*/ | ||
|
||
use wasmtime::component::{Component, Linker, ResourceTable}; | ||
use wasmtime::*; | ||
use wasmtime_wasi::bindings::Command; | ||
use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}; | ||
|
||
pub struct ComponentRunStates { | ||
// These two are required basically as a standard way to enable the impl of WasiView | ||
// impl of WasiView is required by [`wasmtime_wasi::add_to_linker_sync`] | ||
pub wasi_ctx: WasiCtx, | ||
pub resource_table: ResourceTable, | ||
// You can add other custom host states if needed | ||
} | ||
|
||
impl WasiView for ComponentRunStates { | ||
fn table(&mut self) -> &mut ResourceTable { | ||
&mut self.resource_table | ||
} | ||
fn ctx(&mut self) -> &mut WasiCtx { | ||
&mut self.wasi_ctx | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<()> { | ||
// Construct the wasm engine with async support enabled. | ||
let mut config = Config::new(); | ||
config.async_support(true); | ||
let engine = Engine::new(&config)?; | ||
let mut linker = Linker::new(&engine); | ||
wasmtime_wasi::add_to_linker_async(&mut linker)?; | ||
|
||
// Create a WASI context and put it in a Store; all instances in the store | ||
// share this context. `WasiCtxBuilder` provides a number of ways to | ||
// configure what the target program will have access to. | ||
let wasi = WasiCtxBuilder::new().inherit_stdio().inherit_args().build(); | ||
let state = ComponentRunStates { | ||
wasi_ctx: wasi, | ||
resource_table: ResourceTable::new(), | ||
}; | ||
let mut store = Store::new(&engine, state); | ||
|
||
// Instantiate our component with the imports we've created, and run it. | ||
let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/wasi.wasm")?; | ||
let command = Command::instantiate_async(&mut store, &component, &linker).await?; | ||
let program_result = command.wasi_cli_run().call_run(&mut store).await?; | ||
match program_result { | ||
Ok(()) => Ok(()), | ||
Err(()) => std::process::exit(1), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
//! Example of instantiating a wasm module which uses WASI imports. | ||
/* | ||
You can execute this example with: | ||
cmake examples/ | ||
cargo run --example wasip2 | ||
*/ | ||
|
||
use wasmtime::component::{Component, Linker, ResourceTable}; | ||
use wasmtime::*; | ||
use wasmtime_wasi::bindings::sync::Command; | ||
use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}; | ||
|
||
pub struct ComponentRunStates { | ||
// These two are required basically as a standard way to enable the impl of WasiView | ||
// impl of WasiView is required by [`wasmtime_wasi::add_to_linker_sync`] | ||
pub wasi_ctx: WasiCtx, | ||
pub resource_table: ResourceTable, | ||
// You can add other custom host states if needed | ||
} | ||
|
||
impl WasiView for ComponentRunStates { | ||
fn table(&mut self) -> &mut ResourceTable { | ||
&mut self.resource_table | ||
} | ||
fn ctx(&mut self) -> &mut WasiCtx { | ||
&mut self.wasi_ctx | ||
} | ||
} | ||
|
||
fn main() -> Result<()> { | ||
// Define the WASI functions globally on the `Config`. | ||
let engine = Engine::default(); | ||
let mut linker = Linker::new(&engine); | ||
wasmtime_wasi::add_to_linker_sync(&mut linker)?; | ||
|
||
// Create a WASI context and put it in a Store; all instances in the store | ||
// share this context. `WasiCtxBuilder` provides a number of ways to | ||
// configure what the target program will have access to. | ||
let wasi = WasiCtxBuilder::new().inherit_stdio().inherit_args().build(); | ||
let state = ComponentRunStates { | ||
wasi_ctx: wasi, | ||
resource_table: ResourceTable::new(), | ||
}; | ||
let mut store = Store::new(&engine, state); | ||
|
||
// Instantiate our component with the imports we've created, and run it. | ||
let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/wasi.wasm")?; | ||
let command = Command::instantiate(&mut store, &component, &linker)?; | ||
let program_result = command.wasi_cli_run().call_run(&mut store)?; | ||
if program_result.is_err() { | ||
std::process::exit(1) | ||
} | ||
|
||
// Alternatively, instead of using `Command`, just instantiate it as a normal component | ||
// New states | ||
let wasi = WasiCtxBuilder::new().inherit_stdio().inherit_args().build(); | ||
let state = ComponentRunStates { | ||
wasi_ctx: wasi, | ||
resource_table: ResourceTable::new(), | ||
}; | ||
let mut store = Store::new(&engine, state); | ||
// Instantiate it as a normal component | ||
let instance = linker.instantiate(&mut store, &component)?; | ||
// Get the index for the exported interface | ||
let interface_idx = instance | ||
.get_export(&mut store, None, "wasi:cli/[email protected]") | ||
.expect("Cannot get `wasi:cli/[email protected]` interface"); | ||
// Get the index for the exported function in the exported interface | ||
let parent_export_idx = Some(&interface_idx); | ||
let func_idx = instance | ||
.get_export(&mut store, parent_export_idx, "run") | ||
.expect("Cannot get `run` function in `wasi:cli/[email protected]` interface"); | ||
let func = instance | ||
.get_func(&mut store, func_idx) | ||
.expect("Unreachable since we've got func_idx"); | ||
// As the `run` function in `wasi:cli/[email protected]` takes no argument and return a WASI result that correspond to a `Result<(), ()>` | ||
// Reference: | ||
// * https://github.com/WebAssembly/wasi-cli/blob/main/wit/run.wit | ||
// * Documentation for [Func::typed](https://docs.rs/wasmtime/latest/wasmtime/component/struct.Func.html#method.typed) and [ComponentNamedList](https://docs.rs/wasmtime/latest/wasmtime/component/trait.ComponentNamedList.html) | ||
let typed = func.typed::<(), (Result<(), ()>,)>(&store)?; | ||
let (result,) = typed.call(&mut store, ())?; | ||
// Required, see documentation of TypedFunc::call | ||
typed.post_return(&mut store)?; | ||
result.map_err(|_| anyhow::anyhow!("error")) | ||
} |
File renamed without changes.
File renamed without changes.