ここまでのあらすじ
<読み飛ばし推奨>
子どもたちのためにJava版マインクラフトを買い、よーしパパ*1MOD作って「威厳*2」を見せちゃうぞ〜 と思ったが全くやる気配がない。
一方で、子どもは無限の時間をScratchに費やしていた。Scratch 3.0はReactで作られていることが知られており、いわゆるSPA(もしかして、この用語もう古い?)であり、基本的にはGitHub Pagesのようなバックエンドレスな環境にもデプロイできる。
また、TypeScript/JavaScript+Reactで拡張を書くことが出来る。自作の拡張は残念ながら公式のScratchでは利用できないが、自分でビルドしてデプロイしたScratchインスタンスで使うことができる。一種のMODである。例えば機械学習に触れることが出来る拡張を有効にしたMODが知られている。
champierre.github.io
ということで、サーバサイドやカーネルHackをいくらやっても子どもたちに相手をしてもらうのは難しい*3ため、フロントエンドをやっていくことにした。
しかしフロントエンド、ここまでのキャリアにおいてほぼ、全く触れていない、せいぜいVueとReactのチュートリアルをなぞってTODOアプリをマスターした程度のレベルだし、それも忘れてしまった。
ひとまずとっつきやすいところから始めよう、ならWebAssemblyだな、なんかRubyも対応したし、と思ったのでそこからやることにした。前途多難であることは言うまでもない。
www.ruby-lang.org
</読み飛ばし推奨>
要するにRuby 3.2 preview1 のソースコードから WASM 版Rubyをビルドすることにした。
必要な情報は以下のリポジトリにある。
github.com
が、ここでLinux環境であればDockerfileが提供され、どう言うツールが必要でどうインストールするかが全部まとまっており、このイメージを作ればビルドできる...
はずなのだが見れば分かるとおり x86_64 環境のみを想定している。これは関連ツールがLinuxの場合x86_64のバイナリしかないとか、色々仕方ない面がある。すなわちApple Siliconの上で生活してる人の場合、Linux環境、ことDockerイメージを作ってその上で作業というのは逆に難しい。
この辺は以前znzさんが試した際にも言及されている。
blog.n-z.jp
今回はmacOS + Apple Siliconな環境でのビルド手順をまとめる。
ひとまず今回必要なツールキットは以下のような感じである。
順番にインストール方法を解説する。
公式に配られているバイナリ、Mac版はアーキテクチャが明示されておらず、不安なので自分でビルドする。
github.com
ビルドの依存は cmake、clang、ninja らしい。いかついが、clangはXCodeのアレで入ってるはずだし、cmakeとninjaはHomebrewなどで入れれば問題ない。
あとはチェックアウトして以下でOK。
git checkout wasi-sdk-14
NINJA_FLAGS=-v make package
build/wasi-sdk-14.0
に関連ファイルが作られるので ~/wasi
など適当なディレクトリにコピーする。
cp -vr build/wasi-sdk-14.0/* ~/wasi/
Binaryen
wasi-sdkはWASIのSDKか! って感じだが、Binaryen が何者か調べてみても難しい...。
- 何かしらの言語のソースコードをWebAssemblyにコンパイルするためのツールキット
- Binaryen IRというものを持っていて使える
こんな感じだと理解した。
こちらはarm64のMac向けバイナリがある。
wget https://github.com/WebAssembly/binaryen/releases/download/version_105/binaryen-version_105-arm64-macos.tar.gz
tar xzf binaryen-version_105-arm64-macos.tar.gz
cp -vr binaryen-version_105/* ~/wasi/
いよいよruby.wasmリポジトリに何があるかの話になる*4。
github.com
Emscriptenで作ったやつとWASIの上に作るやつがあるらしく、WASI向けの情報は このディレクトリ にまとまっているようだが、ひとまず rake コマンドで色々ビルドできる。
$ rake -T
rake build:head-wasm32-unknown-emscripten-full # Build head-wasm32-unknown-emscripten-full
rake build:head-wasm32-unknown-emscripten-minimal # Build head-wasm32-unknown-emscripten-minimal
rake build:head-wasm32-unknown-wasi-full # Build head-wasm32-unknown-wasi-full
rake build:head-wasm32-unknown-wasi-full-debug # Build head-wasm32-unknown-wasi-full-debug
rake build:head-wasm32-unknown-wasi-full-js # Build head-wasm32-unknown-wasi-full-js
rake build:head-wasm32-unknown-wasi-full-js-debug # Build head-wasm32-unknown-wasi-full-js-debug
rake build:head-wasm32-unknown-wasi-minimal # Build head-wasm32-unknown-wasi-minimal
rake build:head-wasm32-unknown-wasi-minimal-debug # Build head-wasm32-unknown-wasi-minimal-debug
rake build:head-wasm32-unknown-wasi-minimal-js # Build head-wasm32-unknown-wasi-minimal-js
rake build:head-wasm32-unknown-wasi-minimal-js-debug # Build head-wasm32-unknown-wasi-minimal-js-debug
rake deps:libyaml-wasm32-unknown-emscripten # build libyaml 0.2.5 for wasm32-unknown-emscripten
rake deps:libyaml-wasm32-unknown-wasi # build libyaml 0.2.5 for wasm32-unknown-wasi
rake fetch_artifacts[run_id] # Fetch artifacts of a run of GitHub Actions
rake npm:all # Build all npm packages
rake npm:configure_prerelease[prerel] # Configure for pre-release
rake npm:ruby-head-wasm-emscripten # Build npm package ruby-head-wasm-emscripten
rake npm:ruby-head-wasm-emscripten-check # Check npm package ruby-head-wasm-emscripten
rake npm:ruby-head-wasm-wasi # Build npm package ruby-head-wasm-wasi
rake npm:ruby-head-wasm-wasi-check # Check npm package ruby-head-wasm-wasi
rake publish[tag] # Publish artifacts as a GitHub Release
rake wapm:irb-build # Build wapm package irb
rake wapm:irb-publish # Publish wapm package irb
rake wapm:ruby-build # Build wapm package ruby
rake wapm:ruby-publish # Publish wapm package ruby
wasm32-unknown
みたいなのはRustのターゲット名に合わせた呼び方だと思う。で、suffixがいくつかあって
minimal
: 拡張(Cコンパイルが必要ということだと思う) standard library 抜きでビルドする(like json, yaml, or stringio など)
full
: 拡張ライブラリ含め全部ビルドする
*-js
: JavaScript で使いやすいような形でビルドし、Glue的なファイルも用意する。npmパッケージにするみたい
*-debug
: DWARFの情報や、WASMのnameセクション? を含む。明らかにバイナリサイズがデカくなりそう。
今回はとにかくシンプルに rake build:head-wasm32-unknown-wasi-minimal
を試す。通常のRubyのビルドよろしくconfigureから走る。
$ rake build:head-wasm32-unknown-wasi-minimal
warning: vfs feature is not enabled due to no LIB_WASI_VFS_A
/opt/ghq/github.com/ruby/ruby.wasm/build/src/head/configure --host wasm32-unknown-wasi --build arm64-apple-darwin21 --with-static-linked-ext --with-ext="" --with-libyaml-d
ir="/opt/ghq/github.com/ruby/ruby.wasm/build/deps/wasm32-unknown-wasi/opt/libyaml/usr/local" LDFLAGS="-Xlinker -zstack-size=16777216" XLDFLAGS="/opt/ghq/github.com/ruby/ru
by.wasm/build/ext-build/head-wasm32-unknown-wasi-minimal/extinit.o" debugflags="-g0" --disable-install-doc
checking for ruby... /Users/udzura/.rbenv/versions/3.1.1/bin/ruby
tool/config.guess already exists tool/config.sub already exists
checking build system type... aarch64-apple-darwin21
checking host system type... wasm32-unknown-wasi
checking target system type... wasm32-unknown-wasi
checking for wasm32-unknown-wasi-wasm-opt... no
checking for wasm-opt... wasm-opt
configure: WARNING: using cross tools not prefixed with host triplet
checking wheather $WASI_SDK_PATH is set... yes
checking for wasm32-unknown-wasi-cl.exe... /Users/udzura/wasi/bin/clang
checking for /Users/udzura/wasi/bin/llvm-ar... /Users/udzura/wasi/bin/llvm-ar
checking for /Users/udzura/wasi/bin/clang++... no
checking for /Users/udzura/wasi/bin/llvm-nm... no
...
WASMなRubyは rubies
ディレクトリにできる。
$ file rubies/head-wasm32-unknown-wasi-minimal/work-wasm/usr/local/bin/ruby
rubies/head-wasm32-unknown-wasi-minimal/work-wasm/usr/local/bin/ruby: WebAssembly (wasm) binary module version 0x1 (MVP)
$ cd rubies/head-wasm32-unknown-wasi-minimal/work-wasm/usr/local/bin
$ wasmtime ./ruby -- -e 'p RUBY_VERSION'
`RubyGems' were not loaded.
`error_highlight' was not loaded.
`did_you_mean' was not loaded.
"3.2.0"
という感じ。この先は... あまり考えていない...。