Hello, world! with Wasm Component(WITパッケージ編)
これまでのあらすじ:
-
初回
- cargo-componentを使ってWebAssemblyコンポーネント(Wasmコンポーネント)を作りました
- Wasmtimeで実行しました
- wasm-toolsを使って作成したコンポーネントのワールドを出力しました
-
前回
- cargo-componentを使ってライブラリーとして働くWasmコンポーネントを作成しました
- WebAssembly Interface Typeでライブラリーのインターフェースを定義し、Rustでインターフェースを実装しました
- Wasmのパッケージレジストリーであるwa.devに、作成したコンポーネントを登録しました
前回コンポーネントパッケージをレジストリーに登録しました。これを含めて3種類のパッケージをレジストリーに登録できます:
- コンポーネントパッケージ
- モジュールパッケージ
- WITパッケージ
今回はWITパッケージを作成し、wa.devに登録します。そして登録したWITパッケージを実装するコンポーネントを実装し、レジストリーに登録します。
前回までのプロジェクトフォルダー
前回までの内容で、プロジェクトフォルダーは次のようになっています。
.
├── Cargo.lock
├── Cargo.toml
├── crates
│ └── greet
│ ├── Cargo.toml
│ ├── src
│ │ └── lib.rs
│ └── wit
│ └── world.wit
├── src
│ ├── bindings.rs
│ └── main.rs
└── target
前回までにインストールしたツール
前回までに次のツールがインストールされています:
- Rustの開発環境
- cargo-component
- wasm-tools
- wasmtime
- warg-cli
準備
今回の内容のために、以下の準備を行います:
- wit toolのインストール
- プロジェクトにWITパッケージ用のフォルダーを追加
wit toolのインストール
前回はwarg-cliを利用してレジストリーにコンポーネントを登録しました。これは上記の3パッケージを管理するツールでした。今回はWITパッケージの管理に特化したツールwit toolを利用します。
wit toolはcargo install
コマンドでインストールします:
% cargo install wit
ヘルプを表示して、インストールできたことを確認します:
% wit --help
WIT package tool
Usage: wit <COMMAND>
Commands:
init Initialize a new WIT package
add Adds a reference to a WIT package from a registry
build Build a binary WIT package
publish Publish a WIT package to a registry
key Manage signing keys for publishing packages to a registry
update Update dependencies as recorded in the lock file
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
WITパッケージ用のフォルダーの作成
プロジェクトにWITパッケージ用のフォルダーを追加します。フォルダー名をwit
としたいところですが、以下の理由からinterfaces
というフォルダー名にします:
-
wit
フォルダーに複数のパッケージに関するWITファイルを保持できないため - マニフェストに設定がない場合、cargo-componentは
wit
フォルダー内で定義されているワールドをターゲットとしてコンポーネントを定義しようとするため
前者に関してはWITの仕様に次のような記述があります:
The current thinking for a convention is that projects will have a wit folder where all
wit/*.wit
files within describe a single package.
またwit
フォルダー内に、複数のパッケージに関するWITファイルがある状態でcargo-componentを実行するとエラーになります。下記の例では、先に発見したcomponent:name2
というパッケージとは異なるパッケージcomponent:name
に関する記述を発見したというエラーが表示されています:
% cargo component build
error: failed to create a target world for package `name` (/somewhere/name/Cargo.toml)
Caused by:
0: failed to parse local target from directory `/Users/chikoski/bbb/name/wit`
1: package identifier `component:name` does not match previous package name of `component:name2`
--> /somewhere/name/wit/world.wit:1:9
|
1 | package component:name;
| ^-------------
username:name
パッケージの定義
新しくusername:name
パッケージを定義します。
interfaces/name
フォルダの作成
interfaces/name
という名前でプロジェクトフォルダに、インターフェースを定義するためのフォルダを作成します。
% pwd
/somewhere/hello-wasi-cli/
% mkdir -p interfaces/name
作成したフォルダーで、wit init
コマンドを実行します。実行すると、WITパッケージのマニフェストが作成されます:
% cd interfaces/name
% wit init
Created configuration file `./wit.toml`
作成されたマニフェスト
作成直後は、パッケージのバージョン番号だけが記述されています。バージョン番号以外には、インターフェース定義で参照する他のパッケージが記述されます。
version = "0.1.0"
WITファイルの作成
次のようなWITファイルを作成します:
- パッケージ名は
username:name
とします -
name-provider
インターフェースを定義します。このインターフェースはパラメーターがなく、文字列を返す関数name
を持っています。 -
name-provider
をエキスポートするワールドを定義します
上記の内容を満たすWITファイルは次のようになります:
package username:name;
interface name-provider {
name: func() -> string;
}
world default{
export name-provider;
}
wa.devでのパッケージ作成
WITパッケージを公開するために、まずwa.devでパッケージを初期化します。初期化はWebインターフェースから行うとわかりやすくて良いと思います。
パッケージの公開
WITファイルが存在するフォルダーで、wit pubish
コマンドを実行します。
% pwd
/somewhere/hello-wasi-cli/interfaces/name
% wit publish
公開されたパッケージの例はこちらです。
公開されたWITパッケージの実装
公開されたWITパッケージをRustで実装します。以下の作業は、プロジェクトフォルダー内のcrates
フォルダーで行います。
--target
オプションに実装するWITパッケージを指定してcargo component new
コマンドを実行します。作成するライブラリークレートの名前はname-impl
とします。
% pwd
/someshere/hello-wasi-cli/crates
% cargo component new --lib --target username:name name-impl
Updating component registry package logs
Created library `name-impl` package
Updated manifest of package `name-impl`
Generated source file `src/lib.rs` for target `username:name` v0.1.0
作成されたライブラリークレートにはwit
フォルダーがありません。オプションに指定したWITパッケージからbindings.rs
が生成されるためです:
% ls -T name-impl
name-impl
├── Cargo.lock
├── Cargo.toml
└── src
└── lib.rs
パッケージの実装
lib.rs
の初期状態は次のようになっています。Guest
トレイトは先ほど定義したusername:name
パッケージに基づいて定義されています。
#[allow(warnings)]
mod bindings;
use bindings::exports::username::name::name_provider::Guest;
struct Component;
impl Guest for Component {
fn name() -> String {
unimplemented!()
}
}
bindings::export!(Component with_types_in bindings);
あとはGuest
トレイトを実装します:
#[allow(warnings)]
mod bindings;
use bindings::exports::username::name::name_provider::Guest;
struct Component;
impl Guest for Component {
fn name() -> String {
"Wasm".to_string()
}
}
bindings::export!(Component with_types_in bindings);
ビルド
cargo component build
コマンドでビルドします。
% pwd
/someshare/hello-wasi-cli/crates/name-impl
% cargo component build -r --target wasm32-unknown-unknown
作成されたWasmファイルがname/name-provider
をエキスポートしていることが確認できます:
% cd ../..
% pwd
/somewhere/hello-wasi-cli
% wasm-tools component wit target/wasm32-unnown-unknown/release/name_ipml.wasm
package root:component;
world root {
export username:name/name-provider;
}
まとめ
- wit toolを使って、WITパッケージをwa.devに公開しました
- wa.devに公開されているWITパッケージをRustクレートとして使って実装しました
- Rustクレートをビルドすることで、WITパッケージを実装したWasmコンポーネントを取得しました
Discussion