[20190201_RustでWebAssembly入門のためのまとめ
Rust で WebAssembly に入門する
Introduction – Rust and WebAssembly
上記ページが Rust + WebAssembly のためのチュートリアル & リファレンスのページになります。このページを参考に、Rust のコードから WebAssembly を生成するための環境構築~サンプルコードの実装までを試します。
WebAssembly とは
WebAssembly とは、ブラウザ上で動作するプログラミング言語の1種です。Javascriptと違いバイナリ形式での実行が可能であるため、実行速度がJavascriptに比べて向上します。
主要ブラウザ (Firefox, Chromium, Google Chrome, Microsoft Edge) 上で動作がサポートされています。IEは未対応です。
WebAssembly のコード(wasm) は、C, C++ 等のプログラミング言語からコンパイルして生成することになります。ほかにも Rust, Go などの言語でも開発できます。
Rust + WebAssembly セットアップ
今回は Rust をコンパイルして WebAssembly を生成します。Windows環境での動作を想定しています。Rust で WebAssembly を開発するためには以下のツールが必要です。
Rust Toolchain
Install – Rust programming language
rustup
, rustc
, cargo
のことです。上記ページから環境に合わせてインストールしてください。それぞれのコマンドが使えるようになればOKです。
後述の wasm-pack
を使うために Rust 1.30.0以降が必要 になります。
上記ページで一通りインストールからセットアップを行っていますので参考にしてください。
wasm-pack
Rust で WebAssembly を構築したり、テストや公開するためのコマンドが諸々入ったツールです。上記ページから環境に合わせてインストールしてください。
$ cargo install wasm-pack
インストールはすぐに完了します。設定等もありません。
cargo-generate
GitHub – ashleygwilliams/cargo-generate: cargo, make me a project cargo-generate
は、指定されたレポジトリをもとに、テンプレートプロジェクトを生成してくれる便利ツールです。cargo でインストールして使えるようにしておきます。
$cargo install cargo-generate
これを使って環境を作ります。
npm
最後に npm
を使えるようにしてください。Node.js をインストールすると使えるようになります。あたらしくNode.js をインストールする場合は最新の安定版を入れて、最新のnpmが使えるようにします。
すでにnpmが使える場合は最新版にしておきます。npm
$ npm install npm@latest -g
Hello World サンプル
セットアップが完了したら、サンプルプログラムを作って動かしてみます。”Hello World” をアラートするだけの簡単なものです。
Rustプロジェクトテンプレートを複製する
このプロジェクトは、Rustで作られたライブラリコードを WebAssembly にコンパイルし、npm に公開するためのものです。
以下のコマンドでテンプレートを複製し、プロジェクトを作成します。パラメータ(--name my-project
)でプロジェクト名を指定しています。指定したプロジェクト名のフォルダ以下にテンプレートが生成されます。
$ cargo generate --git https://github.com/rustwasm/wasm-pack-template --name my-project
$ cd my-project
中身は以下のような構成になっています。
my-project/
├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
└── src
├── lib.rs
└── utils.rs
src/lib.rs がプログラムのルートです。以下のような内容になっています。
extern crate cfg_if;
extern crate wasm_bindgen;
mod utils;
use cfg_if::cfg_if;
use wasm_bindgen::prelude::*;
cfg_if! {
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
if #[cfg(feature = "wee_alloc")] {
extern crate wee_alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
}
}
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet() {
alert("Hello, my-project!");
}
Javascriptの alert()
をインポートしてRustから呼び出せるようにしています。また、Rustで定義した greet()
をJavascript側から利用できるようにしています。
src/utils.rs は、デバッグ用の何からしいです。
コンパイルしてwasmファイルを生成する
$ wasm-pack build
wasm-pack
コマンドでビルドすると pkg
ディレクトリが生成され、その中に次のようなファイルが生成されます。
pkg/
├── package.json
├── README.md
├── my_project_bg.wasm
├── my_project.d.ts
└── my_project.js
my_project_bg.wasm
ファイルが、Rustコードをコンパイルすることによって得られたWebAssemblyバイナリです。この中にある処理を呼び出すためのラッパーが、ts, jsのファイルです。Typescriptの環境下で使用するには ts ファイルを使えばいいということでしょう。
/* tslint:disable */
import * as wasm from './my_project_bg';
let cachedTextDecoder = new TextDecoder('utf-8');
let cachegetUint8Memory = null;
function getUint8Memory() {
if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) {
cachegetUint8Memory = new Uint8Array(wasm.memory.buffer);
}
return cachegetUint8Memory;
}
function getStringFromWasm(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len));
}
export function __wbg_alert_6c792a5e55b14999(arg0, arg1) {
let varg0 = getStringFromWasm(arg0, arg1);
alert(varg0);
}
/**
* @returns {void}
*/
export function greet() {
return wasm.greet();
}
wasmファイルをインポートして、そこで定義されている greet()
を呼び出しています。これは Rust で定義した処理です。
package.jsonファイルには、生成されたコードのメタ情報が記述されており、このパッケージをNPMで公開するときに役立ちます。
Webプロジェクトを生成する
cargo で生成したプロジェクト内に、Webページ用のフォルダを生成し、wasmを実行できるようなプロジェクトを作ります。上で生成したjsファイルを使って手動で構築もできそうですが、wasm用のプロジェクトを生成するための便利ツールがあるので、これを利用します。
$ npm init wasm-app www
このコマンドを実行するためには新しめの npm が必要になるようです。
実行すると、プロジェクトディレクトリ内に /www
ディレクトリが生成され、そこに必要なファイル等が自動で生成されます。
my-project/www/
├── bootstrap.js
├── index.html
├── index.js
├── LICENSE-APACHE
├── LICENSE-MIT
├── package.json
├── README.md
└── webpack.config.js
webpackを使っていますが、必要な設定は完了しているので特にファイルを触る必要はありません。
必要な依存パッケージをインストールするために www で install コマンドを実行します。
$ cd www
$ npm install
ローカルのパッケージリンクする
先ほど wasm-pack
コマンドで生成した wasm ファイルをローカルパッケージとしてリンクします。npm link
コマンドは npm にパッケージを公開していないローカルパッケージを、npm プロジェクトから利用できるようにするものです。
まずは、/pkg 内でリンクコマンドを実行します。
$ npm link
次に、/www で、リンクしたパッケージを利用できるようにします。
$ npm link my-project
パッケージ名は /www/package.json の name に書かれている値です。
これで利用する準備は完了です。
ローカルで動かしてみる
wwwディレクトリの中身ですが、index.html が bootstrap.js を経由して index.js を読み込んでいます。index.js の内容を先ほどリンクした内容に書き換えます。
import * as wasm from "hello-wasm-pack";
wasm.greet();
では実行します。
ローカルに開発用サーバーを立てて動かすコマンドが用意されているので利用します。
$ npm run start
これでサーバーの起動完了です。localhost:8080 にアクセスすると、index.html の内容が表示され、alert が呼ばれます。
wasm-pack build
で Rust を再コンパイルすると、wasm ファイルが更新され、ブラウザがリロードされます。
あとは、開発するだけです。
以上。
コメントを書く