近年流行っていると噂のWebAssemblyの環境構築を試したので、忘備録として書いておきます。
フォルダ構成は以下のようになります。
.
├── README.md
├── wasm
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── wasm.md
│ ├── pkg
│ ├── src
│ └── target
└── web
├── README.md
├── bootstrap.js
├── index.html
├── index.ts
├── package-lock.json
├── package.json
├── tsconfig.json
├── web.md
└── webpack.config.js
コードはgithubに置いています。
Rustの環境構築
使用したRustのバージョンは以下。
$ cargo --version
cargo 1.74.0-nightly (96fe1c9e1 2023-08-29)
$ rustc -V
rustc 1.74.0-nightly (35e416303 2023-09-01)
$ rustup -V
rustup 1.26.0 (5af9b9484 2023-04-05)
Rustのlibraryの作成
mkdir wasm
cd wasm
cargo init --lib
これでライブラリ用のパッケージ環境が作成されます。
まずはwasm-packをinstallします。
cargo install wasm-pack
次にWasmのためのライブラリwasm-bindgenと軽量なメモリアロケータwee_allocをinstallします。
今回用いるCargo.toml
は以下のようになります。
[package]
name = "wasm-sample"
version = "0.1.0"
edition = "2021"
[dependencies]
wasm-bindgen = "0.2.63"
wee_alloc = "0.4.5"
[lib]
crate-type = ["cdylib"]
Hello, worldの実装
今回は画面にalertを出すrustのコードを書いてみます。lib.rs
を以下のように書き換えます。
use wasm_bindgen::prelude::*;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
pub fn hello() {
alert("hello, world!");
}
#[wasm_bindgen]
extern "C" {
pub fn alert(name: &str);
}
ここで、 pub fn alert
はJavaScriptのalertを呼び出す関数です。詳しい仕組みは rust-wasmを参照してください。
以下のコマンドでwasmのファイルをビルドします。
wasm-pack build --target web
これでpkg
フォルダが作成され、以下のようにwasmのためのコードが生成されたと思います。
├── pkg
│ ├── README.md
│ ├── package.json
│ ├── wasm_sample.d.ts
│ ├── wasm_sample.js
│ ├── wasm_sample_bg.wasm
│ └── wasm_sample_bg.wasm.d.ts
これを適切にフロントエンドから呼び出せばHello, world成功です。これで一旦wasm側は終了となります。
フロントエンドの環境構築
今回は素朴にwebpack+typescriptを用いたフロントエンドの環境を構築します。使用したNode.jsのバージョンは以下。
$ node --version
v16.16.0
$ npm --version
8.11.0
NodeModules, wasmのinstall
まずpackage.json
を以下のように作成します。
{
"name": "wasm-game",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack build"
},
"keywords": [],
"author": "ganemaruko",
"dependencies": {
"copy-webpack-plugin": "^10.0.0",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1"
},
"devDependencies": {
"ts-loader": "^9.4.4",
"typescript": "^5.2.2",
"webpack-dev-server": "^4.6.0"
}
}
そして、npm installを実行
npm install
ここで、先ほどの手順でビルドしておいたwasmをnode_modulesに加えます。package.jsonを以下のように変更します。
{
"name": "wasm-game",
...略
"dependencies": {
"copy-webpack-plugin": "^10.0.0",
"webpack": "^5.65.0",
!この一行を追加!
"wasm-sample": "file:../wasm/pkg",
"webpack-cli": "^4.9.1"
},
"devDependencies": {
"ts-loader": "^9.4.4",
"typescript": "^5.2.2",
"webpack-dev-server": "^4.6.0"
}
}
wasm-sampleのバージョン部分に相対パスを入れることで、npm install実行時に該当箇所からモジュールをinstallしてくれます。
この状態で以下を実行してください。
npm install wasm-sample
これでwasmがtypescriptから呼び出せるようになったはずです
また、webpackとtypescriptを用いるので、webpack.config.js
とtsconfig.json
も作成しておきます。詳細は割愛しますが、以下のようになります。
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
entry: "./index.ts",
output: {
path: path.resolve(__dirname, "public"),
filename: "index.js",
},
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
mode: "development",
plugins: [
new CopyWebpackPlugin({
patterns: [{ from: "./index.html", to: "./" }],
}),
],
};
index.tsをコンパイルしてpublic/index.js
に出力する、というところだけ押さえていただければ大丈夫です。
合わせてtsconfig.jsonは以下になります。
{
"compilerOptions": {
"outDir": "./public/",
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"allowJs": true,
"moduleResolution": "node"
}
}
これてtypescriptからwasmを呼ぶ出す準備が整いました。
index.tsの作成
wasmファイルを呼び出すindex.ts
を作成します。以下のようなコードになります。
import init, { hello } from "wasm-sample";
init().then((_) => {
hello();
console.log("OK!");
});
wasm-sampleが正しくinstallできていればhelloにきちんと型が当たっているはずです。
先ほどのwebpack.config.jsでこのindex.ts
がpublic/index.js
にコンパイルされるように設定されているので、index.htmlを以下のように作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Wasm Sample</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
これで準備OKです。webpack-dev-serverを起動します。
npm run dev
これでlocalhost:8080を確認すると、以下のようにalertが上がっているのが確認できると思います。
これでrustをコンパイルしたwasmをtypescriptから呼びだせました。
コメント