Slide 1

Slide 1 text

RubyGems on ruby.wasm Yuta Saito (@kateinoigakukun) RubyKaigi 2024 1

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

About me • Yuta Saito / @kateinoigakukun • Master's student at Waseda University • Newbie commi>er of CRuby and SwiB • Creator of ruby.wasm 3

Slide 4

Slide 4 text

Overview 1. Introduc+on to ruby.wasm 2. Challenges of RubyGems support 3. Demo 4. Technical details 5. Future vision 4

Slide 5

Slide 5 text

1. Introduc,on: ruby.wasm • Introduced in RubyKaigi 2022 • Enables running Ruby on everywhere by WebAssembly • This project consists of: 1. CRuby interpreter compiled to WebAssembly 2. Packaging system for interpreter + .rb files 3. JS interop library ( require 'js') 5

Slide 6

Slide 6 text

require 'js' button = JS.global[:document].createElement("button") button[:textContent] = "Click me!" button.addEventListener("click") do puts "Hello~" end response = JS.global.fetch("https://example.com/data.json").await puts response.json.await 6

Slide 7

Slide 7 text

$ wasmtime run ruby.wasm hello.rb Hello, world! 7

Slide 8

Slide 8 text

But, what about RubyGems? 8

Slide 9

Slide 9 text

2. Challenges of RubyGems support RubyGems has two main components: 1. Installer: gem install 2. Run:me loader: require 'gem_name' 9

Slide 10

Slide 10 text

2. Challenges of RubyGems support Run$me loader ✅ • Supported from early versions • require 'gem_name' works as expected 10

Slide 11

Slide 11 text

2. Challenges of RubyGems support Gem installer ! When to install gems? • On-the-fly installa/on • Pre-installa/on 11

Slide 12

Slide 12 text

2. Challenges of RubyGems support On-the-fly installa.on Install Gems at run-.me • Convenient way for daily scrip4ng • Run gem install on Wasm • Requires network access at 12

Slide 13

Slide 13 text

2. Challenges of RubyGems support On-the-fly installa.on • I showed on-the-fly installa2on demo at RubyKaigi 2022. • Network access is passed through the browser's fetch API. • You can try it now on runruby.dev by Svyatoslav • No built-in support 13

Slide 14

Slide 14 text

2. Challenges of RubyGems support Pre-installa+on Install Gems in advance before running the Ruby program. • No network access at Run- 1me • "build" step before running the Ruby program. 14

Slide 15

Slide 15 text

Today's focus Pure-Ruby Gems C-Extension Gems On-the-fly installa.on ⚠ ❌ Pre-installa.on ❌ ❌ 15

Slide 16

Slide 16 text

Today's focus Pure-Ruby Gems C-Extension Gems On-the-fly installa.on ⚠ ❌ Pre-installa6on # # 16

Slide 17

Slide 17 text

3. Demo: Mastodon in the browser 17

Slide 18

Slide 18 text

18

Slide 19

Slide 19 text

h"ps:/ /mastodon-in-browser.vercel.app/boot.html Mastodon in the browser Inspired by 19

Slide 20

Slide 20 text

3. Demo - Mastodon in the browser 20

Slide 21

Slide 21 text

Possible use case? "Serverless Staging": Host Rails app as a sta3c site • Low-cost • Low security risk • e.g. Preview environments for Pull Requests 21

Slide 22

Slide 22 text

4. Technical details 22

Slide 23

Slide 23 text

4. Technical details 1. Package Gems 2. Cross-compile C-Extensions 3. Pre-link C-Extension shared libraries 23

Slide 24

Slide 24 text

4.1. Package Gems Package gem source files into the WebAssembly binary by VFS # WARNING: Still Experimental $ bundle add ruby_wasm --group=development $ bundle add rainbow $ bundle exec rbwasm build -o ruby.wasm (snip) INFO: Packaging gem: rainbow-3.1.1 INFO: Size: 51.17 MB 24

Slide 25

Slide 25 text

4.1. Package Gems 25

Slide 26

Slide 26 text

4.2. Cross-compile C-Extensions Cross-compila,on support in mkmf • mkmf is the core of the C-extension build system in Ruby • 3 major mkmf users (extconf.rb consumers in in other words): • CRuby build system • rake-compiler • RubyGems 26

Slide 27

Slide 27 text

4.2. Cross-compile C-Extensions Cross-compila,on support status in major mkmf users • CRuby build system & rake-compiler: • Fake rbconfig.rb trick • RubyGems: " • Does not support cross-compila

Slide 28

Slide 28 text

