社内でwasmを触っているプロジェクトの人と話して、羨ましかったので入門してみる。
wasmとは
ブラウザ上で高速に動作するバイナリファイルのことです。
WebAssemblyとは?できること・できないこと・使い方をわかりやすく解説 |パーソルクロステクノロジー
主にC++やRustで実装されています。
2023-08-07のwasmの対応状況はこんな感じです。
safariやモバイル版operaを除いて、ほぼほぼ対応していますね。
WebAssembly | Can I use... Support tables for HTML5, CSS3, etc
Cargoプロジェクトを作成する
とにかく実装したかったので、Rustで実装してみます。
公式にチュートリアルが用意してあったので、これをなぞる形でやっていこうと思います。
チュートリアル - Rust and WebAssembly
チュートリアルを実施するに際して、以下の3つのツールが必要なのでインストールしておきます。
私の場合はcargo-generateとwasm-packがなかったのでインストールしておきました。
- wasm-pack
- cargo-generate
- npm
cargo-generateを使用して、以下のgithubリポジトリをベースにパッケージを作成します。
cargo generate --git https://github.com/rustwasm/wasm-pack-template
名前は以下のようにしておきます。
wasm-game-of-life
これでwasmのチュートリアルパッケージが生成されました。
Cargo.tomlの中身は以下のようになっています。
[package]
name = "wasm-game-of-life"
version = "0.1.0"
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.84"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.7", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
見てみると、crate-type
というセクションがありますね。
一体なんなのかよくわからなかったので調べてみました。
- cdylib : 動的なシステムライブラリを生成します。他言語からロードするために使用されます。
- rlib : “Rust library"を生成します。静的なRustライブラリとも言え、中間生成物として使用されます。
要はFFI(foreign function interface)を使用して他の言語でRustを動かすための設定と言った感じでとりあえず理解しておけばよいでしょう。
ビルドは先程インストールしたwasm-pack CLIを使用して行います。
wasm-pack build
これを実行すると、以下のような処理を実行してくれます。
- Rust 1.30以上とwasm32-unknown-unknownターゲットをrustupを通してインストールしていることを確認します。
- cargoを使用してRustのソースをWebAssemblyの.wasmバイナリにコンパイルします。
- wasm-bindgenを使ってRustで生成したWebAssemblyを使用するためのJavaScript APIを生成します。
本来であれば、ビルドするためにwasm32-unknown-unknown
というターゲットが必要なので、rustupで追加して
rustup target add wasm32-unknown-unknown
以下のようにターゲット指定をしてビルドしなければいけないです。
cargo build --target=wasm32-unknown-unknown
他にもwasmファイル生成のためにコマンドを実行する必要がありますが、それらをすべて包括的に行ってくれるコマンドがwasm-pack build
ということですね。便利。
ビルドを行うとpkg
というディレクトリが出来上がります。
pkg
├── README.md # パッケージと全く同じREADME.md
├── package.json # 動かすのに必要な依存関係や設定が書かれた状態になっている
├── wasm_game_of_life.d.ts # wasm_game_of_life.jsに対するTypeScriptの型チェックに使用する
├── wasm_game_of_life.js # Rustとの間で値のやり取りを行う際に利用する
├── wasm_game_of_life_bg.js # wasm_game_of_life.jsで参照されるファイル
├── wasm_game_of_life_bg.wasm # wasmの本体
└── wasm_game_of_life_bg.wasm.d.ts # wasmのTypeScript型チェックに使用する
ちなみに、これらのファイルの_bg
はbindgen
の略称で、wasm-bindgen
を使用して作成されたファイルであることを表しているそうです。
“bg” stands for “bindgen” here, and they’re intended to be internal files generated by wasm-bindgen for your project.
和訳:ここでの「bg」は「bindgen」を表しており、プロジェクト用に wasm-bindgen によって生成される内部ファイルであることを目的としています。
npmパッケージの作成
つづけて、このwasmを静的サイトで動かすためのnpmパッケージを作成します。
npm init wasm-app www
実行すると、www
というディレクトリが作成されます。
ちなみに、wasm-app
コマンドのベースはこの実装↓
GitHub - rustwasm/create-wasm-app: npm init template for consuming rustwasm pkgs
www
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── bootstrap.js # webpackでエントリーモジュールとして使用するjs
├── index.html # メインHTML
├── index.js # メインモジュール。hello-wasm-packから関数を実行
├── package-lock.json
├── package.json # hello-wasm-pack(wasmのhello worldプロジェクト)やwebpackへの依存を持つ
└── webpack.config.js
早速このディレクトリに移動して依存関係のインストールを行いましょう。
% npm install
npm WARN old lockfile
npm WARN old lockfile The package-lock.json file was created with an old version of npm,
npm WARN old lockfile so supplemental metadata must be fetched from the registry.
npm WARN old lockfile
npm WARN old lockfile This is a one-time fix-up, please be patient...
npm WARN old lockfile
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated uuid@3.3.2: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
npm WARN deprecated source-map-resolve@0.5.2: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated ini@1.3.5: Please update to ini >=1.3.6 to avoid a prototype pollution issue
npm WARN deprecated source-map-url@0.4.0: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
npm WARN deprecated chokidar@2.1.8: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
npm WARN deprecated debug@4.1.1: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
npm WARN deprecated uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
npm WARN deprecated debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
npm WARN deprecated debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
added 587 packages, and audited 588 packages in 32s
18 packages are looking for funding
run `npm fund` for details
38 vulnerabilities (1 low, 4 moderate, 27 high, 6 critical)
To address all issues, run:
npm audit fix
Run `npm audit` for details.
なんかいっぱいWARNが出ていますが、とりあえずインストールはできたようです。
とりあえず起動してみます。
% npm start
> create-wasm-app@0.1.0 start
> webpack-dev-server
(node:192465) [DEP0111] DeprecationWarning: Access to process.binding('http_parser') is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /home/username/Project/personal/wasm-game-of-life/www
node:internal/crypto/hash:67
this[kHandle] = new _Hash(algorithm, xofLen);
^
Error: error:0308010C:digital envelope routines::unsupported
at new Hash (node:internal/crypto/hash:67:19)
at Object.createHash (node:crypto:135:10)
at module.exports (/home/username/Project/personal/wasm-game-of-life/www/node_modules/webpack/lib/util/createHash.js:135:53)
at NormalModule._initBuildHash (/home/username/Project/personal/wasm-game-of-life/www/node_modules/webpack/lib/NormalModule.js:417:16)
at handleParseError (/home/username/Project/personal/wasm-game-of-life/www/node_modules/webpack/lib/NormalModule.js:471:10)
at /home/username/Project/personal/wasm-game-of-life/www/node_modules/webpack/lib/NormalModule.js:503:5
at /home/username/Project/personal/wasm-game-of-life/www/node_modules/webpack/lib/NormalModule.js:358:12
at /home/username/Project/personal/wasm-game-of-life/www/node_modules/loader-runner/lib/LoaderRunner.js:373:3
at iterateNormalLoaders (/home/username/Project/personal/wasm-game-of-life/www/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
at Array.<anonymous> (/home/username/Project/personal/wasm-game-of-life/www/node_modules/loader-runner/lib/LoaderRunner.js:205:4)
at Storage.finished (/home/username/Project/personal/wasm-game-of-life/www/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:43:16)
at /home/username/Project/personal/wasm-game-of-life/www/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:79:9
at /home/username/Project/personal/wasm-game-of-life/www/node_modules/graceful-fs/graceful-fs.js:78:16
at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read_file_context:68:3) {
opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'
}
Node.js v17.9.0
なんかエラーがいっぱい出ちゃいました。
調べてみたら、バージョンが原因っぽい?です。
Nodejsのバージョンを上げたら`error:0308010C:digital envelope routines::unsupported`が出てしまう - Qiita
nodenv使っていたので、これで一つメジャーバージョンを下げます。
nodenv install 16.20.1
nodenv local 16.20.1
再度起動してみます。
% npm start
> create-wasm-app@0.1.0 start
> webpack-dev-server
(node:193763) [DEP0111] DeprecationWarning: Access to process.binding('http_parser') is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /home/username/Project/personal/wasm-game-of-life/www
ℹ 「wdm」: Hash: c2abf06051812e05e0b9
Version: webpack 4.43.0
Time: 666ms
Built at: 2023/08/08 8:59:44
Asset Size Chunks Chunk Names
0.bootstrap.js 3.4 KiB 0 [emitted]
8e8fa9289c240ac706a1.module.wasm 872 bytes 0 [emitted] [immutable]
bootstrap.js 369 KiB main [emitted] main
index.html 297 bytes [emitted]
Entrypoint main = bootstrap.js
[0] multi (webpack)-dev-server/client?http://localhost:8080 ./bootstrap.js 40 bytes {main} [built]
[./bootstrap.js] 279 bytes {main} [built]
[./index.js] 56 bytes {0} [built]
[./node_modules/ansi-html/index.js] 4.16 KiB {main} [built]
[./node_modules/ansi-regex/index.js] 135 bytes {main} [built]
[./node_modules/hello-wasm-pack/hello_wasm_pack.js] 698 bytes {0} [built]
[./node_modules/strip-ansi/index.js] 161 bytes {main} [built]
[./node_modules/webpack-dev-server/client/index.js?http://localhost:8080] (webpack)-dev-server/client?http://localhost:8080 4.29 KiB {main} [built]
[./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.51 KiB {main} [built]
[./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.53 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/createSocketUrl.js] (webpack)-dev-server/client/utils/createSocketUrl.js 2.91 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/log.js] (webpack)-dev-server/client/utils/log.js 964 bytes {main} [built]
[./node_modules/webpack-dev-server/client/utils/reloadApp.js] (webpack)-dev-server/client/utils/reloadApp.js 1.59 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/sendMessage.js] (webpack)-dev-server/client/utils/sendMessage.js 402 bytes {main} [built]
[./node_modules/webpack/hot sync ^\.\/log$] (webpack)/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
+ 21 hidden modules
ℹ 「wdm」: Compiled successfully.
うまくいきました。やったね!
ブラウザ側でも表示してみます。
ちゃんと動いていますね。
OpenSSLの問題らしいので、package.jsonのscripts
を以下のように変えてあげても動きます。
{
"scripts": {
"build": "NODE_OPTIONS=--openssl-legacy-provider webpack --config webpack.config.js",
"start": "NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server"
},
}
はやく対応してほしい…
まとめ
長くなってきたので一旦終わります。
続きはまたすぐ更新すると思うので、乞うご期待!