Mac
Rust
asm.js
WebAssembly

RustをWASMに変換してブラウザ上で動かす

これは WebAssembly Advent Calendar 2017 19日目の記事です。

はじめに

この記事ではRustのプログラムをWebブラウザで動かす方法を2つご紹介します。

  1. asm.jsにコンパイルする方法
  2. WebAssembly(以下、WASM)にコンパイルする方法

事前準備

Rustコンパイラ(rustup)のインストール

$ curl https://sh.rustup.rs -sSf | sh

# 環境に合わせて .zshrc や .bashrc に以下を追記
$ source ~/.cargo/env

トラブルシューティング

先にhomebrew経由でrustをインストールしていると、下記のようなエラーが出力され、rustupのインストールに失敗します。

rustを既にインストールしている場合のエラーメッセージ
$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer
error: it looks like you have an existing installation of Rust at:
error: /usr/local/bin
error: rustup cannot be installed alongside Rust. Please uninstall first
error: if this is what you want, restart the installation with `-y'
error: cannot install while Rust is installed

その場合は、brew uninstall --force rust でhomebrew経由のrustをアンインストールしましょう。

$ brew uninstall --force rust

Emscripten のインストール

$ git clone https://github.com/juj/emsdk.git
$ cd emsdk

# 環境に合わせて .zshrc や .bashrc に以下を追記
$ source ~/emsdk/emsdk_env.sh

# かなり重たいコンパイルが走るので注意しましょう。手元の環境では数十分かかりました。
$ ./emsdk install sdk-incoming-64bit binaryen-master-64bit

$ ./emsdk activate sdk-incoming-64bit binaryen-master-64bit

トラブルシューティング

cmakeがインストールされていないと、下記のエラーが出力されます。

cmakeが無い場合のエラーメッセージ
[Errno 2] No such file or directory
Could not run CMake, perhaps it has not been installed?
Installing this package requires CMake. Get it via a OSX package manager (Homebrew: "brew install cmake", or MacPorts: "sudo port install cmake"), or from http://www.cmake.org/
Installation failed!

その場合は、brew install cmake しましょう。

$ brew install cmake

RustのプログラムをWebブラウザで動かす

プロジェクトの作成

まずは、cargoコマンドを使って、「Hello, world!」と出力するプロジェクトを作成します。

$ cargo new --bin hello
$ cd hello

# ファイルの中身を確認
$ cat src/main.rs
fn main() {
    println!("Hello, world!");
}

# ビルド+実行
$ cargo run
Hello, world!

このプロジェクトをWebブラウザで動かすには、asm.jsにコンパイルする方法と、WebAssembly(以下、WASM)にコンパイルする方法の2通りがあります。
それぞれ順に紹介します。

方法1:asm.jsをターゲットにビルド

# asm.jsをターゲットアーキテクチャに追加
$ rustup target add asmjs-unknown-emscripten

# asm.jsをtargetにビルド
$ cargo build --target asmjs-unknown-emscripten

# ビルドの生成物を確認
$ ls target/asmjs-unknown-emscripten/debug
build       deps        examples    hello.d     hello.js    incremental native

ams.jsをブラウザ上で動かす

プロジェクトの直下に、次のようなhtmlファイルを作りましょう。

amsjs.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Rust to asm.js example</title>
    </head>
    <body>
        <script src="target/asmjs-unknown-emscripten/debug/hello.js"></script>
    </body>
</html>

ローカルでHTTPサーバを立てます。これはRubyでサーバを立てる例です。

$ ruby -run -e httpd . -p 3000

http://localhost:3000/asmjs.html にブラウザでアクセスし、開発コンソールを表示しましょう。
成功すれば「Hello, world!」と表示されます。

amsjs.html

方法2:WASMをターゲットにビルド

ターゲット名が asmjs-unknown-emscripten -> wasm32-unknown-emscripten に変わったことを除けば、asm.jsと全く同じ手順です。

# WASMをターゲットアーキテクチャに追加
$ rustup target add wasm32-unknown-emscripten

# WASMをtargetにビルド
$ cargo build --target=wasm32-unknown-emscripten

# ビルドの生成物を確認
$ ls target/wasm32-unknown-emscripten/debug
build       deps        examples    hello.d     hello.js    hello.wasm  incremental native

WASMをブラウザ上で動かす

asm.jsとほぼ同様の手順です。WASMでは次ようなhtmlを作成します。

wasm.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Rust to WebAssembly example</title>
    </head>
    <body>
        <script>
        var Module = {}
        fetch('target/wasm32-unknown-emscripten/debug/hello.wasm')
            .then((response) => response.arrayBuffer())
            .then((buffer) => {
                Module.wasmBinary = buffer
                var script = document.createElement('script')
                script.src = "target/wasm32-unknown-emscripten/debug/hello.js"
                document.body.appendChild(script)
            })
        </script>
    </body>
</html>

http://localhost:3000/wasm.html にブラウザでアクセスし、開発コンソールを表示しましょう。
成功すれば「Hello, world!」と表示されます。

wasm.html

参考記事

以下の記事を大変参考にさせていただきました。本当にありがとうございます!

次回予告

今年の秋に開催されたレイトレ合宿5‽で、Rustを使って物理ベースレンダラーを自作しました。

このレンダラーをぜひ多くの人に動かしてもらいたくWebに移植しようと考えたのですが、その事前調査として簡単なRustのプログラムをブラウザ上で動かすまでに試行錯誤しました。そこで、今回はその手順をまとめて記事にさせていただきました。

次回は「自作のRustによる物理ベースレンダラーをWebに移植した話」について書きたいと思います。