4.2. Cross-compile C-Extensions Exis%ng rbconfig.rb trick • Overwrite top-level RbConfig constants • Does not work for some corner cases $ ruby -rrbconfig -rwasm32-wasi-fake extconf.rb # ./rbconfig.rb module RbConfig # For target platform ... end # ./wasm32-wasi-fake.rb class Object remove_const :CROSS_COMPILING if defined?(CROSS_COMPILING) CROSS_COMPILING = RUBY_PLATFORM constants.grep(/^RUBY_/) {|n| remove_const n} RUBY_VERSION = "3.4.0" RUBY_RELEASE_DATE = "2024-05-11" RUBY_PLATFORM = "wasm32-wasi" ... end 28

Slide 29

Slide 29 text

# From https://github.com/rake-compiler/rake-compiler/blob/v1.2.7/lib/rake/extensiontask.rb # Pre-load resolver library before faking, in order to avoid error # "cannot load such file -- win32/resolv" when it is required later on. # See also: https://github.com/tjschuck/rake-compiler-dev-box/issues/5 require 'resolv' require 'rbconfig' ... 29

Slide 30

Slide 30 text

4.2. Cross-compile C-Extensions Cross-compila,on support without trick ✅ Feature #20345: Add --target-rbconfig op3on to mkmf2 h9ps:/ /bugs.ruby-lang.org/issues/20345 $ ruby extconf.rb --target-rbconfig=path/to/rbconfig.rb creating Makefile $ make $ file nokogiri.so nokogiri.so: WebAssembly (wasm) binary module version 0x1 (MVP) 2 Will be available in Ruby 3.4 and later 30

Slide 31

Slide 31 text

4.2. Cross-compile C-Extensions Cross-compila,on support in RubyGems ! PR #7628: Add --target-rbconfig op.on to gem install h"ps:/ /github.com/rubygems/rubygems/pull/7628 $ gem install nokogiri --target-rbconfig=path/to/rbconfig.rb $ bundle install --target-rbconfig=path/to/rbconfig.rb 31

Slide 32

Slide 32 text

4.3. Pre-link C-Extension shared libraries 32

Slide 33

Slide 33 text

4.3. Pre-link C-Extension shared libraries Why not sta*c linking? • We already support sta/cally-linked C-extensions • But, the build model is not suitable for the RubyGems ecosystem • Take a long linking /me (especially due to Asyncify) • Need to /ghtly integrate with the CRuby build system 33

Slide 34

Slide 34 text

Sta$c linking Dynamic linking 34

Slide 35

Slide 35 text

4.3. Pre-link C-Extension shared libraries Why not dynamic linking so far? • WebAssembly does not have a standard dynamic linking ABI • If we do our bespoke dynamic linking3, the binary will be incompa?ble with standard WebAssembly run?mes 3 Emscripten does dynamic linking by its own loader 35

Slide 36

Slide 36 text

4.3. Pre-link C-Extension shared libraries Breakthrough: Component Model • A new layer of WebAssembly standard • Consists of: • WIT (WebAssembly Interface Types) IDL • Module linking • ... and more 36

Slide 37

Slide 37 text

4.3. Pre-link C-Extension shared libraries Module Linking 37

Slide 38

Slide 38 text

4.3. Pre-link C-Extension shared libraries Dynamic Linking upon Component Model • Each module has its metadata for dynamic linking • "AOT" linker encapsulates "how to link modules" into Component 38

Slide 39

Slide 39 text

4.3. Pre-link C-Extension shared libraries AOT dynamic linking A new concept of dynamic linking in WebAssembly • Done at the build /me • The final binary is a Component • The linker is originally developed by Joel Dice in wit-component • Adjusted for Ruby use case 39

Slide 40

Slide 40 text

4.3. Pre-link C-Extension shared libraries Difference from the tradi0onal dynamic linking • The final binary, a Component, contains all the dependencies • No file-based shared library search at run:me • dlopen is available only for libraries known at build-:me 40

Slide 41

Slide 41 text

Current status Pure-Ruby Gems C-Extension Gems On-the-fly installa.on ⚠ ❌ Pre-installa6on ✅ ✅ (soon) 41

Slide 42

Slide 42 text

5. Future vision 42

Slide 43

Slide 43 text

Roadmap 43

Slide 44

Slide 44 text

Interpreter size 44

Slide 45

Slide 45 text

Summary • RubyGems support will be available soon on ruby.wasm • Ruby is leveraging the state-of-the-art WebAssembly technology including the Component Model 45

Slide 46

Slide 46 text

Acknowledgements • Supported by the Ruby Associa4on Grant Program • Advised by @mame and @ko1 46