虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

【WebAssembly連載第五回】WASIを触ってみようその1

本記事は「WebAssembly連載」の第五回目の記事です。

皆さんこんにちは。虎の穴ラボのY.Fです。

前回の記事では、WebAssemblyのポータビリティを活かして、サーバーサイド、フロントエンド共通のライブラリでマークダウンエディタを作ってみました。

toranoana-lab.hatenablog.com

本連載の締めくくりとして、今回と、次回の記事ではWASIについて触れていこうと思います。

WASIとは

WASIは、WebAssembly System Interface の略です。名前の通りインターフェースの定義ですが、Bytecode Allianceによって策定されています。

wasi.dev

bytecodealliance.org

以前の記事で紹介したとおり、WebAssemblyが特徴としてもっているセキュアな部分やポータビリティに重点がおかれているようです。

ランタイムとしては以下の様なものがあります

  • wasmtime
    • 上記のBytecode Allianceによるプロジェクト
    • 高速かつセキュアなWebAssemblyランタイムを謳っています

wasmtime.dev

  • wasmer
    • wasmer社が作っているプロジェクト
    • wasmtimeより実行速度が高速なことや対応言語が幅広いことを謳っている ( Wasmer vs Wasmtime )

wasmer.io

今回の記事ではwasmtimeを作って簡単なサンプルを作り、WASIについて学んでみたいと思います。WebAssemblyを作成する言語としてはRustを利用します。

チュートリアル

以下公式ドキュメントにチュートリアルがあります。

docs.wasmtime.dev

今回Rustのインストールはされていることを前提とします。まずは、Rustのビルドターゲットとしてwasiを追加します。

$ rustup target add wasm32-wasi

wasm-bindgenなどを利用せず、Rust単独でWebAssemblyを使う場合のビルドターゲットとしては wasm32-unknown-unknown などを使う場合が多いと思います。同様の形でWASI用のビルドターゲットも存在します。

また、他のビルドターゲットが知りたければ以下のようなコマンドで見ることができます。

❯ rustc --print target-list | grep wasm
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
wasm64-unknown-unknown

インストールができたら、以下のコマンドでRustのプロジェクトを作成します。

$ cargo new wasi-demo

デフォルトの状態ではsrc/main.rsは以下のようになっているかと思います。

fn main() {
        println!("Hello, world!");
}

これをこのままWebAssemblyにしてwasmtimeで実行してみます。まずは先程インストールしたビルドターゲットでビルドします。

$ cargo build --target wasm32-wasi

次に、wasmtimeをインストールして実行してみます。

$ curl https://wasmtime.dev/install.sh -sSf | bash
$ wasmtime target/wasm32-wasi/debug/wasi-demo.wasm
Hello, world!

OKです。簡単なチュートリアルですが、Rustで作ったWebAssemblyをwasmtimeで実行できました。

ファイル読み書き

次に、前回の記事で作成したマークダウンライブラリに少し手を加えて以下の機能を作ってみたいと思います。

  • 実行時の引数でパース対象のファイルを受け取る
  • 実行結果のhtmlを保存する

まずは、Cargo.tomlに必要なライブラリを記述します。

[package]
name = "wasi-demo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pulldown-cmark = "0.9.2"
wasm-bindgen = "=0.2.83"
ammonia = "3.3.0"

main.rsを以下のようにします。マークダウンをパースする部分は前回記事と同様です。main関数でのコマンドライン引数及びファイル処理だけ追加しています。また、wasm_bindgenは使わないので削除しています。

use ammonia::clean;
use pulldown_cmark::{html, Options, Parser};
use std::env;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;

fn read_markdown(filename: &str) -> Result<String, String> {
    Ok(fs::read_to_string(filename)
        .map_err(|err| format!("error opening input {}: {}", filename, err))?)
}

fn parse_markdown(markdown_input: &str) -> String {
    let mut options = Options::empty();
    options.insert(Options::ENABLE_STRIKETHROUGH);
    let parser = Parser::new_ext(markdown_input, options);

    let mut html_output = String::new();
    html::push_html(&mut html_output, parser);
    clean(&*html_output)
}

fn write_html(html: &str) -> Result<(), String> {
    let path = Path::new("./example.html");

    let mut file =
        File::create(&path).map_err(|err| format!("error create file example.html: {}", err))?;
    file.write_all(html.as_bytes())
        .map_err(|err| format!("error write file example.html: {}", err))?;
    Ok(())
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    if args.len() < 2 {
        eprintln!("usage: {} <filepath>", program);
        return;
    }

    let contents = match read_markdown(&args[1]) {
        Ok(c) => c,
        Err(err) => {
            eprintln!("{}", err);
            return;
        }
    };

    let html = parse_markdown(&contents);
    match write_html(&html) {
        Ok(_) => println!("success markdown to html. look at example.html"),
        Err(err) => {
            eprintln!("{}", err);
        }
    };
}

素直なRustのソースファイルだと思います。

先ほどと同様にビルドしてみます。

$ cargo build --target wasm32-wasi

実行します。

$  wasmtime --dir . target/wasm32-wasi/debug/wasi-demo.wasm ./example.md

ポイントは --dir オプションで、ディレクトリへのアクセス権限を与えてやる必要がある点かと思います。

以下のmdをインプットとして、

# WebAssemblyでマークダウンライブラリを作る

この文章はサーバーサイドでWebAssemblyを利用して、マークダウンから作られたものです。

利用技術は以下になります。

- Rust
- [pulldown_cmark](https://crates.io/crates/pulldown-cmark)
- Deno
  - fresh
  - bbb

以下のHTMLが生成されます。

<h1>WebAssemblyでマークダウンライブラリを作る</h1>
<p>この文章はサーバーサイドでWebAssemblyを利用して、マークダウンから作られたものです。</p>
<p>利用技術は以下になります。</p>
<ul>
<li>Rust</li>
<li><a href="https://crates.io/crates/pulldown-cmark" rel="noopener noreferrer">pulldown_cmark</a></li>
<li>Deno
<ul>
<li>fresh</li>
<li>bbb</li>
</ul>
</li>
</ul>

これで、WASIを使うことで、WebAssemblyでファイル読み書きができました。

まとめ

今回は、WASIについて基本的なところに触れてみました。次回の記事では、もう少しWASIについて深堀りし、もっと何ができるのか探ってみたいと思います。

Web技術があまり関係ない箇所でWebAssemblyを使えるようになれば、より活用できると思うので、これからもWASIには注目していきたいです。

P.S.

採用

虎の穴では一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧下さい。
yumenosora.co.jp