Hello, world! with Wasm Component
TL;DR
- cargo-componentを使うと、Wasmコンポーネントを作成するためのRustプロジェクトを作成できます
- 作成したWasmコンポーネントはWasmtimeを使って実行できます
- 作成したWasmコンポーネントは
wasi:cli/run
インターフェースを実装しています
必要なツール
必要なツールは次の4つです。
- Rustのツールチェーン
- cargo-component
- Wasmtime
- wasm-tools
Rustのツールチェーンはこちらに説明があります。
cargo-component
cargo-componentは、Wasmコンポーネントの管理を行うためのRustのパッケージ管理ツールであるCargoのサブコマンドです。今回はプロジェクトの作成とビルドに利用します。
cargo-componentはCargoを使ってインストールできます:
% cargo install cargo-component
cargo-componentではcargoコマンドと同様のサブコマンドが利用できます:
Usage: cargo component <COMMAND>
Commands:
add Add a dependency for a WebAssembly component
key Manage signing keys for publishing components to a registry
new Create a new WebAssembly component package at <path>
update Update dependencies as recorded in the component lock file
publish Publish a package to a registry
help Print this message or the help of the given subcommand(s)
Wasmtime
WasimtimeはWasmの処理系の1つです。ビルドしたWasmコンポーネントの実行に利用します。
次のようにインストーラーを利用してインストールします:
curl https://wasmtime.dev/install.sh -sSf | bash
wasmtimeは~/.wasmtime/bin/wasmtime
にインストールされます。またインストーラーは~/.wasmtime/bin
をコマンドサーチパスに追加するため、シェルの設定を変更する必要は特にありません。
次のようにwasmtime
コマンドを実行して、インストールできたことを確認します:
% wasmtime --version
wasmtime-cli 19.0.1 (26104f0c7 2024-04-02)
wasm-tools
wasm-toolsはWasmファイルを操作するツール群です。例えばWasmファイルから、そのテキスト表現であるWATファイルを生成できます。
今回の内容にwasm-toolsは必須ではありませんが、ビルドされたWasmコンポーネントが依存するシステムインターフェース群の確認に利用します。
wasm-toolsもCargoを使ってインストールできます:
% cargo install wasm-tools
Hello, world!
まずRustプロジェクトを作成します。次の例ではcargo-componentのサブコマンドnew
を使ってhello-wasi-cli
プロジェクトを作成しています:
% cargo component new hello-wasi-cli
Created binary (application) `hello-wasi-cli` package
作成したプロジェクトは次のような構造をしています。Rustのバイナリークレートと同じフォルダー構成となっています:
.
├── Cargo.toml
└── src
└── main.rs
main.rs
は次のようになっています。これもcargo new
で作成したバイナリークレートの初期状態と同じ内容になっています:
fn main() {
println!("Hello, world!");
}
ビルド
cargo component build
コマンドを実行し、ビルドします:
% cargo component build
Compiling hello-wasi-cli v0.1.0 (/somewhere/hello-wasi-cli)
Finished dev [unoptimized + debuginfo] target(s) in 0.68s
ビルドされたWasmファイルはtarget/wasm32-wasi/debug/hello-wasi-cli.wasm
に出力されます。devloperプロファイルでビルドを行っていること、またwasm32-wasi
をターゲットにビルドを行っていることが理由です。
% ls target/wasm32-wasi/debug/hello-wasi-cli.wasm
target/wasm32-wasi/debug/hello-wasi-cli.wasm
実行
Wasmtimeを使ってビルドしたWasmファイルを実行します:
% wasmtime target/wasm32-wasi/debug/hello-wasi-cli.wasm
Hello, world!
ビルドされたのはWasmモジュール
これまでの手順でビルドされたのはWasmモジュールです。次のようにwasm-toolsコマンドを実行して、出力されたWAT形式のファイルを眺めると、先頭にmodule
の文字が見えると思います。
% wasm-tools print target/wasm32-wasi/debug/hello-wasi-cli.wasm | head
(module
(type (;0;) (func))
(type (;1;) (func (param i32)))
(type (;2;) (func (param i32 i32)))
(type (;3;) (func (param i32) (result i32)))
(type (;4;) (func (param i32 i32) (result i32)))
(type (;5;) (func (param i32 i32 i32)))
(type (;6;) (func (param i32 i32 i32) (result i32)))
(type (;7;) (func (param i32 i32 i32 i32) (result i32)))
(type (;8;) (func (result i32)))
Hello,World!(コンポーネント編)
前節で作成したhello-wasm-cli
プロジェクトを変更し、Wasmコンポーネントとしてビルドします。
プロジェクトは次のようなフォルダー構成をしています:
.
├── Cargo.toml
└── src
└── main.rs
マニフェストへの追記
プロジェクトのルートフォルダにあるCargo.toml
にはhello-wasm-cli
クレートに関するメタデータが記載されています。これを次のように変更します。
[package]
name = "hello-wasi-cli"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[package.metadata.component]
package = "example:hello-world"
次の2行を追加した点が差分になります:
[package.metadata.component]
package = "example:hello-wasi-cli"
全てのWasmコンポーネントにはパッケージIDが付いています。上記の変更は、ビルドされるWasmコンポーネントのパッケージを指定しています。
このパッケージIDは、コンポーネントをコンポーネントレジストリーで公開する際に利用されます。
ビルド
前節と同様にcargo component build
コマンドでビルドします。ビルドした結果、target/wasm32-wasi/debug/hello-wasi-cli.wasm
が出力されます。
% cargo component build
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
実行
Wasmtimeを利用して、実行します。前節と同様の結果が得られます:
% wasmtime target/wasm32-wasi/debug/hello-wasi-cli.wasm
Hello, world!
コンポーネントがビルドされている
次のようにwasm-toolsを使ってビルドされたWasmファイルを確認します。前節と異なり、1行目にcomponent
とあるのがわかると思います。これはビルドされたファイルはWasmコンポーネントであることを示してます:
% wasm-tools print target/wasm32-wasi/debug/hello-wasi-cli.wasm | head
(component
(type (;0;)
(instance
(type (;0;) (tuple string string))
(type (;1;) (list 0))
(type (;2;) (func (result 1)))
(export (;0;) "get-environment" (func (type 2)))
)
)
(import "wasi:cli/environment@0.2.0" (instance (;0;) (type 0)))
ワールドの出力
Wasmコンポーネントはwasm-toolsで処理することで、次の情報をWebAssembly Interface Type(WIT)形式で出力できます:
- コンポーネントが依存するインターフェース
- コンポーネントが実装するインターフェース
なおこの2つを取りまとめた記述のことをWITでは「ワールド(world)」と呼びます。
ワールドの出力は次のように行います:
% wasm-tools component wit target/wasm32-wasi/debug/hello-wasi-cli.wasm
package root:component;
world root {
import wasi:cli/environment@0.2.0;
import wasi:cli/exit@0.2.0;
import wasi:io/error@0.2.0;
import wasi:io/streams@0.2.0;
import wasi:cli/stdin@0.2.0;
import wasi:cli/stdout@0.2.0;
import wasi:cli/stderr@0.2.0;
import wasi:clocks/wall-clock@0.2.0;
import wasi:filesystem/types@0.2.0;
import wasi:filesystem/preopens@0.2.0;
export wasi:cli/run@0.2.0;
}
import
は依存するインターフェース、export
は実装されているインターフェースを表します。ビルドしたWasmコンポーネントは、WASI 0.2で定義されているwasi:cli/run
インターフェースを実装していることがわかります。
まとめ
- cargo-componentを使うことで、通常のRustプロジェクト開発と同様にWasmコンポーネントを開発できます
- コンポーネントをビルドするには、Cargo.tomlでWasmコンポーネントのパッケージIDを設定する必要があります
- wasm-toolsを使えば、Wasmコンポーネントのワールドを知ることができます
次回
次回はライブラリークレートからWasmコンポーネントを作成し、コンポーネントレジストリーへ登録します:Hello, world! with Wasm Component(レイブラリー編)
Discussion
( 本筋と関係ないですが )
typo
Hello, world! > ビルド
Hello,World!(コンポーネント編) > コンポーネントがビルドされている
ありがとうございます。修正しました。