TL;DR
- cargo-component を使うと、通常の Rust のプロジェクトと同じ感覚で、CLI として実行できる WebAssembly Component を作れる。
wasmtimeコマンドで実行できる Component はwasi:cli/commandWorld を実装しており、wasmtime-wasi を使えば Rust のプログラムからも実行できる。
内容
プロジェクトの作成とビルド
まず cargo-component をインストールする。
cargo install cargo-componentcargo component new でプロジェクトを作成する。ディレクトリ構成は通常のバイナリクレートと同じで、src/main.rs には Hello, world! を出力するコードが生成される。
cargo component new hello-wasi-cliビルドには cargo component build を使う。
$ cargo component build -r --target=wasm32-wasip1
...
Creating component target/wasm32-wasip1/release/hello-wasi-cli.wasm実行
実行するには WebAssembly のランタイムが必要である。ここでは Wasmtime を使う。
$ wasmtime target/wasm32-wasip1/release/hello-wasi-cli.wasm
Hello, world!ライブラリクレートの利用
通常のプロジェクトと同じく、WebAssembly Component のプロジェクトでもサードパーティ製のクレートを使える。
cargo add ferris-sayscargo component add はクレートではなく WebAssembly Component を依存関係に追加するためのコマンドなので注意する。
ferris_says::say を呼ぶように src/main.rs を書き換えてビルドし、wasmtime で実行すると、カニのアスキーアートが Hello, world! を話す。
wasi:cli/command World の実装
wasmtime コマンドで実行できる WebAssembly Component は、wasi:cli/command World を実装している。これは wasm-tools で確かめられる。
$ wasm-tools component wit target/wasm32-wasip1/release/hello-wasi-cli.wasm
package root:component;
world root {
import wasi:cli/[email protected];
import wasi:cli/[email protected];
...
export wasi:cli/[email protected];
}結果から、この Component は wasi:cli/run インタフェースを export していることが分かる。つまり、ライブラリクレートと同じように、Rust のプログラムから実行できる。
wasmtime-wasi を使うと、wasi:cli/run を実装した Component を Rust から簡単に呼び出せる。詳しくは第5章で扱う。新しいプロジェクトに clap、anyhow、wasmtime、wasmtime-wasi を追加し、src/main.rs を次のように書く。
async fn start(cli: Cli) -> anyhow::Result<()> {
let mut config = wasmtime::Config::new();
config.async_support(true);
let engine = Engine::new(&config)?;
let mut linker = Linker::new(&engine);
let mut store = Store::new(&engine, Ctx::new());
wasmtime_wasi::add_to_linker_async(&mut linker)?;
let component = Component::from_file(&engine, cli.wasm_file)?;
let command =
wasmtime_wasi::bindings::Command::instantiate_async(&mut store, &component, &linker)
.await?;
let guest = command.wasi_cli_run();
let _ = guest.call_run(&mut store).await?;
Ok(())
}Ctx は WasiCtx と ResourceTable を持つ構造体で、WasiView トレイトを実装している。引数に先ほど生成した Wasm ファイルのパスを渡して実行すると、wasmtime で実行したときと同じ出力が得られる。
cargo run -- ../hello-wasi-cli/target/wasm32-wasip1/release/hello-wasi-cli.wasm