Slide 1

Slide 1 text

It’s about time to pack Ruby and Ruby scripts in one binary RubyKaigi 2024 ahogappa

Slide 2

Slide 2 text

About me ● Sho Hirano(@ahogappa) ● STORES, Inc. ● #FFF613 2

Slide 3

Slide 3 text

What’s one binary? 3

Slide 4

Slide 4 text

What’s one binary? Works perfectly with just one file 4

Slide 5

Slide 5 text

Why one binary? 5

Slide 6

Slide 6 text

Why one binary in Ruby? 6

Slide 7

Slide 7 text

Why one binary in Ruby? e.g. distributing a Ruby project 7

Slide 8

Slide 8 text

Why one binary in Ruby? 💻 💻 developer user 8

Slide 9

Slide 9 text

Why one binary in Ruby? 💻 a.rb a.rb a.rb a.rb 💻 developer user 9

Slide 10

Slide 10 text

Why one binary in Ruby? 💻 a.rb a.rb a.rb a.rb 💻 Ruby 3.3.0 Sinatra 4.0.0 Sqlite3 2.0.1 developer user 10

Slide 11

Slide 11 text

Why one binary in Ruby? 💻 a.rb a.rb a.rb a.rb 💻🚫 Ruby 3.3.0 Sinatra 4.0.0 Sqlite3 2.0.1 Ruby 3.0.0 Sinatra 3.2.0 Sqlite3 Not installed developer user 11

Slide 12

Slide 12 text

Why one binary in Ruby? 💻 💻 Ruby 3.3.0 Sinatra 4.0.0 Sqlite3 2.0.1 developer user 12 Ruby 3.0.0 Sinatra 3.2.0 Sqlite3 Not installed

Slide 13

Slide 13 text

Why one binary in Ruby? 💻 💻 one binary developer user Ruby 3.3.0 Sinatra 4.0.0 Sqlite3 2.0.1 13 Ruby 3.0.0 Sinatra 3.2.0 Sqlite3 Not installed

Slide 14

Slide 14 text

Why one binary in Ruby? 💻 💻✔ one binary Ruby 3.3.1 Sinatra 4.0.0 Sqlite3 2.0.1 a.rb a.rb a.rb a.rb developer user Ruby 3.3.1 Sinatra 4.0.0 Sqlite3 2.0.1 14 Ruby 3.0.0 Sinatra 3.2.0 Sqlite3 Not installed

Slide 15

Slide 15 text

Any existing tools? 15

Slide 16

Slide 16 text

Any existing tools? ● ocra(ocran) ● exerb ● ruby2exe ● ruby-packer 16

Slide 17

Slide 17 text

🤔 Existing tools look good and useful 17

Slide 18

Slide 18 text

Existing tools look good and useful, but ● No support for 3.0+ ● Patches Ruby ● Windows only ● Write temporary files 18

Slide 19

Slide 19 text

Time to make new one! 😄 19

Slide 20

Slide 20 text

Done 🔧 20

Slide 21

Slide 21 text

Kompo 📦 21

Slide 22

Slide 22 text

Kompo ● 梱包(こんぽう) ○ Means packaging, packing ● A word play on “compo” ○ compose ○ component ○ composite ○ etc… 22 https://github.com/ahogappa0613/kompo

Slide 23

Slide 23 text

Demo One binary with Sinatra and Sqlite3 23

Slide 24

Slide 24 text

Features 24

Slide 25

Slide 25 text

Features ● Monkey patch only, not patching Ruby ● No write temporary files ● Supports Gemfile ● Reads local file as a fallback when failing to read from vfs 25

Slide 26

Slide 26 text

Demo Reads local file as a fallback when failing to read from vfs 26

Slide 27

Slide 27 text

How kompo works 27

Slide 28

Slide 28 text

How kompo works ● Ruby scripts are embedded ● Patch methods that read Ruby scripts ○ Kernel#require ○ Kernel#require_relative ○ Kernel#load ○ Kernel#autoload, Module#autoload ● Native extensions are statically linked 28

Slide 29

Slide 29 text

29 libruby-static.a

Slide 30

Slide 30 text

30 libruby-static.a native extensions

Slide 31

Slide 31 text

main.c 31 libruby-static.a native extensions void Init_gems(void) { ruby_init_ext( "puma/puma_http11.so", Init_puma_http11 ); ruby_init_ext( "sqlite3/sqlite3_native.so", Init_sqlite3_native ); } int main(int argc, char **argv) { ruby_sysinit(&argc, &argv); ruby_init(); Init_gems(); Init_kompo_fs(); … return ruby_run_node(node); }

Slide 32

Slide 32 text

main.c 32 kompofs.o +patches libruby-static.a native extensions

Slide 33

Slide 33 text

33 class Kompo def get_file(abs_path) # return the ruby script from kompofs(.o) end … end main.c libruby-static.a native extensions kompofs.o +patches libkompo.a

Slide 34

Slide 34 text

main.c libkompo.a 34 libruby-static.a kompofs.o +patches native extensions one binary compile and link libruby-static.a

Slide 35

Slide 35 text

Patches 35

Slide 36

Slide 36 text

Patches libruby-static.a main.rb b.rb d.rb a.rb Kernel#require Kernel#require_relative c.so 36

Slide 37

Slide 37 text

Patches libruby-static.a main.rb b.rb d.rb a.rb Kernel#require Kernel#require_relative c.so $ cd /workspace/sample $ kompo a.rb b.rb 37

Slide 38

Slide 38 text

Patches libruby-static.a main.rb b.rb Kernel#require Kernel#require_relative d.rb a.rb c.so 38 kompofs.o

Slide 39

Slide 39 text

Patches Ruby main.rb b.rb Kernel#require Kernel#require_relative c.o d.rb a.rb one binary libkompo.a 39 main.c kompofs.o

Slide 40

Slide 40 text

Patches Ruby main.rb b.rb 🪡Kernel#require 🪡Kernel#require_relative c.o d.rb a.rb one binary libkompo.a 40 module Kernel def require(path) eval(Kompo.get_file(path)) rescue => LoadError original_require(path) main.c kompofs.o

Slide 41

Slide 41 text

Patches Ruby main.rb b.rb 🪡Kernel#require 🪡Kernel#require_relative c.o d.rb a.rb one binary require ‘a’ require_relative ‘./b’ require ‘c’ require ‘d’ libkompo.a 41 module Kernel def require(path) eval(Kompo.get_file(path)) rescue => LoadError original_require(path) main.c kompofs.o

Slide 42

Slide 42 text

Patches Ruby main.rb b.rb 🪡Kernel#require 🪡Kernel#require_relative c.o d.rb a.rb one binary libkompo.a 42 module Kernel def require(path) eval(Kompo.get_file(path)) rescue => LoadError original_require(path) kompofs.o require ‘a’ require_relative ‘./b’ require ‘c’ require ‘d’ main.c

Slide 43

Slide 43 text

Future Works 43

Slide 44

Slide 44 text

Future Works ● Support ways for users to access kompofs freely ● Cross-compilation ● File compression 44

Slide 45

Slide 45 text

Let’s kompo Ruby! 45

Slide 46

Slide 46 text

Thank you!! 46