PlantUMLをWASMに移植してブラウザ上で動かせないか
2022年 GitHubがmermaidによる作図に対応した。
PlantUML派としては悔しいです。
しかしmermaid-jsのいいところはJavaScriptベースなのでブラウザの中で完結する。
それ故にWikiとの統合がしやすかったのだろう。
対してPlantUMLはJavaベースなのがつらい。
- JREがないと動かない
- VSCodeで使うときもここはちょっと煩わしい
- Appletの廃れた今、ブラウザ上でJavaコードを動かすのは無い
- それ故にplantuml-serverを立てる必要がある
- サーバーにするとトラフィック量によるスケール費用とか、DoS攻撃とか色々気になるから採用しづらくなる気持ちがわかる
- それ故にplantuml-serverを立てる必要がある
- おまけにGraphviz依存もある
↓
PlantUMLがWASMになればブラウザ上で完結して幸せになれるんじゃね?
このIssueにてslim versionの必要性を訴えている人がいるけど、タイトルに反してWASMの話は全然出てこない。
PlantUMLがどう実装されているか調べてみる
ビルドシステム複雑だな・・・
- gradle -> kotlinだ
-
maven -> pom.xmlあるしそうだよね?pom.xml消えた - ant -> build.xmlがそれっぽい
3つも使ってんの??
結局どこでGraphvizと連携しているんや…
PlantUML記法→DOT記法からのGraphvizでSVG生成だと思ってたけど違うのだろうか?
PlantUML2
Haxeで書き直してマルチプラットフォーム化を目指しているよう。HaxeからJavaScriptへもトランスパイルできるみたいなのでWeb上でPlantUMLが使いやすくなるのは期待できそうだが。
We will port to Haxe the Smetana project which means that PlantUML 2 will not need any Graphviz/dot executable to run.
ということでGraphviz依存はなくなる模様。
そもそもWASMどう動かすのよ?
これはちょろっとやってみたけど、
- 基本的にwasm-pack crateのチカラ
- Rustコードをwasm形式にコンパイルしてくれる以外にも、
- JavaScript -> wasm内のFunctionに対するバインディングを用意してくれる
- Rust側からJavaScriptのFunctionを呼び出せるバインディングを用意してくれる
という感じで、直接wasmが動くわけでなく、JavaScriptがWASM内のFunctionを呼び出せるようにしているもの。つまりアプリケーションのBootstrap的なところはHTML+JavaScriptの世界で書く必要がある。
このことは上記MDNでも述べられている。
Rust と WebAssembly には、主に 2 つのユースケースがあります。
- アプリケーション全体を構築する — ウェブアプリ全体を Rust ベースで構築します。
- アプリケーションの一部を構築する — 既存の JavaScript フロントエンドの内部で Rust を使用します。 <--こっちにフォーカスされている
Webサイト上でPlantUMLを動かすという意味ではwasm-packのアプローチはマッチしている。
VSCodeの拡張として動かすケースでもマッチする。
WASIも試しておきたい
本筋とは少し異なるがコマンドラインツールの側面としてWASIも試しておきたい
- WASIはBrowser, JS independet なWASM仕様 -> ランタイムのためのインターフェース?
- BytecodeAllianceが中心となって策定している
- WASI実装としては wasmtime, wasmer, lucetの3つがメジャーっぽい
wasmtime
- BytecodeAllienceのリファレンス実装
- 実用性でいうと起動速度、実行速度ともに物足りなくなっている様子
wasmer
- 単純なWASI実装を超えてエコシステム目指してるっぽい
- NPMライクなWAPMとか
- 商業感ある
lucet
- CDNで有名なFastly主管
- Fastlyはエッジコンピューティングの有力な手段としてwasm推し
- BytecodeAllianceのwasmtimeと密な連携してそう
- ...と思ったら開発終了してwasmtimeと合流してる
- もともとwasmtimeの派生でその成果をwasmtimeに還元してプロジェクトを終了したっぽい
wasmtimeでHello World
そのままの流れでWasmerも。コンテナ化はちゃんとしてない。
$ sudo apt update
$ sudo apt install libxkbcommon0
$ sudo apt install libtinfo5
$ curl https://get.wasmer.io -sSfL | bash
libxkbcommon0 問題は https://github.com/wasmerio/wasmer/issues/2822 で議論されてた。
同じwasmファイルが当然wasmerでも動く
$ wasmer ./target/wasm32-wasi/debug/hello.wasm
Hello, world!
単純なHello Worldだからアレだけどwasmer早いな…
$ time wasmer ./target/wasm32-wasi/debug/hello.wasm
Hello, world!
real 0m0.007s
user 0m0.007s
sys 0m0.000s
$ time wasmtime ./target/wasm32-wasi/debug/hello.wasm
Hello, world!
real 0m0.014s
user 0m0.015s
sys 0m0.000s
$ time ./target/debug/hello
Hello, world!
real 0m0.001s
user 0m0.001s
sys 0m0.000s
File I/Oのチュートリアルやってみる
なるほど、サンドボックスとして --dir, --mapdirでファイルアクセス範囲をホワイトリストで指定するようになっているのか。
The --dir= option instructs wasmtime to preopen a directory, and make it available to the program as a capability which can be used to open files inside that directory.
結局、WASI/WASMの関係性は何なのか?
- 一般的にWASMと呼ばれているものはブラウザ向けのWebAssemblyのこと
- WebAssembly Core に JavaScript APIとWeb APIをのっけたもの
- ランタイム実装としてはV8とかSpiderMonkeyとか
- それこそEmbeddedされている
- 対してWASIはCoreにWASI APIをのっけたもの
- 正確にはInterfaceなのでWASI API仕様だけで、実装としてwasmerやwasmtimeなどがある
- こちらもOSネイティブなランタイムにEmbeddedされている
- 正確にはInterfaceなのでWASI API仕様だけで、実装としてwasmerやwasmtimeなどがある
つまり、ブラウザ向けのWASMとWASI向けのWASMには差がある。積集合に当たる部分はCoreで共通するが使えるcapabilityが異なる。
ブラウザ向けにコンパイルされた(アセンブルされたというべき?)wasmはWASIでは動かないかもしれない=依存するAPIが使えないかもしれない
理解の役に立った文献
Graphvizをwasm用にどうにかする
Emscriptenでビルドしてwasm化している例があるので、これを真似ればRustコードからGraphvizを使える気がする。
一方でpure Rustでdot記法をsvgにするプログラムを書いている人もいるがすべてのdot記法をカバーは指定なさそう。実験的なものかな?
hpcc-js-wasmをビルドして何が起きているのかを解析してみる
npm run install-build-deps
- Graphviz公式のGitLabからソースを取得
- cmakeを使って
./src-graphviz/build
の中にビルドする- 例)
./src-graphviz/build/lib/gvc/libgvc.so.6.0.0
- 例)
npm run build
- ツールチェインとしてemscripten使う
- あとはCMakeにヨロ
これじゃ何もわからんやんけ
cpp/graphviz/CMakeLists.txt を追う
src-graphviz/lib
src-graphviz/plugin
src-graphviz/build
cpp/graphviz/lib
cpp/graphviz/plugin
cpp/graphviz/graphvizlib
build.rsに書き直す
cmake crateの使い方はなんとなくわかった。
CMAKE_TOOLCHAIN_FILEにEmscripten.cmakeを指定するのは合っているのか??
hpcc-js-wasmのスクリプト実行結果
当たり前だけど正常に完了する。
build/graphvizlib/
├── CMakeFiles
│ ├── graphvizlib.dir
│ │ └── main.cpp.o
├── graphvizlib.js
└── graphvizlib.wasm
Scanning dependencies of target graphvizlib
[ 99%] Building CXX object graphvizlib/CMakeFiles/graphvizlib.dir/main.cpp.o
[100%] Linking CXX executable graphvizlib.js
[100%] Built target graphvizlib
今回はjsとwasmファイルはいらないんだよな。
CMAKE_TOOLCHAIN_FILE無指定だとgcc使ってリンクがうまくいかない
Emscripten.cmakeを指定すると最後のgraphvizlib.js作るところでコケる
failed (returned 1)
make[2]: *** [graphvizlib/CMakeFiles/graphvizlib.dir/build.make:111: graphvizlib/graphvizlib.js] Error 1
make[1]: *** [CMakeFiles/Makefile2:1608: graphvizlib/CMakeFiles/graphvizlib.dir/all] Error 2
make: *** [Makefile:130: all] Error 2
wasm + jsを吐く挙動になってたのはADD_EXECUTABLE
だったから。
いきなりGraphvizをどうにかするのは難しかったので最小限で試した。
心が折れそう。