From 647b2eb7fa2b4b7346df11ae809991d1cd802116 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 6 Dec 2023 14:33:47 +0000 Subject: [PATCH 01/44] Initial RubyGems/Bundler support --- .gitignore | 3 + Cargo.lock | 2588 +++++++++++++++++ Cargo.toml | 7 + Gemfile | 2 + Rakefile | 5 + Steepfile | 1 + exe/rbwasm | 7 + ext/c10zrt/c10zrt.c | 229 ++ ext/c10zrt/c10zrt_core.s | 13 + ext/c10zrt/depend | 9 + ext/c10zrt/extconf.rb | 5 + ext/extinit.c.erb | 5 +- ext/ruby_wasm/Cargo.toml | 19 + ext/ruby_wasm/extconf.rb | 6 + ext/ruby_wasm/src/lib.rs | 142 + ext/ruby_wasm/src/types.rs | 42 + lib/ruby_wasm.rb | 33 + lib/ruby_wasm/build.rb | 260 +- lib/ruby_wasm/build/downloader.rb | 12 +- lib/ruby_wasm/build/executor.rb | 187 ++ lib/ruby_wasm/build/product/crossruby.rb | 46 +- lib/ruby_wasm/build/product/openssl.rb | 1 - lib/ruby_wasm/cli.rb | 107 + lib/ruby_wasm/packager.rb | 72 + lib/ruby_wasm/packager/core.rb | 89 + lib/ruby_wasm/packager/file_system.rb | 159 + lib/ruby_wasm/rake_task.rb | 82 +- lib/ruby_wasm/util.rb | 15 + packages/crates/wasm-inject/Cargo.toml | 15 + .../crates/wasm-inject/src/bin/wasm-inject.rs | 29 + packages/crates/wasm-inject/src/lib.rs | 362 +++ .../wasm-inject/tests/snapshots/export.wat | 281 ++ .../wasm-inject/tests/snapshots/import.wat | 251 ++ packages/crates/wasm-inject/tests/tests.rs | 116 + packages/crates/wit-bindgen-ruby/Cargo.toml | 10 + packages/crates/wit-bindgen-ruby/src/lib.rs | 18 + .../gems/js/ext}/js/bindgen/.clang-format | 0 .../gems/js/ext}/js/bindgen/rb-js-abi-host.c | 0 .../gems/js/ext}/js/bindgen/rb-js-abi-host.h | 0 .../js/ext}/js/bindgen/rb-js-abi-host.wit | 0 {ext => packages/gems/js/ext}/js/depend | 0 {ext => packages/gems/js/ext}/js/extconf.rb | 0 {ext => packages/gems/js/ext}/js/js-core.c | 0 .../gems/js/ext}/witapi/bindgen/.clang-format | 0 .../js/ext}/witapi/bindgen/rb-abi-guest.c | 0 .../js/ext}/witapi/bindgen/rb-abi-guest.h | 0 .../js/ext}/witapi/bindgen/rb-abi-guest.wit | 0 {ext => packages/gems/js/ext}/witapi/depend | 0 .../gems/js/ext}/witapi/extconf.rb | 0 .../gems/js/ext}/witapi/witapi-core.c | 0 packages/gems/js/js.gemspec | 24 + {ext => packages/gems}/js/lib/js.rb | 0 {ext => packages/gems}/js/lib/js/array.rb | 0 {ext => packages/gems}/js/lib/js/hash.rb | 0 {ext => packages/gems}/js/lib/js/nil_class.rb | 0 .../gems}/js/lib/js/require_remote.rb | 0 .../js/lib/js/require_remote/evaluator.rb | 0 .../js/lib/js/require_remote/url_resolver.rb | 0 packages/npm-packages/ruby-wasm-wasi/Gemfile | 6 + .../npm-packages/ruby-wasm-wasi/Gemfile.lock | 24 + ruby_wasm.gemspec | 3 +- sig/ruby_wasm/cli.rbs | 35 + tasks/gem.rake | 12 + 63 files changed, 5050 insertions(+), 282 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100755 exe/rbwasm create mode 100644 ext/c10zrt/c10zrt.c create mode 100644 ext/c10zrt/c10zrt_core.s create mode 100644 ext/c10zrt/depend create mode 100644 ext/c10zrt/extconf.rb create mode 100644 ext/ruby_wasm/Cargo.toml create mode 100644 ext/ruby_wasm/extconf.rb create mode 100644 ext/ruby_wasm/src/lib.rs create mode 100644 ext/ruby_wasm/src/types.rs create mode 100644 lib/ruby_wasm.rb create mode 100644 lib/ruby_wasm/build/executor.rb create mode 100644 lib/ruby_wasm/cli.rb create mode 100644 lib/ruby_wasm/packager.rb create mode 100644 lib/ruby_wasm/packager/core.rb create mode 100644 lib/ruby_wasm/packager/file_system.rb create mode 100644 lib/ruby_wasm/util.rb create mode 100644 packages/crates/wasm-inject/Cargo.toml create mode 100644 packages/crates/wasm-inject/src/bin/wasm-inject.rs create mode 100644 packages/crates/wasm-inject/src/lib.rs create mode 100644 packages/crates/wasm-inject/tests/snapshots/export.wat create mode 100644 packages/crates/wasm-inject/tests/snapshots/import.wat create mode 100644 packages/crates/wasm-inject/tests/tests.rs create mode 100644 packages/crates/wit-bindgen-ruby/Cargo.toml create mode 100644 packages/crates/wit-bindgen-ruby/src/lib.rs rename {ext => packages/gems/js/ext}/js/bindgen/.clang-format (100%) rename {ext => packages/gems/js/ext}/js/bindgen/rb-js-abi-host.c (100%) rename {ext => packages/gems/js/ext}/js/bindgen/rb-js-abi-host.h (100%) rename {ext => packages/gems/js/ext}/js/bindgen/rb-js-abi-host.wit (100%) rename {ext => packages/gems/js/ext}/js/depend (100%) rename {ext => packages/gems/js/ext}/js/extconf.rb (100%) rename {ext => packages/gems/js/ext}/js/js-core.c (100%) rename {ext => packages/gems/js/ext}/witapi/bindgen/.clang-format (100%) rename {ext => packages/gems/js/ext}/witapi/bindgen/rb-abi-guest.c (100%) rename {ext => packages/gems/js/ext}/witapi/bindgen/rb-abi-guest.h (100%) rename {ext => packages/gems/js/ext}/witapi/bindgen/rb-abi-guest.wit (100%) rename {ext => packages/gems/js/ext}/witapi/depend (100%) rename {ext => packages/gems/js/ext}/witapi/extconf.rb (100%) rename {ext => packages/gems/js/ext}/witapi/witapi-core.c (100%) create mode 100644 packages/gems/js/js.gemspec rename {ext => packages/gems}/js/lib/js.rb (100%) rename {ext => packages/gems}/js/lib/js/array.rb (100%) rename {ext => packages/gems}/js/lib/js/hash.rb (100%) rename {ext => packages/gems}/js/lib/js/nil_class.rb (100%) rename {ext => packages/gems}/js/lib/js/require_remote.rb (100%) rename {ext => packages/gems}/js/lib/js/require_remote/evaluator.rb (100%) rename {ext => packages/gems}/js/lib/js/require_remote/url_resolver.rb (100%) create mode 100644 packages/npm-packages/ruby-wasm-wasi/Gemfile create mode 100644 packages/npm-packages/ruby-wasm-wasi/Gemfile.lock create mode 100644 sig/ruby_wasm/cli.rbs create mode 100644 tasks/gem.rake diff --git a/.gitignore b/.gitignore index faf9b126b6..a342a45097 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,10 @@ dist /build /rubies /release +/tmp +/target /Gemfile.lock +/lib/ruby_wasm/ruby_wasm.so .cache compile_commands.json diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..077281cc5a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2588 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.3", +] + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "ambient-authority" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049" + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "async-trait" +version = "0.1.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.69.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.43", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cap-fs-ext" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc48200a1a0fa6fba138b1802ad7def18ec1cdd92f7b2a04e21f1bd887f7b9" +dependencies = [ + "cap-primitives 1.0.15", + "cap-std 1.0.15", + "io-lifetimes 1.0.11", + "windows-sys 0.48.0", +] + +[[package]] +name = "cap-primitives" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8fca3e81fae1d91a36e9784ca22a39ef623702b5f7904d89dc31f10184a178" +dependencies = [ + "ambient-authority 0.0.1", + "errno 0.2.8", + "fs-set-times 0.15.0", + "io-extras 0.13.2", + "io-lifetimes 0.5.3", + "ipnet", + "maybe-owned", + "rustix 0.33.7", + "winapi", + "winapi-util", + "winx 0.31.0", +] + +[[package]] +name = "cap-primitives" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b6df5b295dca8d56f35560be8c391d59f0420f72e546997154e24e765e6451" +dependencies = [ + "ambient-authority 0.0.2", + "fs-set-times 0.19.2", + "io-extras 0.17.4", + "io-lifetimes 1.0.11", + "ipnet", + "maybe-owned", + "rustix 0.37.27", + "windows-sys 0.48.0", + "winx 0.35.1", +] + +[[package]] +name = "cap-rand" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25555efacb0b5244cf1d35833d55d21abc916fff0eaad254b8e2453ea9b8ab" +dependencies = [ + "ambient-authority 0.0.2", + "rand", +] + +[[package]] +name = "cap-std" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2247568946095c7765ad2b441a56caffc08027734c634a6d5edda648f04e32eb" +dependencies = [ + "cap-primitives 0.24.4", + "io-extras 0.13.2", + "io-lifetimes 0.5.3", + "ipnet", + "rustix 0.33.7", +] + +[[package]] +name = "cap-std" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3373a62accd150b4fcba056d4c5f3b552127f0ec86d3c8c102d60b978174a012" +dependencies = [ + "cap-primitives 1.0.15", + "io-extras 0.17.4", + "io-lifetimes 1.0.11", + "rustix 0.37.27", +] + +[[package]] +name = "cap-time-ext" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e95002993b7baee6b66c8950470e59e5226a23b3af39fc59c47fe416dd39821a" +dependencies = [ + "cap-primitives 1.0.15", + "once_cell", + "rustix 0.37.27", + "winx 0.35.1", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "182b82f78049f54d3aee5a19870d356ef754226665a695ce2fcdd5d55379718e" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c027bf04ecae5b048d3554deb888061bc26f426afff47bf06d6ac933dce0a6" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.27.3", + "hashbrown 0.13.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649f70038235e4c81dba5680d7e5ae83e1081f567232425ab98b55b03afd9904" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d1c5ee2611c6a0bdc8d42d5d3dc5ce8bf53a8040561e26e88b9b21f966417" + +[[package]] +name = "cranelift-control" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da66a68b1f48da863d1d53209b8ddb1a6236411d2d72a280ffa8c2f734f7219e" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd897422dbb66621fa558f4d9209875530c53e3c8f4b13b2849fbb667c431a6" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05db883114c98cfcd6959f72278d2fec42e01ea6a6982cfe4f20e88eebe86653" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84559de86e2564152c87e299c8b2559f9107e9c6d274b24ebeb04fb0a5f4abf8" + +[[package]] +name = "cranelift-native" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f40b57f187f0fe1ffaf281df4adba2b4bc623a0f6651954da9f3c184be72761" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.96.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3eab6084cc789b9dd0b1316241efeb2968199fee709f4bb4fe0fb0923bb468b" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser 0.103.0", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix 0.38.28", + "windows-sys 0.52.0", +] + +[[package]] +name = "file-per-thread-logger" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-set-times" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df62ee66ee2d532ea8d567b5a3f0d03ecd64636b98bad5be1e93dcc918b92aa" +dependencies = [ + "io-lifetimes 0.5.3", + "rustix 0.33.7", + "winapi", +] + +[[package]] +name = "fs-set-times" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d167b646a876ba8fda6b50ac645cfd96242553cbaf0ca4fccaa39afcbf0801f" +dependencies = [ + "io-lifetimes 1.0.11", + "rustix 0.38.28", + "windows-sys 0.48.0", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fxprof-processed-profile" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +dependencies = [ + "bitflags 2.4.1", + "debugid", + "fxhash", + "serde", + "serde_json", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "io-extras" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c937cc9891c12eaa8c63ad347e4a288364b1328b924886970b47a14ab8f8f8" +dependencies = [ + "io-lifetimes 0.5.3", + "winapi", +] + +[[package]] +name = "io-extras" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde93d48f0d9277f977a333eca8313695ddd5301dc96f7e02aeddcb0dd99096f" +dependencies = [ + "io-lifetimes 1.0.11", + "windows-sys 0.48.0", +] + +[[package]] +name = "io-lifetimes" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec58677acfea8a15352d42fc87d11d63596ade9239e0a7c9352914417515dbe6" + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.3", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +dependencies = [ + "hermit-abi 0.3.3", + "rustix 0.38.28", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "ittapi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a5c0b993601cad796222ea076565c5d9f337d35592f8622c753724f06d7271" +dependencies = [ + "anyhow", + "ittapi-sys", + "log", +] + +[[package]] +name = "ittapi-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7b5e473765060536a660eed127f758cf1a810c73e49063264959c60d1727d9" +dependencies = [ + "cc", +] + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "magnus" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4778544796676e8428e9c622460ebf284bea52d8b10db3aeb449d8b5e61b3a13" +dependencies = [ + "magnus-macros", + "rb-sys", + "rb-sys-env", + "seq-macro", +] + +[[package]] +name = "magnus-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5968c820e2960565f647819f5928a42d6e874551cab9d88d75e3e0660d7f71e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix 0.38.28", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rb-sys" +version = "0.9.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b780e6858b0b0eced1d55d0f097c024b77a37b41f83bd35341130f78e37c51" +dependencies = [ + "rb-sys-build", +] + +[[package]] +name = "rb-sys-build" +version = "0.9.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44957a3bc513dad1b0f20bdd0ee3b82e729a59da44086a6b40d8bc71958a6db8" +dependencies = [ + "bindgen", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "shell-words", + "syn 2.0.43", +] + +[[package]] +name = "rb-sys-env" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35802679f07360454b418a5d1735c89716bde01d35b1560fc953c1415a0b3bb" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a52e724646c6c0800fc456ec43b4165d2f91fba88ceaca06d9e0b400023478" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ruby_wasm" +version = "0.0.0" +dependencies = [ + "magnus", + "wasi-cap-std-sync", + "wasi-vfs-cli", + "wasm-inject", + "wasmtime-wasi", + "wizer 3.0.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.33.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938a344304321a9da4973b9ff4f9f8db9caf4597dfd9dda6a60b523340a0fff0" +dependencies = [ + "bitflags 1.3.2", + "errno 0.2.8", + "io-lifetimes 0.5.3", + "itoa", + "libc", + "linux-raw-sys 0.0.42", + "once_cell", + "winapi", +] + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno 0.3.8", + "io-lifetimes 1.0.11", + "itoa", + "libc", + "linux-raw-sys 0.3.8", + "once_cell", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno 0.3.8", + "libc", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "seq-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-interface" +version = "0.25.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10081a99cbecbc363d381b9503563785f0b02735fccbb0d4c1a2cb3d39f7e7fe" +dependencies = [ + "bitflags 2.4.1", + "cap-fs-ext", + "cap-std 1.0.15", + "fd-lock", + "io-lifetimes 2.0.3", + "rustix 0.38.28", + "windows-sys 0.48.0", + "winx 0.36.3", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walrus" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7" +dependencies = [ + "anyhow", + "gimli 0.26.2", + "id-arena", + "leb128", + "log", + "walrus-macro", + "wasm-encoder 0.29.0", + "wasmparser 0.80.2", +] + +[[package]] +name = "walrus-macro" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-cap-std-sync" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d29c5da3b5cfc9212a7fa824224875cb67fb89d2a8392db655e4c59b8ab2ae7" +dependencies = [ + "anyhow", + "async-trait", + "cap-fs-ext", + "cap-rand", + "cap-std 1.0.15", + "cap-time-ext", + "fs-set-times 0.19.2", + "io-extras 0.17.4", + "io-lifetimes 1.0.11", + "is-terminal", + "once_cell", + "rustix 0.37.27", + "system-interface", + "tracing", + "wasi-common", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasi-common" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bd905dcec1448664bf63d42d291cbae0feeea3ad41631817b8819e096d76bd" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "cap-rand", + "cap-std 1.0.15", + "io-extras 0.17.4", + "log", + "rustix 0.37.27", + "thiserror", + "tracing", + "wasmtime", + "wiggle", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasi-vfs-cli" +version = "0.4.0" +dependencies = [ + "anyhow", + "structopt", + "wasmparser 0.106.0", + "wizer 3.0.1", +] + +[[package]] +name = "wasm-encoder" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c94f464d50e31da425794a02da1a82d4b96a657dcb152a6664e8aa915be517" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-inject" +version = "0.1.0" +dependencies = [ + "anyhow", + "walrus", + "wasmparser 0.118.1", + "wasmprinter", +] + +[[package]] +name = "wasmparser" +version = "0.80.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b" + +[[package]] +name = "wasmparser" +version = "0.103.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c437373cac5ea84f1113d648d51f71751ffbe3d90c00ae67618cf20d0b5ee7b" +dependencies = [ + "indexmap 1.9.3", + "url", +] + +[[package]] +name = "wasmparser" +version = "0.106.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d014e33793cab91655fa6349b0bc974984de106b2e0f6b0dfe6f6594b260624d" +dependencies = [ + "indexmap 1.9.3", + "url", +] + +[[package]] +name = "wasmparser" +version = "0.118.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" +dependencies = [ + "indexmap 2.1.0", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.2.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d027eb8294904fc715ac0870cebe6b0271e96b90605ee21511e7565c4ce568c" +dependencies = [ + "anyhow", + "wasmparser 0.118.1", +] + +[[package]] +name = "wasmtime" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634357e8668774b24c80b210552f3f194e2342a065d6d83845ba22c5817d0770" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bumpalo", + "cfg-if", + "fxprof-processed-profile", + "indexmap 1.9.3", + "libc", + "log", + "object", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "serde_json", + "target-lexicon", + "wasmparser 0.103.0", + "wasmtime-cache", + "wasmtime-component-macro", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-runtime", + "wat", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d33c73c24ce79b0483a3b091a9acf88871f4490b88998e8974b22236264d304c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107809b2d9f5b2fd3ddbaddb3bb92ff8048b62f4030debf1408119ffd38c6cb" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix 0.37.27", + "serde", + "sha2", + "toml", + "windows-sys 0.48.0", + "zstd", +] + +[[package]] +name = "wasmtime-component-macro" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ba489850d9c91c6c5b9e1696ee89e7a69d9796236a005f7e9131b6746e13b6" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasmtime-component-util", + "wasmtime-wit-bindgen", + "wit-parser 0.7.1", +] + +[[package]] +name = "wasmtime-component-util" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fa88f9e77d80f828c9d684741a9da649366c6d1cceb814755dd9cab7112d1d1" + +[[package]] +name = "wasmtime-cranelift" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5800616a28ed6bd5e8b99ea45646c956d798ae030494ac0689bc3e45d3b689c1" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.27.3", + "log", + "object", + "target-lexicon", + "thiserror", + "wasmparser 0.103.0", + "wasmtime-cranelift-shared", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e4030b959ac5c5d6ee500078977e813f8768fa2b92fc12be01856cd0c76c55" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli 0.27.3", + "object", + "target-lexicon", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ec815d01a8d38aceb7ed4678f9ba551ae6b8a568a63810ac3ad9293b0fd01c8" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.27.3", + "indexmap 1.9.3", + "log", + "object", + "serde", + "target-lexicon", + "thiserror", + "wasmparser 0.103.0", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c5127908fdf720614891ec741c13dd70c844e102caa393e2faca1ee68e9bfb" +dependencies = [ + "cc", + "cfg-if", + "rustix 0.37.27", + "wasmtime-asm-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2712eafe829778b426cad0e1769fef944898923dd29f0039e34e0d53ba72b234" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.27.3", + "ittapi", + "log", + "object", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fb78eacf4a6e47260d8ef8cc81ea8ddb91397b2e848b3fb01567adebfe89b5" +dependencies = [ + "object", + "once_cell", + "rustix 0.37.27", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1364900b05f7d6008516121e8e62767ddb3e176bdf4c84dfa85da1734aeab79" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a16ffe4de9ac9669175c0ea5c6c51ffc596dfb49320aaa6f6c57eff58cef069" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "rand", + "rustix 0.37.27", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit-debug", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-types" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19961c9a3b04d5e766875a5c467f6f5d693f508b3e81f8dc4a1444aa94f041c9" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser 0.103.0", +] + +[[package]] +name = "wasmtime-wasi" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21080ff62878f1d7c53d9571053dbe96552c0f982f9f29eac65ea89974fabfd7" +dependencies = [ + "anyhow", + "libc", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wiggle", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421f0d16cc5c612b35ae53a0be3d3124c72296f18e5be3468263c745d56d37ab" +dependencies = [ + "anyhow", + "heck 0.4.1", + "wit-parser 0.7.1", +] + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "69.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ee37317321afde358e4d7593745942c48d6d17e0e6e943704de9bbee121e7a" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.38.1", +] + +[[package]] +name = "wat" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb338ee8dee4d4cd05e6426683f21c5087dc7cfc8903e839ccf48d43332da3c" +dependencies = [ + "wast 69.0.1", +] + +[[package]] +name = "wiggle" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b34e40b7b17a920d03449ca78b0319984379eed01a9a11c1def9c3d3832d85a" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 1.3.2", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eefda132eaa84fe5f15d23a55a912f8417385aee65d0141d78a3b65e46201ed" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro2", + "quote", + "shellexpand", + "syn 1.0.109", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca1a344a0ba781e2a94b27be5bb78f23e43d52336bd663b810d49d7189ad334" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wiggle-generate", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winx" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d5973cb8cd94a77d03ad7e23bbe14889cb29805da1cec0e4aff75e21aebded" +dependencies = [ + "bitflags 1.3.2", + "io-lifetimes 0.5.3", + "winapi", +] + +[[package]] +name = "winx" +version = "0.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c52a121f0fbf9320d5f2a9a5d82f6cb7557eda5e8b47fc3e7f359ec866ae960" +dependencies = [ + "bitflags 1.3.2", + "io-lifetimes 1.0.11", + "windows-sys 0.48.0", +] + +[[package]] +name = "winx" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +dependencies = [ + "bitflags 2.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "wit-bindgen-ruby" +version = "0.1.0" +dependencies = [ + "wit-parser 0.13.0", +] + +[[package]] +name = "wit-parser" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca2581061573ef6d1754983d7a9b3ed5871ef859d52708ea9a0f5af32919172" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 1.9.3", + "log", + "pulldown-cmark", + "unicode-xid", + "url", +] + +[[package]] +name = "wit-parser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.1.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast 35.0.2", +] + +[[package]] +name = "wizer" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbf85c6302a99e5c9d15655abd804c0e204a278fdb62c1e53a37ba4f3550b8b" +dependencies = [ + "anyhow", + "cap-std 0.24.4", + "log", + "rayon", + "wasi-cap-std-sync", + "wasm-encoder 0.28.0", + "wasmparser 0.106.0", + "wasmtime", + "wasmtime-wasi", +] + +[[package]] +name = "wizer" +version = "3.0.1" +source = "git+https://github.com/bytecodealliance/wizer.git?rev=v3.0.1#a0125eb591a77bbf61af15d4f8675243a5651229" +dependencies = [ + "anyhow", + "cap-std 0.24.4", + "log", + "rayon", + "wasi-cap-std-sync", + "wasm-encoder 0.28.0", + "wasmparser 0.106.0", + "wasmtime", + "wasmtime-wasi", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..6f04dcd9c7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +# This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is +# a Rust project. Your extensions dependencies should be added to the Cargo.toml +# in the ext/ directory. + +[workspace] +members = ["./ext/ruby_wasm", "packages/crates/wasm-inject", "packages/crates/wit-bindgen-ruby"] +resolver = "2" diff --git a/Gemfile b/Gemfile index bb52083b48..bae4d68274 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,8 @@ source "https://rubygems.org" group :development do gem "rake" + gem "rake-compiler" + gem "rb_sys", "~> 0.9.63" gem "syntax_tree", "~> 3.5" gem "webrick" gem "steep" diff --git a/Rakefile b/Rakefile index fb1655fdc6..195f5c113c 100644 --- a/Rakefile +++ b/Rakefile @@ -51,6 +51,11 @@ BUILD_PROFILES = { default_exts: "", user_exts: [] }, + "minimal-component-debug" => { + debug: true, + default_exts: "", + user_exts: %w[c10zrt] + }, "full" => { debug: false, default_exts: FULL_EXTS, diff --git a/Steepfile b/Steepfile index 98977dcd8d..5f3a0304af 100644 --- a/Steepfile +++ b/Steepfile @@ -14,6 +14,7 @@ target :lib do library "uri" library "shellwords" library "io-console" + library "optparse" configure_code_diagnostics(D::Ruby.default) end diff --git a/exe/rbwasm b/exe/rbwasm new file mode 100755 index 0000000000..484ebea4dc --- /dev/null +++ b/exe/rbwasm @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$LOAD_PATH << File.join(__dir__, "../lib") +require "ruby_wasm" +require "ruby_wasm/cli" + +RubyWasm::CLI.new(stdout: $stdout, stderr: $stderr).run(ARGV) diff --git a/ext/c10zrt/c10zrt.c b/ext/c10zrt/c10zrt.c new file mode 100644 index 0000000000..8cefb4b461 --- /dev/null +++ b/ext/c10zrt/c10zrt.c @@ -0,0 +1,229 @@ + +#include "ruby.h" +#include + +extern void __wasm_call_ctors(void); +extern void __wasm_call_dtors(void); + +static void *initialized_iseq; + +int rb_wasm_rt_start(int(main)(int argc, char **argv), int argc, char **argv); +static int wizer_initialize_internal(int argc, char **argv) { + ruby_sysinit(&argc, &argv); + ruby_init(); + initialized_iseq = ruby_options(argc, argv); + return 0; +} + +#include + +void debug_dump_fs_tree(const char *path) { + // Dump the file system tree for debugging purposes like `tree` command using + // libc's dirent.h + + DIR *dir = opendir(path); + if (dir == NULL) { + printf("Failed to open directory: %s\n", path); + return; + } + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + printf("%s/%s\n", path, entry->d_name); + char *subpath = malloc(strlen(path) + strlen(entry->d_name) + 2); + sprintf(subpath, "%s/%s", path, entry->d_name); + debug_dump_fs_tree(subpath); + free(subpath); + } else { + printf("%s/%s\n", path, entry->d_name); + } + } +} + +__attribute__((export_name("ruby.wizer.initialize"))) void +__ruby_wizer_initialize() { + __wasm_call_ctors(); + + debug_dump_fs_tree("/exe"); + int err; + size_t argc; + size_t argv_buf_size; + err = __wasi_args_sizes_get(&argc, &argv_buf_size); + if (err != 0) { + __wasi_proc_exit(err); + } + char *argv_buf = malloc(argv_buf_size); + if (argv_buf == NULL) { + __wasi_proc_exit(__WASI_ERRNO_NOMEM); + } + char **argv = malloc(argc * sizeof(char *)); + if (argv == NULL) { + __wasi_proc_exit(__WASI_ERRNO_NOMEM); + } + err = __wasi_args_get((uint8_t **)argv, (uint8_t *)argv_buf); + if (err != 0) { + __wasi_proc_exit(err); + } + rb_wasm_rt_start(wizer_initialize_internal, argc, argv); +} + +static int wizer_run_internal(int argc, char **argv) { + return ruby_run_node(initialized_iseq); +} + +__attribute__((export_name("ruby.wizer.resume"))) void __ruby_wizer_resume() { + int ret = rb_wasm_rt_start(wizer_run_internal, 0, NULL); + __wasm_call_dtors(); + if (ret != 0) { + __wasi_proc_exit(ret); + } +} + +VALUE rb_mWaddle; + +typedef struct { + const char *module_name; + const char *name; + void *func; +} import_func_entry; + +static size_t import_funcs_size = 0; +static import_func_entry *import_funcs; + +__attribute__((export_name("ruby.c10zrt.add_import_func"))) void +ruby_c10zrt_add_import_func(const char *module_name, const char *name, + void *func) { + static size_t import_funcs_capacity = 0; + if (import_funcs_capacity == 0) { + import_funcs_capacity = 16; + import_funcs = malloc(import_funcs_capacity * sizeof(import_func_entry)); + } else if (import_funcs_size == import_funcs_capacity) { + import_funcs_capacity *= 2; + import_funcs = realloc(import_funcs, + import_funcs_capacity * sizeof(import_func_entry)); + } + import_funcs[import_funcs_size].module_name = module_name; + import_funcs[import_funcs_size].name = name; + import_funcs[import_funcs_size].func = func; + import_funcs_size++; +} + +static import_func_entry *find_import_func(const char *module_name, + const char *name) { + for (size_t i = 0; i < import_funcs_size; i++) { + if (strcmp(import_funcs[i].module_name, module_name) == 0 && + strcmp(import_funcs[i].name, name) == 0) { + return &import_funcs[i]; + } + } + return NULL; +} + +__attribute__((export_name("ruby.c10zrt.init_context"))) VALUE +ruby_c10zrt_init_context(void) { + return rb_ary_new(); +} + +__attribute__((export_name("ruby.c10zrt.call_export_func"))) void +ruby_c10zrt_call_export_func(const char *name, VALUE ctx) { + VALUE func = rb_funcall(rb_mWaddle, rb_intern("export_func"), 1, + rb_str_new_cstr(name)); + rb_funcall(func, rb_intern("call"), 1, ctx); +} + +__attribute__((export_name("ruby.c10zrt.push_i32"))) void +ruby_c10zrt_push_i32(int32_t value, VALUE ctx) { + rb_ary_push(ctx, INT2NUM(value)); +} + +__attribute__((export_name("ruby.c10zrt.push_i64"))) void +ruby_c10zrt_push_i64(int64_t value, VALUE ctx) { + rb_ary_push(ctx, LL2NUM(value)); +} + +__attribute__((export_name("ruby.c10zrt.push_f32"))) void +ruby_c10zrt_push_f32(float value, VALUE ctx) { + rb_ary_push(ctx, DBL2NUM(value)); +} + +__attribute__((export_name("ruby.c10zrt.push_f64"))) void +ruby_c10zrt_push_f64(double value, VALUE ctx) { + rb_ary_push(ctx, DBL2NUM(value)); +} + +__attribute__((export_name("ruby.c10zrt.pop_i32"))) int32_t +ruby_c10zrt_pop_i32(VALUE ctx) { + return NUM2INT(rb_ary_pop(ctx)); +} + +__attribute__((export_name("ruby.c10zrt.pop_i64"))) int64_t +ruby_c10zrt_pop_i64(VALUE ctx) { + return NUM2LL(rb_ary_pop(ctx)); +} + +__attribute__((export_name("ruby.c10zrt.pop_f32"))) float +ruby_c10zrt_pop_f32(VALUE ctx) { + return NUM2DBL(rb_ary_pop(ctx)); +} + +__attribute__((export_name("ruby.c10zrt.pop_f64"))) double +ruby_c10zrt_pop_f64(VALUE ctx) { + return NUM2DBL(rb_ary_pop(ctx)); +} + +__attribute__((export_name("cabi_realloc"))) void *cabi_realloc(void *ptr, + size_t size) { + return realloc(ptr, size); +} + +__attribute__((export_name("cabi_free"))) void cabi_free(void *ptr) { + free(ptr); +} + +extern void ruby_c10zrt_invoke_import(void *func, VALUE *argv); + +static VALUE rb_call_wasm_import(int argc, VALUE *argv, VALUE self) { + // Exctract (module_name, name, *args) from argv + if (argc < 2) { + rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2+)", + argc); + } + VALUE module_name = argv[0]; + VALUE name = argv[1]; + VALUE *args = argv + 2; + argc -= 2; + + if (TYPE(module_name) != T_STRING) { + rb_raise(rb_eTypeError, "wrong argument type %s (expected String)", + rb_obj_classname(module_name)); + } + + if (TYPE(name) != T_STRING) { + rb_raise(rb_eTypeError, "wrong argument type %s (expected String)", + rb_obj_classname(name)); + } + + const char *module_name_cstr = RSTRING_PTR(module_name); + const char *name_cstr = RSTRING_PTR(name); + + import_func_entry *entry = find_import_func(module_name_cstr, name_cstr); + if (entry == NULL) { + rb_raise(rb_eRuntimeError, "import function %s.%s is not registered", + module_name_cstr, name_cstr); + } + + ruby_c10zrt_invoke_import(entry->func, args); + return Qnil; +} + +void Init_c10zrt() { + rb_mWaddle = rb_define_module("Waddle"); + rb_define_module_function(rb_mWaddle, "call_wasm_import", rb_call_wasm_import, + -1); + + // 0 is reserved for import registration function + ruby_c10zrt_invoke_import(0, 0); +} diff --git a/ext/c10zrt/c10zrt_core.s b/ext/c10zrt/c10zrt_core.s new file mode 100644 index 0000000000..9c4f5c8b0c --- /dev/null +++ b/ext/c10zrt/c10zrt_core.s @@ -0,0 +1,13 @@ +.globl ruby.c10zrt.imports_table +ruby.c10zrt.imports_table: + .tabletype ruby.c10zrt.imports_table, funcref +.export_name ruby.c10zrt.imports_table, ruby.c10zrt.imports_table + +.globl ruby_c10zrt_invoke_import +.type ruby_c10zrt_invoke_import, @function +ruby_c10zrt_invoke_import: + .functype ruby_c10zrt_invoke_import (i32, i32) -> () + local.get 1 + local.get 0 + call_indirect ruby.c10zrt.imports_table, (i32) -> () + end_function diff --git a/ext/c10zrt/depend b/ext/c10zrt/depend new file mode 100644 index 0000000000..65e2c17f9c --- /dev/null +++ b/ext/c10zrt/depend @@ -0,0 +1,9 @@ +link.filelist: + echo $(foreach obj,$(OBJS),$(abspath $(obj))) > $@ + +c10zrt.a: link.filelist + +c10zrt.o: $(srcdir)/c10zrt.c +c10zrt_core.o: $(srcdir)/c10zrt_core.s +# reference-types feature is required to have multiple tables + $(CC) -mreference-types -target wasm32-unknown-unknown-wasm -c $< -o $@ diff --git a/ext/c10zrt/extconf.rb b/ext/c10zrt/extconf.rb new file mode 100644 index 0000000000..2a88bd590d --- /dev/null +++ b/ext/c10zrt/extconf.rb @@ -0,0 +1,5 @@ +require "mkmf" +$objs = %w[c10zrt.o c10zrt_core.o] +$CFLAGS += " -mreference-types" + +create_makefile("c10zrt") diff --git a/ext/extinit.c.erb b/ext/extinit.c.erb index 2d74013839..8fab120a7f 100644 --- a/ext/extinit.c.erb +++ b/ext/extinit.c.erb @@ -14,7 +14,8 @@ end exts = ARGV c_src = ERB.new(DATA.read).result -IO.popen("#{@cc} -c -xc - -o #{@o}", "w") {|f| f << c_src } +IO.popen([@cc, "-c", "-xc", "-", "-o", @o], "w") {|f| f << c_src } +exit $?.exitstatus __END__ #define init(func, name) { \ @@ -26,6 +27,6 @@ void ruby_init_ext(const char *name, void (*init)(void)); void Init_extra_exts(void) { <% exts.each do |ext| %> - init(<%= "Init_#{ext}" %>, "<%= ext %>"); + init(<%= "Init_#{File.basename ext}" %>, "<%= ext %>"); <% end %> } diff --git a/ext/ruby_wasm/Cargo.toml b/ext/ruby_wasm/Cargo.toml new file mode 100644 index 0000000000..0858e5d39f --- /dev/null +++ b/ext/ruby_wasm/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "ruby_wasm" +version = "0.0.0" +edition = "2021" +authors = ["Yuta Saito "] +license = "MIT" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +magnus = "0.6.2" +wizer = "3.0.0" +wasmtime-wasi = "9.0.4" +wasi-cap-std-sync = "9.0.4" +wasm-inject = { path = "../../packages/crates/wasm-inject" } +# wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", tag = "v0.5.0" } +wasi-vfs-cli = { path = "/home/katei/ghq/github.com/kateinoigakukun/wasi-vfs/crates/wasi-vfs-cli" } diff --git a/ext/ruby_wasm/extconf.rb b/ext/ruby_wasm/extconf.rb new file mode 100644 index 0000000000..48b4f12841 --- /dev/null +++ b/ext/ruby_wasm/extconf.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +require "mkmf" +require "rb_sys/mkmf" + +create_rust_makefile("ruby_wasm/ruby_wasm") diff --git a/ext/ruby_wasm/src/lib.rs b/ext/ruby_wasm/src/lib.rs new file mode 100644 index 0000000000..838da9ea9d --- /dev/null +++ b/ext/ruby_wasm/src/lib.rs @@ -0,0 +1,142 @@ +mod types; + +use std::path::PathBuf; + +use magnus::{ + eval, exception, function, method, + prelude::*, + value::{self, InnerValue}, + wrap, Error, ExceptionClass, RArray, RModule, Ruby, +}; +use types::ValType; +use wizer::Wizer; + +static RUBY_WASM: value::Lazy = + value::Lazy::new(|ruby| ruby.define_module("RubyWasmExt").unwrap()); + +fn preinit(core_module: Vec) -> Result, Error> { + let rbwasm_error = eval("RubyWasmExt::Error")?; + let rbwasm_error = ExceptionClass::from_value(rbwasm_error).unwrap(); + let mut wizer = Wizer::new(); + wizer + .wasm_bulk_memory(true) + .inherit_stdio(true) + .inherit_env(true) + .allow_wasi(true) + .map_err(|e| Error::new(rbwasm_error, format!("failed to create wizer: {}", e)))?; + + wizer + .run(&core_module) + .map_err(|e| Error::new(rbwasm_error, format!("failed to run wizer: {}", e))) +} + +#[wrap(class = "RubyWasmExt::WasmInject")] +struct WasmInject(std::cell::RefCell); + +impl WasmInject { + fn new(core_module: Vec) -> Result { + let inner = wasm_inject::WasmInject::new(&core_module, "ruby.c10zrt").map_err(|e| { + Error::new( + exception::standard_error(), + format!("failed to create componentizer: {}", e), + ) + })?; + Ok(Self(std::cell::RefCell::new(inner))) + } + + fn add_export_func(&self, name: String, params: RArray, results: RArray) -> Result<(), Error> { + let params = ValType::vec_from_rarray(params)?; + let results = ValType::vec_from_rarray(results)?; + self.0 + .borrow_mut() + .add_export_func(&name, ¶ms, &results) + .map_err(|e| { + Error::new( + exception::standard_error(), + format!("failed to add export func: {}", e), + ) + })?; + Ok(()) + } + + fn add_import_func( + &self, + module: String, + name: String, + params: RArray, + results: RArray, + ) -> Result<(), Error> { + let params = ValType::vec_from_rarray(params)?; + let results = ValType::vec_from_rarray(results)?; + self.0 + .borrow_mut() + .add_import_func(&module, &name, ¶ms, &results) + .map_err(|e| { + Error::new( + exception::standard_error(), + format!("failed to add import func: {}", e), + ) + })?; + Ok(()) + } + + fn run(&self) -> Result, Error> { + let wasm = self.0.borrow_mut().run().map_err(|e| { + Error::new( + exception::standard_error(), + format!("failed to run componentizer: {}", e), + ) + })?; + Ok(wasm) + } +} + +struct WasiVfsInner { + map_dirs: Vec<(PathBuf, PathBuf)>, +} + +#[wrap(class = "RubyWasmExt::WasiVfs")] +struct WasiVfs(std::cell::RefCell); + +impl WasiVfs { + fn new() -> Self { + Self(std::cell::RefCell::new(WasiVfsInner { map_dirs: vec![] })) + } + + fn map_dir(&self, guest_dir: String, host_dir: String) { + self.0.borrow_mut().map_dirs.push((guest_dir.into(), host_dir.into())); + } + + fn pack(&self, wasm_bytes: Vec) -> Result, Error> { + let output_bytes = wasi_vfs_cli::pack(&wasm_bytes, self.0.borrow().map_dirs.clone()).map_err(|e| { + Error::new( + exception::standard_error(), + format!("failed to pack wasi vfs: {}", e), + ) + })?; + Ok(output_bytes) + } +} + +#[magnus::init] +fn init(ruby: &Ruby) -> Result<(), Error> { + let module = RUBY_WASM.get_inner_with(ruby); + module.define_error("Error", exception::standard_error())?; + + module.define_singleton_method("preinitialize", function!(preinit, 1))?; + + let wasm_inject = module.define_class("WasmInject", ruby.class_object())?; + wasm_inject.define_singleton_method("new", function!(WasmInject::new, 1))?; + wasm_inject.define_method("add_export_func", method!(WasmInject::add_export_func, 3))?; + wasm_inject.define_method("add_import_func", method!(WasmInject::add_import_func, 4))?; + wasm_inject.define_method("run", method!(WasmInject::run, 0))?; + + let wasi_vfs = module.define_class("WasiVfs", ruby.class_object())?; + wasi_vfs.define_singleton_method("new", function!(WasiVfs::new, 0))?; + wasi_vfs.define_method("map_dir", method!(WasiVfs::map_dir, 2))?; + wasi_vfs.define_method("pack", method!(WasiVfs::pack, 1))?; + + let val_type = module.define_class("ValType", ruby.class_object())?; + val_type.define_singleton_method("new", function!(ValType::new, 1))?; + Ok(()) +} diff --git a/ext/ruby_wasm/src/types.rs b/ext/ruby_wasm/src/types.rs new file mode 100644 index 0000000000..92023e415f --- /dev/null +++ b/ext/ruby_wasm/src/types.rs @@ -0,0 +1,42 @@ +use magnus::{exception, wrap, Error, RArray, Symbol}; + +#[wrap(class = "ComponentizerExt::ValType")] +pub(crate) struct ValType(wasm_inject::walrus::ValType); + +impl ValType { + pub(crate) fn new(ty: Symbol) -> Result { + let ty = match ty.to_string().as_str() { + "i32" => wasm_inject::walrus::ValType::I32, + "i64" => wasm_inject::walrus::ValType::I64, + "f32" => wasm_inject::walrus::ValType::F32, + "f64" => wasm_inject::walrus::ValType::F64, + "v128" => wasm_inject::walrus::ValType::V128, + "externref" => wasm_inject::walrus::ValType::Externref, + "funcref" => wasm_inject::walrus::ValType::Funcref, + _ => { + return Err(Error::new( + exception::standard_error(), + format!("invalid type: {}", ty), + )) + } + }; + Ok(Self(ty)) + } + + pub(crate) fn vec_from_rarray(tys: RArray) -> Result, Error> { + let tys = unsafe { tys.as_slice() } + .iter() + .map(|v| -> Result { + let sym = Symbol::from_value(*v).ok_or_else(|| { + Error::new( + exception::standard_error(), + format!("invalid type: {}", v), + ) + })?; + let ty = ValType::new(sym)?; + Ok(ty.0) + }) + .collect::, _>>()?; + Ok(tys) + } +} diff --git a/lib/ruby_wasm.rb b/lib/ruby_wasm.rb new file mode 100644 index 0000000000..fb94e0478a --- /dev/null +++ b/lib/ruby_wasm.rb @@ -0,0 +1,33 @@ +require "logger" + +require_relative "ruby_wasm/version" +require_relative "ruby_wasm/util" +require_relative "ruby_wasm/build" +require_relative "ruby_wasm/packager" +require_relative "ruby_wasm/packager/file_system" +require_relative "ruby_wasm/packager/core" + +module RubyWasm + class << self + attr_accessor :log_level + + def logger + @logger ||= + begin + logger = + Logger.new( + $stderr, + level: @log_level || Logger::INFO, + progname: "rbwasm" + ) + logger.formatter = + proc { |severity, datetime, progname, msg| "#{severity}: #{msg}\n" } + logger + end + end + + def logger=(logger) + @logger = logger + end + end +end diff --git a/lib/ruby_wasm/build.rb b/lib/ruby_wasm/build.rb index 379890dfb7..00d0191dcf 100644 --- a/lib/ruby_wasm/build.rb +++ b/lib/ruby_wasm/build.rb @@ -1,190 +1,84 @@ require_relative "build/build_params" require_relative "build/product" require_relative "build/toolchain" - -module RubyWasm - # Build executor to run the actual build commands. - class BuildExecutor - attr_reader :process_count - - def initialize(verbose: false, process_count: nil) - @verbose = verbose - @github_actions_markup = ENV["ENABLE_GITHUB_ACTIONS_MARKUP"] != nil - __skip__ = - begin - require "etc" - @process_count = process_count || Etc.nprocessors - rescue LoadError - @process_count = process_count || 1 - end - end - - def system(*args, chdir: nil, env: nil) - require "open3" - - _print_command(args, env) - - # @type var kwargs: Hash[Symbol, untyped] - kwargs = {} - kwargs[:chdir] = chdir if chdir - - args = args.to_a.map(&:to_s) - # TODO: Remove __skip__ once we have open3 RBS definitions. - __skip__ = - if @verbose || !$stdout.tty? - kwargs[:exception] = true - if env - Kernel.system(env, *args, **kwargs) - else - Kernel.system(*args, **kwargs) - end - else - printer = StatusPrinter.new - block = - proc do |stdin, stdout, stderr, wait_thr| - mux = Mutex.new - out = String.new - err = String.new - readers = - [ - [stdout, :stdout, out], - [stderr, :stderr, err] - ].map do |io, name, str| - reader = - Thread.new do - while (line = io.gets) - mux.synchronize do - printer.send(name, line) - str << line - end - end - end - reader.report_on_exception = false - reader - end - - readers.each(&:join) - - [out, err, wait_thr.value] - end - begin - stdout, stderr, status = - if env - Open3.popen3(env, *args, **kwargs, &block) - else - Open3.popen3(*args, **kwargs, &block) - end - unless status.success? - $stderr.puts stdout - $stderr.puts stderr - raise "Command failed with status (#{status.exitstatus}): #{args.join(" ")}" - end - ensure - printer.done - end - end - return - rescue => e - $stdout.flush - $stderr.puts "Try running with `rake --verbose` for more complete output." - raise e - end - - def begin_section(klass, name, note) - message = "\e[1;36m==>\e[0m \e[1m#{klass}(#{name}) -- #{note}\e[0m" - if @github_actions_markup - puts "::group::#{message}" - else - puts message - end - - # Record the start time - @start_times ||= Hash.new - @start_times[[klass, name]] = Time.now - - $stdout.flush - end - - def end_section(klass, name) - took = Time.now - @start_times[[klass, name]] - puts "::endgroup::" if @github_actions_markup - puts "\e[1;36m==>\e[0m \e[1m#{klass}(#{name}) -- done in #{took.round(2)}s\e[0m" - end - - def rm_rf(list) - FileUtils.rm_rf(list) - end - - def rm_f(list) - FileUtils.rm_f(list) - end - - def cp_r(src, dest) - FileUtils.cp_r(src, dest) - end - - def mv(src, dest) - FileUtils.mv(src, dest) - end - - def mkdir_p(list) - FileUtils.mkdir_p(list) - end - - def write(path, data) - File.write(path, data) - end - - private - - def _print_command(args, env) - require "shellwords" - # Bold cyan - print "\e[1;36m ==>\e[0m " - print "env " + env.map { |k, v| "#{k}=#{v}" }.join(" ") + " " if env - print args.map { |arg| Shellwords.escape(arg.to_s) }.join(" ") + "\n" - end +require_relative "build/executor" + +class RubyWasm::Build + # Source to build from. + attr_reader :source + + # Target to build for. + attr_reader :target + + # Toolchain for the build. + # Defaults to the Toolchain.get for the target. + attr_reader :toolchain + + # LibYAML product to build. + attr_reader :libyaml + + # zlib product to build. + attr_reader :zlib + + # wasi-vfs product used by the crossruby. + attr_reader :wasi_vfs + + # BaseRuby product to build. + attr_reader :baseruby + + # CrossRuby product to build. + attr_reader :crossruby + + def initialize( + name, + target:, + src:, + toolchain: nil, + build_dir: nil, + rubies_dir: nil, + **options + ) + @target = target + @build_dir = build_dir + @rubies_dir = rubies_dir + @toolchain = (toolchain || RubyWasm::Toolchain.get(target, @build_dir)) + + @libyaml = RubyWasm::LibYAMLProduct.new(@build_dir, @target, @toolchain) + @zlib = RubyWasm::ZlibProduct.new(@build_dir, @target, @toolchain) + @wasi_vfs = RubyWasm::WasiVfsProduct.new(@build_dir) + @source = RubyWasm::BuildSource.new(src, @build_dir) + @baseruby = RubyWasm::BaseRubyProduct.new(@build_dir, @source) + @openssl = RubyWasm::OpenSSLProduct.new(@build_dir, @target, @toolchain) + + build_params = + RubyWasm::BuildParams.new(options.merge(name: name, target: target)) + + @crossruby = + RubyWasm::CrossRubyProduct.new( + build_params, + @build_dir, + @rubies_dir, + @baseruby, + @source, + @toolchain + ) + + @crossruby.with_libyaml @libyaml + @crossruby.with_zlib @zlib + @crossruby.with_wasi_vfs @wasi_vfs + @crossruby.with_openssl @openssl end - # Human readable status printer for the build. - class StatusPrinter - def initialize - @mutex = Mutex.new - @counter = 0 - @indicators = "|/-\\" - end - - def stdout(message) - require "io/console" - @mutex.synchronize do - $stdout.print "\e[K" - first_line = message.lines(chomp: true).first || "" - - # Make sure we don't line-wrap the output - size = - __skip__ = - IO.respond_to?(:console_size) ? IO.console_size : IO.console.winsize - terminal_width = size[1].to_i.nonzero? || 80 - width_limit = terminal_width / 2 - 3 - - if first_line.length > width_limit - first_line = (first_line[0..width_limit - 5] || "") + "..." - end - indicator = @indicators[@counter] || " " - to_print = " " + indicator + " " + first_line - $stdout.print to_print - $stdout.print "\e[1A\n" - @counter += 1 - @counter = 0 if @counter >= @indicators.length - end - end - - def stderr(message) - @mutex.synchronize { $stdout.print message } - end - - def done - @mutex.synchronize { $stdout.print "\e[K" } - end + def cache_key(digest) + @source.cache_key(digest) + @crossruby.cache_key(digest) + digest << @build_dir + digest << @rubies_dir + digest << @target + digest << @toolchain.name + digest << @libyaml.name + digest << @zlib.name + digest << @openssl.name + digest << @wasi_vfs.name end end diff --git a/lib/ruby_wasm/build/downloader.rb b/lib/ruby_wasm/build/downloader.rb index 38946eda14..b6e80019f7 100644 --- a/lib/ruby_wasm/build/downloader.rb +++ b/lib/ruby_wasm/build/downloader.rb @@ -1,15 +1,5 @@ module RubyWasm class Downloader - def format_size(size) - units = %w[B KB MB GB TB] - unit = 0 - while size > 1024 and unit < units.size - 1 - size /= 1024.0 - unit += 1 - end - "%s #{units[unit]}" % size.round(2) - end - def download(url, dest, message) require "open-uri" content_length = 0 @@ -18,7 +8,7 @@ def download(url, dest, message) uri, content_length_proc: ->(len) { content_length = len }, progress_proc: ->(size) do - print "\r#{message} (#{format_size(content_length)}) %.2f%%" % + print "\r#{message} (#{SizeFormatter.format(content_length)}) %.2f%%" % (size.to_f / content_length * 100) end ) { |f| File.open(dest, "wb") { |out| out.write f.read } } diff --git a/lib/ruby_wasm/build/executor.rb b/lib/ruby_wasm/build/executor.rb new file mode 100644 index 0000000000..f1e10d4926 --- /dev/null +++ b/lib/ruby_wasm/build/executor.rb @@ -0,0 +1,187 @@ +module RubyWasm + # Build executor to run the actual build commands. + class BuildExecutor + attr_reader :process_count + + def initialize(verbose: false, process_count: nil) + @verbose = verbose + @github_actions_markup = ENV["ENABLE_GITHUB_ACTIONS_MARKUP"] != nil + __skip__ = + begin + require "etc" + @process_count = process_count || Etc.nprocessors + rescue LoadError + @process_count = process_count || 1 + end + end + + def system(*args, chdir: nil, env: nil) + require "open3" + + _print_command(args, env) + + # @type var kwargs: Hash[Symbol, untyped] + kwargs = {} + kwargs[:chdir] = chdir if chdir + + args = args.to_a.map(&:to_s) + # TODO: Remove __skip__ once we have open3 RBS definitions. + __skip__ = + if @verbose || !$stdout.tty? + kwargs[:exception] = true + if env + Kernel.system(env, *args, **kwargs) + else + Kernel.system(*args, **kwargs) + end + else + printer = StatusPrinter.new + block = + proc do |stdin, stdout, stderr, wait_thr| + mux = Mutex.new + out = String.new + err = String.new + readers = + [ + [stdout, :stdout, out], + [stderr, :stderr, err] + ].map do |io, name, str| + reader = + Thread.new do + while (line = io.gets) + mux.synchronize do + printer.send(name, line) + str << line + end + end + end + reader.report_on_exception = false + reader + end + + readers.each(&:join) + + [out, err, wait_thr.value] + end + begin + stdout, stderr, status = + if env + Open3.popen3(env, *args, **kwargs, &block) + else + Open3.popen3(*args, **kwargs, &block) + end + unless status.success? + $stderr.puts stdout + $stderr.puts stderr + cmd_to_print = args.map { |a| "'#{a}'" }.join(" ") + raise "Command failed with status (#{status.exitstatus}): #{cmd_to_print}" + end + ensure + printer.done + end + end + return + rescue => e + $stdout.flush + $stderr.puts "Try running with `rake --verbose` for more complete output." + raise e + end + + def begin_section(klass, name, note) + message = "\e[1;36m==>\e[0m \e[1m#{klass}(#{name}) -- #{note}\e[0m" + if @github_actions_markup + puts "::group::#{message}" + else + puts message + end + + # Record the start time + @start_times ||= Hash.new + @start_times[[klass, name]] = Time.now + + $stdout.flush + end + + def end_section(klass, name) + took = Time.now - @start_times[[klass, name]] + puts "::endgroup::" if @github_actions_markup + puts "\e[1;36m==>\e[0m \e[1m#{klass}(#{name}) -- done in #{took.round(2)}s\e[0m" + end + + def rm_rf(list) + FileUtils.rm_rf(list) + end + + def rm_f(list) + FileUtils.rm_f(list) + end + + def cp_r(src, dest) + FileUtils.cp_r(src, dest) + end + + def mv(src, dest) + FileUtils.mv(src, dest) + end + + def mkdir_p(list) + FileUtils.mkdir_p(list) + end + + def write(path, data) + File.write(path, data) + end + + private + + def _print_command(args, env) + require "shellwords" + # Bold cyan + print "\e[1;36m ==>\e[0m " + print "env " + env.map { |k, v| "#{k}=#{v}" }.join(" ") + " " if env + print args.map { |arg| Shellwords.escape(arg.to_s) }.join(" ") + "\n" + end + end + + # Human readable status printer for the build. + class StatusPrinter + def initialize + @mutex = Mutex.new + @counter = 0 + @indicators = "|/-\\" + end + + def stdout(message) + require "io/console" + @mutex.synchronize do + $stdout.print "\e[K" + first_line = message.lines(chomp: true).first || "" + + # Make sure we don't line-wrap the output + size = + __skip__ = + IO.respond_to?(:console_size) ? IO.console_size : IO.console.winsize + terminal_width = size[1].to_i.nonzero? || 80 + width_limit = terminal_width / 2 - 3 + + if first_line.length > width_limit + first_line = (first_line[0..width_limit - 5] || "") + "..." + end + indicator = @indicators[@counter] || " " + to_print = " " + indicator + " " + first_line + $stdout.print to_print + $stdout.print "\e[1A\n" + @counter += 1 + @counter = 0 if @counter >= @indicators.length + end + end + + def stderr(message) + @mutex.synchronize { $stdout.print message } + end + + def done + @mutex.synchronize { $stdout.print "\e[K" } + end + end +end diff --git a/lib/ruby_wasm/build/product/crossruby.rb b/lib/ruby_wasm/build/product/crossruby.rb index ddbb91240a..9f90fdb919 100644 --- a/lib/ruby_wasm/build/product/crossruby.rb +++ b/lib/ruby_wasm/build/product/crossruby.rb @@ -1,21 +1,35 @@ require_relative "./product" +require "json" module RubyWasm class CrossRubyExtProduct < BuildProduct attr_reader :name - def initialize(srcdir, toolchain, name: nil) + + def initialize(srcdir, toolchain, ext_relative_path: nil) @srcdir, @toolchain = srcdir, toolchain - @name = name || File.basename(srcdir) + # ext_relative_path is relative path from build dir + # e.g. cgi-0.3.6/ext/cgi/escape + @ext_relative_path = ext_relative_path || File.basename(srcdir) + @name = ext_relative_path end def product_build_dir(crossruby) - File.join(crossruby.ext_build_dir, @name) + File.join(crossruby.ext_build_dir, @ext_relative_path) end def linklist(crossruby) File.join(product_build_dir(crossruby), "link.filelist") end + def metadata_json(crossruby) + File.join(product_build_dir(crossruby), "rbwasm.metadata.json") + end + + def feature_name(crossruby) + metadata = JSON.parse(File.read(metadata_json(crossruby))) + metadata["target"] + end + def make_args(crossruby) make_args = [] make_args << "CC=#{@toolchain.cc}" @@ -28,7 +42,6 @@ def make_args(crossruby) end def build(executor, crossruby) - lib = @name objdir = product_build_dir crossruby executor.mkdir_p objdir do_extconf executor, crossruby @@ -37,7 +50,7 @@ def build(executor, crossruby) "-C", "#{objdir}", *make_args(crossruby), - "#{lib}.a" + "static" # A ext can provide link args by link.filelist. It contains only built archive file by default. unless File.exist?(linklist(crossruby)) executor.write( @@ -58,6 +71,9 @@ def do_extconf(executor, crossruby) # HACK: extout is required to find config.h "-e", %Q($extout="#{crossruby.build_dir}/.ext"), + # HACK: skip have_devel check since ruby is not installed yet + "-e", + "$have_devel = true", # HACK: force static ext build by imitating extmk "-e", "$static = true; trace_var(:$static) {|v| $static = true }", @@ -68,6 +84,10 @@ def do_extconf(executor, crossruby) %Q($0="#{@srcdir}/extconf.rb"), "-e", %Q(require_relative "#{@srcdir}/extconf.rb"), + # HACK: extract "$target" from extconf.rb to get a full target name + # like "cgi/escape" instead of "escape" + "-e", + %Q(require "json"; File.write("#{metadata_json(crossruby)}", JSON.dump({target: $target}))), "-I#{crossruby.build_dir}" ] # Clear RUBYOPT to avoid loading unrelated bundle setup @@ -108,22 +128,14 @@ class CrossRubyProduct < AutoconfProduct :xcflags, :xldflags - def initialize( - params, - build_dir, - rubies_dir, - baseruby, - source, - toolchain, - user_exts: [] - ) + def initialize(params, build_dir, rubies_dir, baseruby, source, toolchain) @params = params @rubies_dir = rubies_dir @build_dir = build_dir @baseruby = baseruby @source = source @toolchain = toolchain - @user_exts = user_exts + @user_exts = [] @wasmoptflags = [] @cppflags = [] @cflags = [] @@ -170,7 +182,7 @@ def build(executor, remake: false, reconfigure: false) executor.mkdir_p File.dirname(extinit_obj) executor.system "ruby", extinit_c_erb, - *@user_exts.map(&:name), + *@user_exts.map { |ext| ext.feature_name(self) }, "--cc", toolchain.cc, "--output", @@ -187,7 +199,7 @@ def build(executor, remake: false, reconfigure: false) executor.rm_rf dest_dir executor.cp_r install_dir, dest_dir @user_exts.each { |ext| ext.do_install_rb(executor, self) } - executor.system "tar", "cfz", artifact, "-C", "rubies", name + executor.system "tar", "cfz", artifact, "-C", @rubies_dir, name executor.end_section self.class, name end diff --git a/lib/ruby_wasm/build/product/openssl.rb b/lib/ruby_wasm/build/product/openssl.rb index ed5e0c086b..4bea0ddb45 100644 --- a/lib/ruby_wasm/build/product/openssl.rb +++ b/lib/ruby_wasm/build/product/openssl.rb @@ -1,4 +1,3 @@ -require "rake" require_relative "./product" module RubyWasm diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb new file mode 100644 index 0000000000..e8a80ed849 --- /dev/null +++ b/lib/ruby_wasm/cli.rb @@ -0,0 +1,107 @@ +require "optparse" +require "rbconfig" + +module RubyWasm + class CLI + def initialize(stdout:, stderr:) + @stdout = stdout + @stderr = stderr + end + + def run(args) + available_commands = %w[build] + OptionParser + .new do |opts| + opts.banner = <<~USAGE + Usage: rbwasm [options...] [command] + + Available commands: #{available_commands.join(", ")} + USAGE + opts.version = RubyWasm::VERSION + opts.on("-h", "--help", "Prints this help") do + @stdout.puts opts + exit + end + opts.on("--log-level LEVEL", "Log level") do |level| + RubyWasm.log_level = level.to_sym + end + end + .order!(args) + + command = args.shift + case command + when "build" + build(args) + else + @stderr.puts "Unknown command: #{command}" + exit + end + end + + def build(args) + # @type var options: Hash[Symbol, untyped] + options = { + save_temps: false, + optimize: false, + format: :single_module, + stdlib: true + } + OptionParser + .new do |opts| + opts.banner = "Usage: rbwasm componentize [options]" + opts.on("-h", "--help", "Prints this help") do + @stdout.puts opts + exit + end + + opts.on("--save-temps", "Save temporary files") do + options[:save_temps] = true + end + + opts.on("--optimize", "Optimize the output") do + options[:optimize] = true + end + + opts.on("-o", "--output FILE", "Output file") do |file| + options[:output] = file + end + + opts.on("--format FORMAT", "Output format") do |format| + options[:format] = format.to_sym + end + + opts.on("--[no-]stdlib", "Include stdlib") do |stdlib| + options[:stdlib] = stdlib + end + end + .parse!(args) + + verbose = RubyWasm.logger.level == :debug + executor = RubyWasm::BuildExecutor.new(verbose: verbose) + + require "tmpdir" + + if options[:save_temps] + tmpdir = Dir.mktmpdir + self.do_build(executor, tmpdir, options) + @stdout.puts "Temporary files are saved to #{tmpdir}" + exit + else + Dir.mktmpdir { |tmpdir| self.do_build(executor, tmpdir, options) } + end + end + + private def do_build(executor, tmp_dir, options) + packager = RubyWasm::Packager.new(tmp_dir) + wasm_bytes = packager.package(executor, options) + @stdout.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}" + if options[:output] + File.binwrite(options[:output], wasm_bytes.pack("C*")) + @stdout.puts "Wrote #{options[:output]}" + else + @stderr.puts "Writing to stdout" + @stdout.write wasm_bytes.pack("C*") + end + end + end +end diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb new file mode 100644 index 0000000000..893996adb2 --- /dev/null +++ b/lib/ruby_wasm/packager.rb @@ -0,0 +1,72 @@ +class RubyWasm::Packager + def initialize(dest_dir, definition = Bundler.definition) + @dest_dir = dest_dir + @definition = definition + @target_triplet = "wasm32-unknown-wasi" + end + + def package(executor, options) + require_relative "ruby_wasm.so" + + ruby_core = RubyWasm::Packager::Core.new(self) + tarball = ruby_core.build(executor) + + fs = RubyWasm::Packager::FileSystem.new(@dest_dir, self) + fs.package_ruby_root(tarball, executor) + + ruby_wasm_bin = File.expand_path("bin/ruby", fs.ruby_root) + wasm_bytes = File.binread(ruby_wasm_bin).bytes + + fs.package_gems + fs.remove_non_runtime_files(executor) + fs.remove_stdlib(executor) unless options[:stdlib] + + wasi_vfs = RubyWasmExt::WasiVfs.new + wasi_vfs.map_dir("/bundle", fs.bundle_dir) + wasi_vfs.map_dir("/usr", File.dirname(fs.ruby_root)) + + wasm_bytes = wasi_vfs.pack(wasm_bytes) + + wasm_bytes = RubyWasmExt.preinitialize(wasm_bytes) if options[:optimize] + wasm_bytes + end + + EXCLUDED_GEMS = %w[ruby_wasm bundler] + + def specs + @definition.specs.reject { |spec| EXCLUDED_GEMS.include?(spec.name) } + end + + def support_dynamic_linking? + @ruby_channel == "head" + end + + def root + @root ||= + begin + if explicit = ENV["RUBY_WASM_ROOT"] + explicit + else + Bundler.root + end + rescue Bundler::GemfileNotFound + Dir.pwd + end + end + + def build_options + { + target: @target_triplet, + src: { + name: "3.3", + type: "tarball", + url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.0.tar.gz" + }, + default_exts: + "bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl", + toolchain: RubyWasm::Toolchain.get(@target_triplet), + build_dir: File.join(root, "build"), + rubies_dir: File.join(root, "rubies") + } + end +end diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb new file mode 100644 index 0000000000..8dee79b199 --- /dev/null +++ b/lib/ruby_wasm/packager/core.rb @@ -0,0 +1,89 @@ +class RubyWasm::Packager::Core + def initialize(packager) + @packager = packager + end + + def build(executor) + strategy = build_strategy + strategy.build(executor) + end + + private + + def build_strategy + @build_strategy ||= + begin + has_exts = @packager.specs.any? { |spec| spec.extensions.any? } + if @packager.support_dynamic_linking? + DynamicLinking.new(@packager) + else + StaticLinking.new(@packager) + end + end + end + + class BuildStrategy + def initialize(packager) + @packager = packager + end + + def build(executor) + raise NotImplementedError + end + + # Array of paths to extconf.rb files. + def specs_with_extensions + @packager.specs.select { |spec| !spec.extensions.empty? } + end + end + + class DynamicLinking < BuildStrategy + end + + class StaticLinking < BuildStrategy + def build(executor) + @build ||= RubyWasm::Build.new(name, **@packager.build_options) + @build.crossruby.user_exts = user_exts + @build.crossruby.debugflags = %w[-g] + @build.crossruby.wasmoptflags = %w[-O3 -g] + @build.crossruby.ldflags = %w[ + -Xlinker + --stack-first + -Xlinker + -z + -Xlinker + stack-size=16777216 + ] + Bundler.with_unbundled_env { @build.crossruby.build(executor) } + @build.crossruby.artifact + end + + def user_exts + @user_exts ||= + specs_with_extensions.flat_map do |spec| + spec.extensions.map do |ext| + ext_feature = File.dirname(ext) # e.g. "ext/cgi/escape" + ext_srcdir = File.join(spec.full_gem_path, ext_feature) + ext_relative_path = File.join(spec.full_name, ext_feature) + RubyWasm::CrossRubyExtProduct.new( + ext_srcdir, + @build.toolchain, + ext_relative_path: ext_relative_path + ) + end + end + end + + def name + require "digest" + options = @packager.build_options + src_channel = options[:src][:name] + target_triplet = options[:target] + base = "ruby-#{src_channel}-static-#{target_triplet}" + exts = specs_with_extensions.sort + hash = ::Digest::MD5.new + specs_with_extensions.each { |spec| hash << spec.full_name } + exts.empty? ? base : "#{base}-#{hash.hexdigest}" + end + end +end diff --git a/lib/ruby_wasm/packager/file_system.rb b/lib/ruby_wasm/packager/file_system.rb new file mode 100644 index 0000000000..e918b42303 --- /dev/null +++ b/lib/ruby_wasm/packager/file_system.rb @@ -0,0 +1,159 @@ +# Package Ruby code into a mountable directory. +class RubyWasm::Packager::FileSystem + def initialize(dest_dir, packager) + @dest_dir = dest_dir + @packager = packager + @ruby_root = File.join(@dest_dir, "usr", "local") + end + + def package_ruby_root(tarball, executor) + usr_dir = File.dirname(@ruby_root) + executor.mkdir_p usr_dir + executor.system( + "tar", + "-C", + usr_dir, + "-xzf", + tarball, + "--strip-components=2" + ) + end + + def remove_stdlib(executor) + # Include only rbconfig.rb + rbconfig = + File.join( + @ruby_root, + "lib", + "ruby", + ruby_version, + "wasm32-wasi", + "rbconfig.rb" + ) + # Remove all files except rbconfig.rb + RubyWasm.logger.info "Removing stdlib (except rbconfig.rb: #{rbconfig})" + rbconfig_contents = File.read(rbconfig) + executor.rm_rf @ruby_root + executor.mkdir_p File.dirname(rbconfig) + File.write(rbconfig, rbconfig_contents) + end + + def package_gems + @packager.specs.each do |spec| + RubyWasm.logger.info "Packaging gem: #{spec.full_name}" + end + self.each_gem_content_path do |relative, source| + RubyWasm.logger.debug "Packaging gem file: #{relative}" + dest = File.join(@dest_dir, relative) + FileUtils.mkdir_p File.dirname(dest) + FileUtils.cp_r source, dest + end + + setup_rb_path = File.join(bundle_relative_path, "setup.rb") + RubyWasm.logger.info "Packaging setup.rb: #{setup_rb_path}" + File.write(File.join(@dest_dir, setup_rb_path), setup_rb_content) + end + + def setup_rb_content + content = <<~RUBY + kernel = (class << ::Kernel; self; end) + [kernel, ::Kernel].each do |k| + if k.private_method_defined?(:gem_original_require) + private_require = k.private_method_defined?(:require) + k.send(:remove_method, :require) + k.send(:define_method, :require, k.instance_method(:gem_original_require)) + k.send(:private, :require) if private_require + end + end + RUBY + + self.each_gem_require_path do |relative, _| + content << %Q[$:.unshift File.expand_path("#{File.join("/", relative)}")\n] + end + content + end + + def remove_non_runtime_files(executor) + %w[ + **/*.so + usr/local/lib/libruby-static.a + usr/local/bin/ruby + usr/local/include + ].each do |pattern| + Dir + .glob(File.join(@dest_dir, pattern)) + .each do |entry| + RubyWasm.logger.debug do + relative_entry = Pathname.new(entry).relative_path_from(@dest_dir) + "Removing non-runtime file: #{relative_entry}" + end + executor.rm_rf entry + end + end + end + + def bundle_dir = File.join(@dest_dir, bundle_relative_path) + def ruby_root = @ruby_root + + private + + # Iterates over each gem's require path and extension path. + # Yields the installation relative path and the source path. + def each_gem_require_path(&block) + each_gem_extension_path(&block) + @packager.specs.each do |spec| + spec.raw_require_paths.each do |require_path| + source = File.expand_path(require_path, spec.full_gem_path) + next unless File.exist?(source) + relative = + File.join(bundle_relative_path, "gems", spec.full_name, require_path) + yield relative, source + end + end + end + + def each_gem_content_path(&block) + each_gem_extension_path(&block) + + @packager.specs.each do |spec| + next unless File.exist?(spec.full_gem_path) + + Dir + .children(spec.full_gem_path) + .each do |require_path| + source = File.expand_path(require_path, spec.full_gem_path) + next unless File.exist?(source) + relative = + File.join( + bundle_relative_path, + "gems", + spec.full_name, + require_path + ) + yield relative, source + end + end + end + + def each_gem_extension_path + @packager.specs.each do |spec| + if !spec.extensions.empty? && File.exist?(spec.extension_dir) + relative = File.join(bundle_relative_path, "extensions", spec.full_name) + yield relative, spec.extension_dir + end + end + end + + def bundle_relative_path + "bundle" + end + + def ruby_version + rubyarchdir = self.rubyarchdir + File.basename(File.dirname(rubyarchdir)) + end + + def rubyarchdir + Dir.glob(File.join(@ruby_root, "lib", "ruby", "*", "wasm32-wasi")).first + end +end diff --git a/lib/ruby_wasm/rake_task.rb b/lib/ruby_wasm/rake_task.rb index eb403617c8..2540f0d4d1 100644 --- a/lib/ruby_wasm/rake_task.rb +++ b/lib/ruby_wasm/rake_task.rb @@ -5,31 +5,6 @@ class RubyWasm::BuildTask < ::Rake::TaskLib # Name of the task. attr_accessor :name - # Source to build from. - attr_reader :source - - # Target to build for. - attr_reader :target - - # Toolchain for the build. - # Defaults to the Toolchain.get for the target. - attr_reader :toolchain - - # LibYAML product to build. - attr_reader :libyaml - - # zlib product to build. - attr_reader :zlib - - # wasi-vfs product used by the crossruby. - attr_reader :wasi_vfs - - # BaseRuby product to build. - attr_reader :baseruby - - # CrossRuby product to build. - attr_reader :crossruby - def initialize( name, target:, @@ -37,40 +12,22 @@ def initialize( toolchain: nil, build_dir: nil, rubies_dir: nil, - **options + **options, + &block ) - @name = name - @target = target - @build_dir = build_dir || File.join(Dir.pwd, "build") - @rubies_dir = rubies_dir || File.join(Dir.pwd, "rubies") - @toolchain = (toolchain || RubyWasm::Toolchain.get(target, @build_dir)) - - @libyaml = RubyWasm::LibYAMLProduct.new(@build_dir, @target, @toolchain) - @zlib = RubyWasm::ZlibProduct.new(@build_dir, @target, @toolchain) - @wasi_vfs = RubyWasm::WasiVfsProduct.new(@build_dir) - @source = RubyWasm::BuildSource.new(src, @build_dir) - @baseruby = RubyWasm::BaseRubyProduct.new(@build_dir, @source) - @openssl = RubyWasm::OpenSSLProduct.new(@build_dir, @target, @toolchain) - - build_params = - RubyWasm::BuildParams.new(options.merge(name: name, target: target)) - - @crossruby = - RubyWasm::CrossRubyProduct.new( - build_params, - @build_dir, - @rubies_dir, - @baseruby, - @source, - @toolchain + @build = + RubyWasm::Build.new( + name, + target: target, + src: src, + toolchain: toolchain, + build_dir: build_dir || File.join(Dir.pwd, "build"), + rubies_dir: rubies_dir || File.join(Dir.pwd, "rubies"), + **options, + &block ) - yield self if block_given? - - @crossruby.with_libyaml @libyaml - @crossruby.with_zlib @zlib - @crossruby.with_wasi_vfs @wasi_vfs - @crossruby.with_openssl @openssl - + yield @build if block_given? + @crossruby = @build.crossruby # Rake.verbose can be Object.new by default, so compare with true explicitly. executor = RubyWasm::BuildExecutor.new(verbose: Rake.verbose == true) @@ -95,16 +52,7 @@ def initialize( def hexdigest require "digest" digest = Digest::SHA256.new - @source.cache_key(digest) - @crossruby.cache_key(digest) - digest << @build_dir - digest << @rubies_dir - digest << @target - digest << @toolchain.name - digest << @libyaml.name - digest << @zlib.name - digest << @openssl.name - digest << @wasi_vfs.name + @build.cache_key(digest) digest.hexdigest end end diff --git a/lib/ruby_wasm/util.rb b/lib/ruby_wasm/util.rb new file mode 100644 index 0000000000..f19530ee0f --- /dev/null +++ b/lib/ruby_wasm/util.rb @@ -0,0 +1,15 @@ +module RubyWasm + module SizeFormatter + def format(size) + units = %w[B KB MB GB TB] + unit = 0 + while size > 1024 and unit < units.size - 1 + size /= 1024.0 + unit += 1 + end + "%s #{units[unit]}" % size.round(2) + end + + module_function :format + end +end diff --git a/packages/crates/wasm-inject/Cargo.toml b/packages/crates/wasm-inject/Cargo.toml new file mode 100644 index 0000000000..75a17f590c --- /dev/null +++ b/packages/crates/wasm-inject/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "wasm-inject" +version = "0.1.0" +edition = "2021" +authors = ["Yuta Saito "] +license = "MIT" +publish = false + +[dependencies] +walrus = "0.20.3" +anyhow = "1.0.75" + +[dev-dependencies] +wasmparser = "0.118.1" +wasmprinter = "0.2.75" diff --git a/packages/crates/wasm-inject/src/bin/wasm-inject.rs b/packages/crates/wasm-inject/src/bin/wasm-inject.rs new file mode 100644 index 0000000000..4df2290741 --- /dev/null +++ b/packages/crates/wasm-inject/src/bin/wasm-inject.rs @@ -0,0 +1,29 @@ +fn main() { + // Parse CLI args + // ``` + // wasm-inject + // ``` + + let args: Vec = std::env::args().collect(); + let file = args.get(1).expect("missing file"); + let output = args.get(2).expect("missing output"); + componentize(file, output); +} + +fn componentize(file: &str, output: &str) { + let core_module = + std::fs::read(file).unwrap_or_else(|_| panic!("failed to read file: {}", file)); + let mut c10zer = wasm_inject::WasmInject::new(&core_module, "ruby.c10zrt") + .expect("failed to create componentizer"); + + c10zer + .add_import_func("my_module", "hello", &[], &[]) + .expect("failed to add import func"); + c10zer + .add_export_func("greet", &[], &[]) + .expect("failed to add export func"); + + let wasm = c10zer.run().expect("failed to run componentizer"); + + std::fs::write(output, wasm).expect("failed to write output"); +} diff --git a/packages/crates/wasm-inject/src/lib.rs b/packages/crates/wasm-inject/src/lib.rs new file mode 100644 index 0000000000..6c6dcbcff0 --- /dev/null +++ b/packages/crates/wasm-inject/src/lib.rs @@ -0,0 +1,362 @@ +use std::collections::HashMap; + +use anyhow::{anyhow, Ok, Result}; +use walrus::{ir::Value, ModuleConfig, ValType}; + +pub use walrus; + +struct InjectedImportFunc { + name: String, + trampoline: walrus::FunctionId, +} +pub struct WasmInject { + prefix: String, + module: walrus::Module, + injected_imports: HashMap>, + imports_table: walrus::TableId, +} + +mod abi { + // Canonical ABI + pub(crate) const CABI_REALLOC: &str = "cabi_realloc"; + pub(crate) const CABI_FREE: &str = "cabi_free"; + + // wasm-inject ABI + // fn push_{T}(val: T, ctx: ptr) -> () + pub(crate) const PUSH_I32: &str = "push_i32"; + pub(crate) const PUSH_I64: &str = "push_i64"; + pub(crate) const PUSH_F32: &str = "push_f32"; + pub(crate) const PUSH_F64: &str = "push_f64"; + // fn pop_{T}(ctx: ptr) -> T + pub(crate) const POP_I32: &str = "pop_i32"; + pub(crate) const POP_I64: &str = "pop_i64"; + pub(crate) const POP_F32: &str = "pop_f32"; + pub(crate) const POP_F64: &str = "pop_f64"; + // fn call_export_func(name: *const u8, ctx: ptr) -> () + pub(crate) const CALL_EXPORT_FUNC: &str = "call_export_func"; + // fn add_import_func(module_name: *const u8, name: *const u8, func: ptr) -> () + pub(crate) const ADD_IMPORT_FUNC: &str = "add_import_func"; + // fn init_context() -> ptr + pub(crate) const INIT_CONTEXT: &str = "init_context"; + // fn destroy_context(ctx: ptr) -> () + pub(crate) const DESTROY_CONTEXT: &str = "destroy_context"; +} + +impl WasmInject { + pub fn new(core_module: &[u8], prefix: &str) -> Result { + let mut module_config = ModuleConfig::new(); + module_config.generate_name_section(true); + let module = module_config.parse(core_module)?; + let export_name = format!("{}.imports_table", prefix); + let imports_table = Self::imports_table(&module, &export_name)?; + Ok(Self { + prefix: prefix.to_string(), + module, + injected_imports: HashMap::new(), + imports_table, + }) + } + + fn get_export_func(&self, name: &str) -> Result { + self.module + .exports + .get_func(format!("{}.{}", self.prefix, name)) + } + + pub fn add_export_func( + &mut self, + name: &str, + params: &[ValType], + results: &[ValType], + ) -> Result<()> { + let mut func = walrus::FunctionBuilder::new(&mut self.module.types, params, results); + func.name(name.to_string()); + let mut body = func.func_body(); + + let memory = Self::get_memory(&self.module)?; + + let name_ptr = self.module.locals.add(ValType::I32); + let name_bytes = [name.as_bytes(), b"\0"].concat(); + + let cabi_realloc = self.get_cabi_func(abi::CABI_REALLOC)?; + + body.i32_const(0) + .i32_const(name_bytes.len() as i32) + .call(cabi_realloc) + .local_set(name_ptr); + + // 1. Allocate a string for the name + store_string_at(&mut body, memory, &name_bytes, name_ptr, 0); + + let call_export_func = self.get_export_func(abi::CALL_EXPORT_FUNC)?; + let init_ctx = self.get_export_func(abi::INIT_CONTEXT)?; + + let ctx = self.module.locals.add(ValType::I32); + + // 2. Allocate a stack context + body.call(init_ctx).local_set(ctx); + + // 3. Push parameters to the stack + let args = params.iter().map(|p| { + self.module.locals.add(*p) + }).collect::>(); + for (idx, param) in params.iter().enumerate().rev() { + let local = args[idx]; + match param { + ValType::I32 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_I32)?), + ValType::I64 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_I64)?), + ValType::F32 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_F32)?), + ValType::F64 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_F64)?), + _ => unimplemented!("unsupported param type: {:?}", param), + }; + } + + // 4. Call the exposed function by function name + body.local_get(ctx) + .local_get(name_ptr) + .call(call_export_func); + + // 5. Deallocate the function name + body.local_get(name_ptr).call(self.get_cabi_func(abi::CABI_FREE)?); + + // 6. Pop results from the stack + for result in results.iter() { + match result { + ValType::I32 => body.local_get(ctx).call(self.get_export_func(abi::POP_I32)?), + ValType::I64 => body.local_get(ctx).call(self.get_export_func(abi::POP_I64)?), + ValType::F32 => body.local_get(ctx).call(self.get_export_func(abi::POP_F32)?), + ValType::F64 => body.local_get(ctx).call(self.get_export_func(abi::POP_F64)?), + _ => unimplemented!("unsupported result type: {:?}", result), + }; + } + + // 7. Deallocate the stack context + body.local_get(ctx).call(self.get_export_func(abi::DESTROY_CONTEXT)?); + + body.return_(); + + let func = func.finish(args, &mut self.module.funcs); + self.module.exports.add(name, func); + + Ok(()) + } + + pub fn add_import_func( + &mut self, + module: &str, + name: &str, + params: &[ValType], + results: &[ValType], + ) -> Result<()> { + let ty = self + .module + .types + .find(params, results) + .unwrap_or_else(|| self.module.types.add(params, results)); + self.add_import_func_with_tyid(module, name, ty) + } + + fn add_import_func_with_tyid( + &mut self, + module: &str, + name: &str, + ty: walrus::TypeId, + ) -> Result<()> { + let (import_func, _) = self.module.add_import_func(module, name, ty); + let ty = self.module.types.get(ty); + let params = ty.params().to_vec(); + let results = ty.results().to_vec(); + + let ctx_ty = ValType::I32; + let mut builder = walrus::FunctionBuilder::new(&mut self.module.types, &[ctx_ty], &[]); + let ctx = self.module.locals.add(ctx_ty); + let mut body = builder.func_body(); + + // 1. Pop parameters from the stack + for param in params.iter() { + match param { + ValType::I32 => body.local_get(ctx).call(self.get_export_func(abi::POP_I32)?), + ValType::I64 => body.local_get(ctx).call(self.get_export_func(abi::POP_I64)?), + ValType::F32 => body.local_get(ctx).call(self.get_export_func(abi::POP_F32)?), + ValType::F64 => body.local_get(ctx).call(self.get_export_func(abi::POP_F64)?), + _ => unimplemented!("unsupported param type: {:?}", param), + }; + } + + // 2. Call the import function + body.call(import_func); + + // 3. Push results to the stack + for result in results.iter().rev() { + match result { + ValType::I32 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_I32)?), + ValType::I64 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_I64)?), + ValType::F32 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_F32)?), + ValType::F64 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_F64)?), + _ => unimplemented!("unsupported result type: {:?}", result), + }; + } + + let func = builder.finish(vec![ctx], &mut self.module.funcs); + self.injected_imports + .entry(module.to_string()) + .or_default() + .push(InjectedImportFunc { + name: name.to_string(), + trampoline: func, + }); + Ok(()) + } + + pub fn run(&mut self) -> Result> { + // 1. Create a new registrar function + let registrar = self.make_import_registrar()?; + // 2. Create a new Element segment with the import trampolines + let mut members = vec![Some(registrar)]; + for (_, entries) in self.injected_imports.iter() { + for entry in entries { + members.push(Some(entry.trampoline)); + } + } + let members_len = members.len(); + + self.module.elements.add( + walrus::ElementKind::Active { + table: self.imports_table, + offset: walrus::InitExpr::Value(Value::I32(0)), + }, + ValType::Funcref, + members, + ); + self.module.tables.get_mut(self.imports_table).initial = members_len as u32; + Ok(self.module.emit_wasm()) + } + + fn make_import_registrar(&mut self) -> Result { + let params = [ValType::I32]; // unused context to be called by ruby_c10zrt_invoke_import + let mut builder = walrus::FunctionBuilder::new(&mut self.module.types, ¶ms, &[]); + builder.name(format!("{}.init_add_import_func", self.prefix)); + let mut body = builder.func_body(); + + let cabi_realloc = self.get_cabi_func(abi::CABI_REALLOC)?; + let add_import_func = self.get_export_func(abi::ADD_IMPORT_FUNC)?; + let memory = Self::get_memory(&self.module)?; + let module_name_ptr = self.module.locals.add(ValType::I32); + let name_ptr = self.module.locals.add(ValType::I32); + + let mut element_index = 1; // 0 is preserved for this registrar function + + for (module_name, entries) in self.injected_imports.iter() { + let module_name = [module_name.as_bytes(), b"\0"].concat(); + body.i32_const(0) + .i32_const(module_name.len() as i32) + .call(cabi_realloc) + .local_set(module_name_ptr); + store_string_at(&mut body, memory, &module_name, module_name_ptr, 0); + + for entry in entries { + let name = [entry.name.as_bytes(), b"\0"].concat(); + // Allocate a string for the name + body.i32_const(0) + .i32_const(name.len() as i32) + .call(cabi_realloc) + .local_set(name_ptr); + store_string_at(&mut body, memory, &name, name_ptr, 0); + + // Register the import + body.local_get(module_name_ptr) + .local_get(name_ptr) + .i32_const(element_index) + .call(add_import_func); + + element_index += 1; + } + } + // No need to free the strings, since the buffer ownership is transferred to the application + + Ok(builder.finish(vec![], &mut self.module.funcs)) + } + + fn imports_table(module: &walrus::Module, export_name: &str) -> Result { + for export in module.exports.iter() { + if export.name == export_name { + return match export.item { + walrus::ExportItem::Table(id) => Ok(id), + _ => return Err(anyhow!("export named {} is not a table", export_name)), + }; + } + } + Err(anyhow!("no export named {} found", export_name)) + } + + fn get_memory(module: &walrus::Module) -> Result { + for export in module.exports.iter() { + if export.name == "memory" { + return match export.item { + walrus::ExportItem::Memory(id) => Ok(id), + _ => return Err(anyhow!("export named memory is not a memory")), + }; + } + } + Err(anyhow!("no export named memory found")) + } + + fn get_cabi_func(&self, name: &str) -> Result { + self.module.exports.get_func(name) + } +} + +fn usize_to_wasm_i32(x: usize) -> Value { + Value::I32(i32::from_le_bytes((x as u32).to_le_bytes())) +} + +fn store_string_at( + builder: &mut walrus::InstrSeqBuilder, + memory: walrus::MemoryId, + bytes: &[u8], + base: walrus::LocalId, + offset: usize, +) { + let mut written = 0; + for chunk_size in [8, 4, 2, 1] { + let chunk_count = (bytes.len() - written) / chunk_size; + for _ in 0..chunk_count { + use walrus::ir::{BinaryOp, MemArg, StoreKind}; + + let chunk = &bytes[written..written + chunk_size]; + let (v, kind) = match chunk_size { + 8 => ( + Value::I64(i64::from_le_bytes(chunk.try_into().unwrap())), + StoreKind::I64 { atomic: false }, + ), + 4 => ( + Value::I32(i32::from_le_bytes(chunk.try_into().unwrap())), + StoreKind::I32 { atomic: false }, + ), + 2 => ( + Value::I32(i16::from_le_bytes(chunk.try_into().unwrap()) as i32), + StoreKind::I32_16 { atomic: false }, + ), + 1 => ( + Value::I32(i8::from_le_bytes(chunk.try_into().unwrap()) as i32), + StoreKind::I32_8 { atomic: false }, + ), + _ => unreachable!(), + }; + builder + .local_get(base) + .const_(usize_to_wasm_i32(offset + written)) + .binop(BinaryOp::I32Add) + .const_(v) + .store( + memory, + kind, + MemArg { + align: 1, + offset: 0, + }, + ); + written += chunk_size; + } + } +} diff --git a/packages/crates/wasm-inject/tests/snapshots/export.wat b/packages/crates/wasm-inject/tests/snapshots/export.wat new file mode 100644 index 0000000000..7101a82eb9 --- /dev/null +++ b/packages/crates/wasm-inject/tests/snapshots/export.wat @@ -0,0 +1,281 @@ +(module + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (type (;2;) (func (result i32 i64))) + (type (;3;) (func (param i32))) + (type (;4;) (func (param i32) (result i32))) + (type (;5;) (func (param i32) (result i64))) + (type (;6;) (func (param i32) (result f32))) + (type (;7;) (func (param i32) (result f64))) + (type (;8;) (func (param i32 i32))) + (type (;9;) (func (param i32 i32) (result i32))) + (type (;10;) (func (param i32 i32 i32))) + (type (;11;) (func (param i32 i64))) + (type (;12;) (func (param i32 i64) (result f32 f64))) + (type (;13;) (func (param i64 i32))) + (type (;14;) (func (param f32 i32))) + (type (;15;) (func (param f64 i32))) + (func $p_i32_i64_r_f32_f64 (;0;) (type 12) (param i32 i64) (result f32 f64) + (local i32 i32) + i32.const 0 + i32.const 20 + call $cabi_realloc + local.set 2 + local.get 2 + i32.const 0 + i32.add + i64.const 3920769619818274672 + i64.store align=1 + local.get 2 + i32.const 8 + i32.add + i64.const 6859601697219698484 + i64.store align=1 + local.get 2 + i32.const 16 + i32.add + i32.const 3421798 + i32.store align=1 + call $my-mod.init_context + local.set 3 + local.get 1 + local.get 3 + call $my-mod.push_i64 + local.get 0 + local.get 3 + call $my-mod.push_i32 + local.get 3 + local.get 2 + call $my-mod.call_export_func + local.get 2 + call $cabi_free + local.get 3 + call $my-mod.pop_f32 + local.get 3 + call $my-mod.pop_f64 + local.get 3 + call $my-mod.destroy_context + return + ) + (func $p_i32_i64 (;1;) (type 11) (param i32 i64) + (local i32 i32) + i32.const 0 + i32.const 10 + call $cabi_realloc + local.set 2 + local.get 2 + i32.const 0 + i32.add + i64.const 3920769619818274672 + i64.store align=1 + local.get 2 + i32.const 8 + i32.add + i32.const 52 + i32.store16 align=1 + call $my-mod.init_context + local.set 3 + local.get 1 + local.get 3 + call $my-mod.push_i64 + local.get 0 + local.get 3 + call $my-mod.push_i32 + local.get 3 + local.get 2 + call $my-mod.call_export_func + local.get 2 + call $cabi_free + local.get 3 + call $my-mod.destroy_context + return + ) + (func $r_i32_i64 (;2;) (type 2) (result i32 i64) + (local i32 i32) + i32.const 0 + i32.const 10 + call $cabi_realloc + local.set 0 + local.get 0 + i32.const 0 + i32.add + i64.const 3920769619818274674 + i64.store align=1 + local.get 0 + i32.const 8 + i32.add + i32.const 52 + i32.store16 align=1 + call $my-mod.init_context + local.set 1 + local.get 1 + local.get 0 + call $my-mod.call_export_func + local.get 0 + call $cabi_free + local.get 1 + call $my-mod.pop_i32 + local.get 1 + call $my-mod.pop_i64 + local.get 1 + call $my-mod.destroy_context + return + ) + (func $p_i32 (;3;) (type 3) (param i32) + (local i32 i32) + i32.const 0 + i32.const 6 + call $cabi_realloc + local.set 1 + local.get 1 + i32.const 0 + i32.add + i32.const 862543728 + i32.store align=1 + local.get 1 + i32.const 4 + i32.add + i32.const 50 + i32.store16 align=1 + call $my-mod.init_context + local.set 2 + local.get 0 + local.get 2 + call $my-mod.push_i32 + local.get 2 + local.get 1 + call $my-mod.call_export_func + local.get 1 + call $cabi_free + local.get 2 + call $my-mod.destroy_context + return + ) + (func $r_i32 (;4;) (type 1) (result i32) + (local i32 i32) + i32.const 0 + i32.const 6 + call $cabi_realloc + local.set 0 + local.get 0 + i32.const 0 + i32.add + i32.const 862543730 + i32.store align=1 + local.get 0 + i32.const 4 + i32.add + i32.const 50 + i32.store16 align=1 + call $my-mod.init_context + local.set 1 + local.get 1 + local.get 0 + call $my-mod.call_export_func + local.get 0 + call $cabi_free + local.get 1 + call $my-mod.pop_i32 + local.get 1 + call $my-mod.destroy_context + return + ) + (func $empty (;5;) (type 0) + (local i32 i32) + i32.const 0 + i32.const 6 + call $cabi_realloc + local.set 0 + local.get 0 + i32.const 0 + i32.add + i32.const 1953525093 + i32.store align=1 + local.get 0 + i32.const 4 + i32.add + i32.const 121 + i32.store16 align=1 + call $my-mod.init_context + local.set 1 + local.get 1 + local.get 0 + call $my-mod.call_export_func + local.get 0 + call $cabi_free + local.get 1 + call $my-mod.destroy_context + return + ) + (func $my-mod.call_export_func (;6;) (type 8) (param i32 i32) + unreachable + ) + (func $my-mod.add_import_func (;7;) (type 10) (param i32 i32 i32) + unreachable + ) + (func $my-mod.init_context (;8;) (type 1) (result i32) + unreachable + ) + (func $my-mod.destroy_context (;9;) (type 3) (param i32) + unreachable + ) + (func $my-mod.push_i32 (;10;) (type 8) (param i32 i32) + unreachable + ) + (func $my-mod.push_i64 (;11;) (type 13) (param i64 i32) + unreachable + ) + (func $my-mod.push_f32 (;12;) (type 14) (param f32 i32) + unreachable + ) + (func $my-mod.push_f64 (;13;) (type 15) (param f64 i32) + unreachable + ) + (func $my-mod.pop_i32 (;14;) (type 4) (param i32) (result i32) + unreachable + ) + (func $my-mod.pop_i64 (;15;) (type 5) (param i32) (result i64) + unreachable + ) + (func $my-mod.pop_f32 (;16;) (type 6) (param i32) (result f32) + unreachable + ) + (func $my-mod.pop_f64 (;17;) (type 7) (param i32) (result f64) + unreachable + ) + (func $cabi_realloc (;18;) (type 9) (param i32 i32) (result i32) + unreachable + ) + (func $cabi_free (;19;) (type 3) (param i32) + unreachable + ) + (func $my-mod.init_add_import_func (;20;) (type 3) (param i32)) + (table (;0;) 1 funcref) + (memory (;0;) 1) + (export "my-mod.imports_table" (table 0)) + (export "memory" (memory 0)) + (export "my-mod.call_export_func" (func $my-mod.call_export_func)) + (export "my-mod.add_import_func" (func $my-mod.add_import_func)) + (export "my-mod.init_context" (func $my-mod.init_context)) + (export "my-mod.destroy_context" (func $my-mod.destroy_context)) + (export "my-mod.push_i32" (func $my-mod.push_i32)) + (export "my-mod.push_i64" (func $my-mod.push_i64)) + (export "my-mod.push_f32" (func $my-mod.push_f32)) + (export "my-mod.push_f64" (func $my-mod.push_f64)) + (export "my-mod.pop_i32" (func $my-mod.pop_i32)) + (export "my-mod.pop_i64" (func $my-mod.pop_i64)) + (export "my-mod.pop_f32" (func $my-mod.pop_f32)) + (export "my-mod.pop_f64" (func $my-mod.pop_f64)) + (export "cabi_realloc" (func $cabi_realloc)) + (export "cabi_free" (func $cabi_free)) + (export "empty" (func $empty)) + (export "p_i32" (func $p_i32)) + (export "p_i32_i64" (func $p_i32_i64)) + (export "r_i32" (func $r_i32)) + (export "r_i32_i64" (func $r_i32_i64)) + (export "p_i32_i64_r_f32_f64" (func $p_i32_i64_r_f32_f64)) + (elem (;0;) (i32.const 0) func $my-mod.init_add_import_func) + (@producers + (processed-by "walrus" "0.20.3") + ) +) \ No newline at end of file diff --git a/packages/crates/wasm-inject/tests/snapshots/import.wat b/packages/crates/wasm-inject/tests/snapshots/import.wat new file mode 100644 index 0000000000..6fe2f49a23 --- /dev/null +++ b/packages/crates/wasm-inject/tests/snapshots/import.wat @@ -0,0 +1,251 @@ +(module + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (type (;2;) (func (result i32 i64))) + (type (;3;) (func (param i32))) + (type (;4;) (func (param i32) (result i32))) + (type (;5;) (func (param i32) (result i64))) + (type (;6;) (func (param i32) (result f32))) + (type (;7;) (func (param i32) (result f64))) + (type (;8;) (func (param i32 i32))) + (type (;9;) (func (param i32 i32) (result i32))) + (type (;10;) (func (param i32 i32 i32))) + (type (;11;) (func (param i32 i64))) + (type (;12;) (func (param i32 i64) (result f32 f64))) + (type (;13;) (func (param i64 i32))) + (type (;14;) (func (param f32 i32))) + (type (;15;) (func (param f64 i32))) + (import "t" "empty" (func (;0;) (type 0))) + (import "t" "p_i32" (func (;1;) (type 3))) + (import "t" "p_i32_i64" (func (;2;) (type 11))) + (import "t" "r_i32" (func (;3;) (type 1))) + (import "t" "r_i32_i64" (func (;4;) (type 2))) + (import "t" "p_i32_i64_r_f32_f64" (func (;5;) (type 12))) + (func $my-mod.init_add_import_func (;6;) (type 3) (param i32) + (local i32 i32) + i32.const 0 + i32.const 2 + call $cabi_realloc + local.set 0 + local.get 0 + i32.const 0 + i32.add + i32.const 116 + i32.store16 align=1 + i32.const 0 + i32.const 6 + call $cabi_realloc + local.set 1 + local.get 1 + i32.const 0 + i32.add + i32.const 1953525093 + i32.store align=1 + local.get 1 + i32.const 4 + i32.add + i32.const 121 + i32.store16 align=1 + local.get 0 + local.get 1 + i32.const 1 + call $my-mod.add_import_func + i32.const 0 + i32.const 6 + call $cabi_realloc + local.set 1 + local.get 1 + i32.const 0 + i32.add + i32.const 862543728 + i32.store align=1 + local.get 1 + i32.const 4 + i32.add + i32.const 50 + i32.store16 align=1 + local.get 0 + local.get 1 + i32.const 2 + call $my-mod.add_import_func + i32.const 0 + i32.const 10 + call $cabi_realloc + local.set 1 + local.get 1 + i32.const 0 + i32.add + i64.const 3920769619818274672 + i64.store align=1 + local.get 1 + i32.const 8 + i32.add + i32.const 52 + i32.store16 align=1 + local.get 0 + local.get 1 + i32.const 3 + call $my-mod.add_import_func + i32.const 0 + i32.const 6 + call $cabi_realloc + local.set 1 + local.get 1 + i32.const 0 + i32.add + i32.const 862543730 + i32.store align=1 + local.get 1 + i32.const 4 + i32.add + i32.const 50 + i32.store16 align=1 + local.get 0 + local.get 1 + i32.const 4 + call $my-mod.add_import_func + i32.const 0 + i32.const 10 + call $cabi_realloc + local.set 1 + local.get 1 + i32.const 0 + i32.add + i64.const 3920769619818274674 + i64.store align=1 + local.get 1 + i32.const 8 + i32.add + i32.const 52 + i32.store16 align=1 + local.get 0 + local.get 1 + i32.const 5 + call $my-mod.add_import_func + i32.const 0 + i32.const 20 + call $cabi_realloc + local.set 1 + local.get 1 + i32.const 0 + i32.add + i64.const 3920769619818274672 + i64.store align=1 + local.get 1 + i32.const 8 + i32.add + i64.const 6859601697219698484 + i64.store align=1 + local.get 1 + i32.const 16 + i32.add + i32.const 3421798 + i32.store align=1 + local.get 0 + local.get 1 + i32.const 6 + call $my-mod.add_import_func + ) + (func (;7;) (type 3) (param i32) + local.get 0 + call $my-mod.pop_i32 + local.get 0 + call $my-mod.pop_i64 + call 5 + local.get 0 + call $my-mod.push_f64 + local.get 0 + call $my-mod.push_f32 + ) + (func (;8;) (type 3) (param i32) + local.get 0 + call $my-mod.pop_i32 + local.get 0 + call $my-mod.pop_i64 + call 2 + ) + (func (;9;) (type 3) (param i32) + call 4 + local.get 0 + call $my-mod.push_i64 + local.get 0 + call $my-mod.push_i32 + ) + (func (;10;) (type 3) (param i32) + local.get 0 + call $my-mod.pop_i32 + call 1 + ) + (func (;11;) (type 3) (param i32) + call 3 + local.get 0 + call $my-mod.push_i32 + ) + (func $my-mod.call_export_func (;12;) (type 8) (param i32 i32) + unreachable + ) + (func $my-mod.add_import_func (;13;) (type 10) (param i32 i32 i32) + unreachable + ) + (func $my-mod.init_context (;14;) (type 1) (result i32) + unreachable + ) + (func $my-mod.destroy_context (;15;) (type 3) (param i32) + unreachable + ) + (func $my-mod.push_i32 (;16;) (type 8) (param i32 i32) + unreachable + ) + (func $my-mod.push_i64 (;17;) (type 13) (param i64 i32) + unreachable + ) + (func $my-mod.push_f32 (;18;) (type 14) (param f32 i32) + unreachable + ) + (func $my-mod.push_f64 (;19;) (type 15) (param f64 i32) + unreachable + ) + (func $my-mod.pop_i32 (;20;) (type 4) (param i32) (result i32) + unreachable + ) + (func $my-mod.pop_i64 (;21;) (type 5) (param i32) (result i64) + unreachable + ) + (func $my-mod.pop_f32 (;22;) (type 6) (param i32) (result f32) + unreachable + ) + (func $my-mod.pop_f64 (;23;) (type 7) (param i32) (result f64) + unreachable + ) + (func $cabi_realloc (;24;) (type 9) (param i32 i32) (result i32) + unreachable + ) + (func $cabi_free (;25;) (type 3) (param i32) + unreachable + ) + (func (;26;) (type 3) (param i32) + call 0 + ) + (table (;0;) 7 funcref) + (memory (;0;) 1) + (export "my-mod.imports_table" (table 0)) + (export "memory" (memory 0)) + (export "my-mod.call_export_func" (func $my-mod.call_export_func)) + (export "my-mod.add_import_func" (func $my-mod.add_import_func)) + (export "my-mod.init_context" (func $my-mod.init_context)) + (export "my-mod.destroy_context" (func $my-mod.destroy_context)) + (export "my-mod.push_i32" (func $my-mod.push_i32)) + (export "my-mod.push_i64" (func $my-mod.push_i64)) + (export "my-mod.push_f32" (func $my-mod.push_f32)) + (export "my-mod.push_f64" (func $my-mod.push_f64)) + (export "my-mod.pop_i32" (func $my-mod.pop_i32)) + (export "my-mod.pop_i64" (func $my-mod.pop_i64)) + (export "my-mod.pop_f32" (func $my-mod.pop_f32)) + (export "my-mod.pop_f64" (func $my-mod.pop_f64)) + (export "cabi_realloc" (func $cabi_realloc)) + (export "cabi_free" (func $cabi_free)) + (elem (;0;) (i32.const 0) func $my-mod.init_add_import_func 26 10 8 11 9 7) + (@producers + (processed-by "walrus" "0.20.3") + ) +) \ No newline at end of file diff --git a/packages/crates/wasm-inject/tests/tests.rs b/packages/crates/wasm-inject/tests/tests.rs new file mode 100644 index 0000000000..56b77346c8 --- /dev/null +++ b/packages/crates/wasm-inject/tests/tests.rs @@ -0,0 +1,116 @@ +use walrus::{ExportItem, FunctionBuilder, Module, ModuleConfig, ValType}; +use wasm_inject::WasmInject; + +fn build_runtime_stub(m: &mut Module, prefix: &str) { + let imports_table = m.tables.add_local(0, None, ValType::Funcref); + m.exports.add( + format!("{}.imports_table", prefix).as_str(), + ExportItem::Table(imports_table), + ); + let memory = m.memories.add_local(false, 1, None); + m.exports.add("memory", ExportItem::Memory(memory)); + + fn export(module: &mut Module, name: impl AsRef, params: &[ValType], results: &[ValType]) { + let mut builder = FunctionBuilder::new(&mut module.types, params, results); + builder.name(name.as_ref().to_string()); + builder.func_body().unreachable(); + let func = builder.finish(vec![], &mut module.funcs); + + module + .exports + .add(name.as_ref(), ExportItem::Function(func)); + } + + { + use ValType::*; + let pfx = prefix; + + export(m, format!("{pfx}.call_export_func"), &[I32, I32], &[]); + export(m, format!("{pfx}.add_import_func"), &[I32, I32, I32], &[]); + export(m, format!("{pfx}.init_context"), &[], &[I32]); + export(m, format!("{pfx}.destroy_context"), &[I32], &[]); + export(m, format!("{pfx}.push_i32"), &[I32, I32], &[]); + export(m, format!("{pfx}.push_i64"), &[I64, I32], &[]); + export(m, format!("{pfx}.push_f32"), &[F32, I32], &[]); + export(m, format!("{pfx}.push_f64"), &[F64, I32], &[]); + export(m, format!("{pfx}.pop_i32"), &[I32], &[I32]); + export(m, format!("{pfx}.pop_i64"), &[I32], &[I64]); + export(m, format!("{pfx}.pop_f32"), &[I32], &[F32]); + export(m, format!("{pfx}.pop_f64"), &[I32], &[F64]); + export(m, "cabi_realloc", &[I32, I32], &[I32]); + export(m, "cabi_free", &[I32], &[]); + } +} + +fn snapshot(name: &str, wasm_bytes: &[u8]) { + use std::fs::File; + use std::io::Write; + + let path = format!("tests/snapshots/{name}.wat"); + let contents = wasmprinter::print_bytes(wasm_bytes).unwrap(); + + // Update snapshots if `UPDATE_SNAPSHOTS` env var is set or if the snapshot file doesn't exist + let update_snapshots = + std::env::var("UPDATE_SNAPSHOTS").is_ok() || !std::path::Path::new(&path).exists(); + + if update_snapshots { + let mut file = File::create(path).unwrap(); + file.write_all(contents.as_bytes()).unwrap(); + } else { + let existing = std::fs::read(path).unwrap_or_default(); + let existing = String::from_utf8_lossy(&existing); + assert_eq!(existing, contents); + } +} + +fn with_snapshot anyhow::Result<()>>(name: &str, f: F) { + let mut module = Module::with_config(ModuleConfig::new()); + build_runtime_stub(&mut module, "my-mod"); + + let mut inject = WasmInject::new(&module.emit_wasm(), "my-mod").unwrap(); + f(&mut inject).unwrap(); + let btyes = inject.run().expect("failed to run componentizer"); + + snapshot(name, &btyes); + wasmparser::validate(&btyes).expect("failed to validate wasm"); +} + +#[test] +fn import() { + with_snapshot("import", |inject| { + inject.add_import_func("t", "empty", &[], &[])?; + inject.add_import_func("t", "p_i32", &[ValType::I32], &[])?; + + inject.add_import_func("t", "p_i32_i64", &[ValType::I32, ValType::I64], &[])?; + + inject.add_import_func("t", "r_i32", &[], &[ValType::I32])?; + inject.add_import_func("t", "r_i32_i64", &[], &[ValType::I32, ValType::I64])?; + + inject.add_import_func( + "t", + "p_i32_i64_r_f32_f64", + &[ValType::I32, ValType::I64], + &[ValType::F32, ValType::F64], + )?; + Ok(()) + }); +} + +#[test] +fn export() { + with_snapshot("export", |inject| { + inject.add_export_func("empty", &[], &[])?; + inject.add_export_func("p_i32", &[ValType::I32], &[])?; + inject.add_export_func("p_i32_i64", &[ValType::I32, ValType::I64], &[])?; + + inject.add_export_func("r_i32", &[], &[ValType::I32])?; + inject.add_export_func("r_i32_i64", &[], &[ValType::I32, ValType::I64])?; + + inject.add_export_func( + "p_i32_i64_r_f32_f64", + &[ValType::I32, ValType::I64], + &[ValType::F32, ValType::F64], + )?; + Ok(()) + }); +} diff --git a/packages/crates/wit-bindgen-ruby/Cargo.toml b/packages/crates/wit-bindgen-ruby/Cargo.toml new file mode 100644 index 0000000000..eceb027645 --- /dev/null +++ b/packages/crates/wit-bindgen-ruby/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wit-bindgen-ruby" +version = "0.1.0" +edition = "2021" +authors = ["Yuta Saito "] +license = "MIT" +publish = false + +[dependencies] +wit-parser = "0.13.0" diff --git a/packages/crates/wit-bindgen-ruby/src/lib.rs b/packages/crates/wit-bindgen-ruby/src/lib.rs new file mode 100644 index 0000000000..c885e4a499 --- /dev/null +++ b/packages/crates/wit-bindgen-ruby/src/lib.rs @@ -0,0 +1,18 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +pub fn bindgen() { + let mut resolve = wit_parser::Resolve::new(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/ext/js/bindgen/.clang-format b/packages/gems/js/ext/js/bindgen/.clang-format similarity index 100% rename from ext/js/bindgen/.clang-format rename to packages/gems/js/ext/js/bindgen/.clang-format diff --git a/ext/js/bindgen/rb-js-abi-host.c b/packages/gems/js/ext/js/bindgen/rb-js-abi-host.c similarity index 100% rename from ext/js/bindgen/rb-js-abi-host.c rename to packages/gems/js/ext/js/bindgen/rb-js-abi-host.c diff --git a/ext/js/bindgen/rb-js-abi-host.h b/packages/gems/js/ext/js/bindgen/rb-js-abi-host.h similarity index 100% rename from ext/js/bindgen/rb-js-abi-host.h rename to packages/gems/js/ext/js/bindgen/rb-js-abi-host.h diff --git a/ext/js/bindgen/rb-js-abi-host.wit b/packages/gems/js/ext/js/bindgen/rb-js-abi-host.wit similarity index 100% rename from ext/js/bindgen/rb-js-abi-host.wit rename to packages/gems/js/ext/js/bindgen/rb-js-abi-host.wit diff --git a/ext/js/depend b/packages/gems/js/ext/js/depend similarity index 100% rename from ext/js/depend rename to packages/gems/js/ext/js/depend diff --git a/ext/js/extconf.rb b/packages/gems/js/ext/js/extconf.rb similarity index 100% rename from ext/js/extconf.rb rename to packages/gems/js/ext/js/extconf.rb diff --git a/ext/js/js-core.c b/packages/gems/js/ext/js/js-core.c similarity index 100% rename from ext/js/js-core.c rename to packages/gems/js/ext/js/js-core.c diff --git a/ext/witapi/bindgen/.clang-format b/packages/gems/js/ext/witapi/bindgen/.clang-format similarity index 100% rename from ext/witapi/bindgen/.clang-format rename to packages/gems/js/ext/witapi/bindgen/.clang-format diff --git a/ext/witapi/bindgen/rb-abi-guest.c b/packages/gems/js/ext/witapi/bindgen/rb-abi-guest.c similarity index 100% rename from ext/witapi/bindgen/rb-abi-guest.c rename to packages/gems/js/ext/witapi/bindgen/rb-abi-guest.c diff --git a/ext/witapi/bindgen/rb-abi-guest.h b/packages/gems/js/ext/witapi/bindgen/rb-abi-guest.h similarity index 100% rename from ext/witapi/bindgen/rb-abi-guest.h rename to packages/gems/js/ext/witapi/bindgen/rb-abi-guest.h diff --git a/ext/witapi/bindgen/rb-abi-guest.wit b/packages/gems/js/ext/witapi/bindgen/rb-abi-guest.wit similarity index 100% rename from ext/witapi/bindgen/rb-abi-guest.wit rename to packages/gems/js/ext/witapi/bindgen/rb-abi-guest.wit diff --git a/ext/witapi/depend b/packages/gems/js/ext/witapi/depend similarity index 100% rename from ext/witapi/depend rename to packages/gems/js/ext/witapi/depend diff --git a/ext/witapi/extconf.rb b/packages/gems/js/ext/witapi/extconf.rb similarity index 100% rename from ext/witapi/extconf.rb rename to packages/gems/js/ext/witapi/extconf.rb diff --git a/ext/witapi/witapi-core.c b/packages/gems/js/ext/witapi/witapi-core.c similarity index 100% rename from ext/witapi/witapi-core.c rename to packages/gems/js/ext/witapi/witapi-core.c diff --git a/packages/gems/js/js.gemspec b/packages/gems/js/js.gemspec new file mode 100644 index 0000000000..a62cd88f3e --- /dev/null +++ b/packages/gems/js/js.gemspec @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require_relative "../../../lib/ruby_wasm/version" + +Gem::Specification.new do |spec| + spec.name = "js" + spec.version = RubyWasm::VERSION + spec.authors = ["Yuta Saito"] + spec.email = ["kateinoigakukun@gmail.com"] + + spec.summary = "JavaScript bindings for ruby.wasm" + + spec.license = "MIT" + spec.required_ruby_version = ">= 3.2.0" + + spec.files = Dir.chdir(__dir__) do + `git ls-files -z`.split("\x0").reject do |f| + (File.expand_path(f) == __FILE__) || + f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile]) + end + end + spec.require_paths = ["lib"] + spec.extensions = ["ext/js/extconf.rb", "ext/witapi/extconf.rb"] +end diff --git a/ext/js/lib/js.rb b/packages/gems/js/lib/js.rb similarity index 100% rename from ext/js/lib/js.rb rename to packages/gems/js/lib/js.rb diff --git a/ext/js/lib/js/array.rb b/packages/gems/js/lib/js/array.rb similarity index 100% rename from ext/js/lib/js/array.rb rename to packages/gems/js/lib/js/array.rb diff --git a/ext/js/lib/js/hash.rb b/packages/gems/js/lib/js/hash.rb similarity index 100% rename from ext/js/lib/js/hash.rb rename to packages/gems/js/lib/js/hash.rb diff --git a/ext/js/lib/js/nil_class.rb b/packages/gems/js/lib/js/nil_class.rb similarity index 100% rename from ext/js/lib/js/nil_class.rb rename to packages/gems/js/lib/js/nil_class.rb diff --git a/ext/js/lib/js/require_remote.rb b/packages/gems/js/lib/js/require_remote.rb similarity index 100% rename from ext/js/lib/js/require_remote.rb rename to packages/gems/js/lib/js/require_remote.rb diff --git a/ext/js/lib/js/require_remote/evaluator.rb b/packages/gems/js/lib/js/require_remote/evaluator.rb similarity index 100% rename from ext/js/lib/js/require_remote/evaluator.rb rename to packages/gems/js/lib/js/require_remote/evaluator.rb diff --git a/ext/js/lib/js/require_remote/url_resolver.rb b/packages/gems/js/lib/js/require_remote/url_resolver.rb similarity index 100% rename from ext/js/lib/js/require_remote/url_resolver.rb rename to packages/gems/js/lib/js/require_remote/url_resolver.rb diff --git a/packages/npm-packages/ruby-wasm-wasi/Gemfile b/packages/npm-packages/ruby-wasm-wasi/Gemfile new file mode 100644 index 0000000000..5fc9e696ce --- /dev/null +++ b/packages/npm-packages/ruby-wasm-wasi/Gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "js", path: "../../gems/js" +gem "ruby_wasm", path: "../../../" diff --git a/packages/npm-packages/ruby-wasm-wasi/Gemfile.lock b/packages/npm-packages/ruby-wasm-wasi/Gemfile.lock new file mode 100644 index 0000000000..821cb70969 --- /dev/null +++ b/packages/npm-packages/ruby-wasm-wasi/Gemfile.lock @@ -0,0 +1,24 @@ +PATH + remote: ../../.. + specs: + ruby_wasm (2.4.1) + +PATH + remote: ../../gems/js + specs: + js (2.4.1) + +GEM + remote: https://rubygems.org/ + specs: + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + js! + ruby_wasm! + +BUNDLED WITH + 2.5.3 diff --git a/ruby_wasm.gemspec b/ruby_wasm.gemspec index 3a8d73cb00..74ee69f214 100644 --- a/ruby_wasm.gemspec +++ b/ruby_wasm.gemspec @@ -23,8 +23,9 @@ Gem::Specification.new do |spec| `git ls-files -z`.split("\x0").reject do |f| (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) end - end + end + ["rubies/ruby-head-wasm32-unknown-wasi-full.tar.gz"] spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] + spec.extensions = ["ext/ruby_wasm/Cargo.toml"] end diff --git a/sig/ruby_wasm/cli.rbs b/sig/ruby_wasm/cli.rbs new file mode 100644 index 0000000000..88ebe7749b --- /dev/null +++ b/sig/ruby_wasm/cli.rbs @@ -0,0 +1,35 @@ +module RubyWasm + class CLI + DEFAULT_RUBIES_DIR: string + + @stdout: IO + @stderr: IO + @rubies_dir: string + + def initialize: (stdout: IO, stderr: IO, rubies_dir: string) -> void + + def build: (Array[String] args) -> void + + private def do_build: (String tarball, RubyWasm::BuildExecutor executor, String output_dir, Hash[untyped, untyped] options) -> void + end + + class RubyWasmExt + def self.preinitialize: (Array[Integer] module_bytes) -> Array[Integer] + + class WasmInject + def initialize: (Array[Integer] module_bytes) -> void + + def add_import_func: (String module_name, String func_name, Array[Symbol] params, Array[Symbol] return_type) -> void + + def run: () -> Array[Integer] + end + + class WasiVfs + def initialize: () -> void + + def map_dir: (String guest_path, String host_path) -> void + + def pack: (Array[Integer] module_bytes) -> Array[Integer] + end + end +end diff --git a/tasks/gem.rake b/tasks/gem.rake new file mode 100644 index 0000000000..acf41b7003 --- /dev/null +++ b/tasks/gem.rake @@ -0,0 +1,12 @@ +require "bundler/gem_tasks" +require "rake/testtask" + +Rake::TestTask.new(:test) do |t| + t.libs << "test" + t.libs << "lib" + t.test_files = FileList["test/**/test_*.rb"] +end + +require "rb_sys/extensiontask" + +RbSys::ExtensionTask.new("ruby_wasm") { |ext| ext.lib_dir = "lib/ruby_wasm" } From 9fdc2a56d14b998c4b77e199a0c2c7b23002fdce Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 05:23:02 +0000 Subject: [PATCH 02/44] Remove experimental binary-editing approach I've tried two approaches to implement the Component Model for Ruby. The first one is the *binary-editing* approach and the second one is *extension-library* approach. The basic idea of binary-editing* is to inject import/export sections into a preview1 compatible .wasm binary and turn it into a Component by `wasm-tool component new` command. The approach was good for binary reusability. The binary before injection can be used as a standalone WASI preview1 binary. Therefore user can use a pre-built .wasm binary and just inject the import/export sections to turn it into a Component. However, considering the future dynamic linking support, the approach introduces a bunch of complexity. In the dynamic linking era, an extension library will be able to provide a WIT interface and a Ruby core interpreter should not import or export anything except for WASI. The binary editing approach needs to edit the Ruby core interpreter binary every time when WIT world changes, so the build process would be different from the one of dynamically linked extension library. I decided to go to the extension library approach for mainainability. The static linking build is just for a compatibility with the existing Wasm runtimes, so I don't want to pay much effort for maintining it. The extention library approach uses almost the same build process for static linking and dynamic linking, so it's easier to maintain and we can focus on the dynamic linking support. --- Cargo.lock | 98 +---- Cargo.toml | 2 +- Rakefile | 5 - ext/c10zrt/c10zrt.c | 229 ----------- ext/c10zrt/c10zrt_core.s | 13 - ext/c10zrt/depend | 9 - ext/c10zrt/extconf.rb | 5 - ext/ruby_wasm/Cargo.toml | 1 - ext/ruby_wasm/src/lib.rs | 75 +--- ext/ruby_wasm/src/types.rs | 42 -- packages/crates/wasm-inject/Cargo.toml | 15 - .../crates/wasm-inject/src/bin/wasm-inject.rs | 29 -- packages/crates/wasm-inject/src/lib.rs | 362 ------------------ .../wasm-inject/tests/snapshots/export.wat | 281 -------------- .../wasm-inject/tests/snapshots/import.wat | 251 ------------ packages/crates/wasm-inject/tests/tests.rs | 116 ------ 16 files changed, 9 insertions(+), 1524 deletions(-) delete mode 100644 ext/c10zrt/c10zrt.c delete mode 100644 ext/c10zrt/c10zrt_core.s delete mode 100644 ext/c10zrt/depend delete mode 100644 ext/c10zrt/extconf.rb delete mode 100644 ext/ruby_wasm/src/types.rs delete mode 100644 packages/crates/wasm-inject/Cargo.toml delete mode 100644 packages/crates/wasm-inject/src/bin/wasm-inject.rs delete mode 100644 packages/crates/wasm-inject/src/lib.rs delete mode 100644 packages/crates/wasm-inject/tests/snapshots/export.wat delete mode 100644 packages/crates/wasm-inject/tests/snapshots/import.wat delete mode 100644 packages/crates/wasm-inject/tests/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 077281cc5a..7e6d70cde3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.3", + "gimli", ] [[package]] @@ -347,7 +347,7 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli 0.27.3", + "gimli", "hashbrown 0.13.2", "log", "regalloc2", @@ -700,17 +700,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.27.3" @@ -1339,7 +1328,6 @@ dependencies = [ "magnus", "wasi-cap-std-sync", "wasi-vfs-cli", - "wasm-inject", "wasmtime-wasi", "wizer 3.0.0", ] @@ -1751,34 +1739,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "walrus" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7" -dependencies = [ - "anyhow", - "gimli 0.26.2", - "id-arena", - "leb128", - "log", - "walrus-macro", - "wasm-encoder 0.29.0", - "wasmparser 0.80.2", -] - -[[package]] -name = "walrus-macro" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1835,6 +1795,7 @@ version = "0.4.0" dependencies = [ "anyhow", "structopt", + "wasm-encoder 0.38.1", "wasmparser 0.106.0", "wizer 3.0.1", ] @@ -1848,15 +1809,6 @@ dependencies = [ "leb128", ] -[[package]] -name = "wasm-encoder" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881" -dependencies = [ - "leb128", -] - [[package]] name = "wasm-encoder" version = "0.38.1" @@ -1866,22 +1818,6 @@ dependencies = [ "leb128", ] -[[package]] -name = "wasm-inject" -version = "0.1.0" -dependencies = [ - "anyhow", - "walrus", - "wasmparser 0.118.1", - "wasmprinter", -] - -[[package]] -name = "wasmparser" -version = "0.80.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b" - [[package]] name = "wasmparser" version = "0.103.0" @@ -1902,26 +1838,6 @@ dependencies = [ "url", ] -[[package]] -name = "wasmparser" -version = "0.118.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" -dependencies = [ - "indexmap 2.1.0", - "semver", -] - -[[package]] -name = "wasmprinter" -version = "0.2.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d027eb8294904fc715ac0870cebe6b0271e96b90605ee21511e7565c4ce568c" -dependencies = [ - "anyhow", - "wasmparser 0.118.1", -] - [[package]] name = "wasmtime" version = "9.0.4" @@ -2020,7 +1936,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.27.3", + "gimli", "log", "object", "target-lexicon", @@ -2040,7 +1956,7 @@ dependencies = [ "cranelift-codegen", "cranelift-control", "cranelift-native", - "gimli 0.27.3", + "gimli", "object", "target-lexicon", "wasmtime-environ", @@ -2054,7 +1970,7 @@ checksum = "9ec815d01a8d38aceb7ed4678f9ba551ae6b8a568a63810ac3ad9293b0fd01c8" dependencies = [ "anyhow", "cranelift-entity", - "gimli 0.27.3", + "gimli", "indexmap 1.9.3", "log", "object", @@ -2089,7 +2005,7 @@ dependencies = [ "bincode", "cfg-if", "cpp_demangle", - "gimli 0.27.3", + "gimli", "ittapi", "log", "object", diff --git a/Cargo.toml b/Cargo.toml index 6f04dcd9c7..4448fa6ca1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,5 @@ # in the ext/ directory. [workspace] -members = ["./ext/ruby_wasm", "packages/crates/wasm-inject", "packages/crates/wit-bindgen-ruby"] +members = ["./ext/ruby_wasm", "packages/crates/wit-bindgen-ruby"] resolver = "2" diff --git a/Rakefile b/Rakefile index 195f5c113c..fb1655fdc6 100644 --- a/Rakefile +++ b/Rakefile @@ -51,11 +51,6 @@ BUILD_PROFILES = { default_exts: "", user_exts: [] }, - "minimal-component-debug" => { - debug: true, - default_exts: "", - user_exts: %w[c10zrt] - }, "full" => { debug: false, default_exts: FULL_EXTS, diff --git a/ext/c10zrt/c10zrt.c b/ext/c10zrt/c10zrt.c deleted file mode 100644 index 8cefb4b461..0000000000 --- a/ext/c10zrt/c10zrt.c +++ /dev/null @@ -1,229 +0,0 @@ - -#include "ruby.h" -#include - -extern void __wasm_call_ctors(void); -extern void __wasm_call_dtors(void); - -static void *initialized_iseq; - -int rb_wasm_rt_start(int(main)(int argc, char **argv), int argc, char **argv); -static int wizer_initialize_internal(int argc, char **argv) { - ruby_sysinit(&argc, &argv); - ruby_init(); - initialized_iseq = ruby_options(argc, argv); - return 0; -} - -#include - -void debug_dump_fs_tree(const char *path) { - // Dump the file system tree for debugging purposes like `tree` command using - // libc's dirent.h - - DIR *dir = opendir(path); - if (dir == NULL) { - printf("Failed to open directory: %s\n", path); - return; - } - struct dirent *entry; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_DIR) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { - continue; - } - printf("%s/%s\n", path, entry->d_name); - char *subpath = malloc(strlen(path) + strlen(entry->d_name) + 2); - sprintf(subpath, "%s/%s", path, entry->d_name); - debug_dump_fs_tree(subpath); - free(subpath); - } else { - printf("%s/%s\n", path, entry->d_name); - } - } -} - -__attribute__((export_name("ruby.wizer.initialize"))) void -__ruby_wizer_initialize() { - __wasm_call_ctors(); - - debug_dump_fs_tree("/exe"); - int err; - size_t argc; - size_t argv_buf_size; - err = __wasi_args_sizes_get(&argc, &argv_buf_size); - if (err != 0) { - __wasi_proc_exit(err); - } - char *argv_buf = malloc(argv_buf_size); - if (argv_buf == NULL) { - __wasi_proc_exit(__WASI_ERRNO_NOMEM); - } - char **argv = malloc(argc * sizeof(char *)); - if (argv == NULL) { - __wasi_proc_exit(__WASI_ERRNO_NOMEM); - } - err = __wasi_args_get((uint8_t **)argv, (uint8_t *)argv_buf); - if (err != 0) { - __wasi_proc_exit(err); - } - rb_wasm_rt_start(wizer_initialize_internal, argc, argv); -} - -static int wizer_run_internal(int argc, char **argv) { - return ruby_run_node(initialized_iseq); -} - -__attribute__((export_name("ruby.wizer.resume"))) void __ruby_wizer_resume() { - int ret = rb_wasm_rt_start(wizer_run_internal, 0, NULL); - __wasm_call_dtors(); - if (ret != 0) { - __wasi_proc_exit(ret); - } -} - -VALUE rb_mWaddle; - -typedef struct { - const char *module_name; - const char *name; - void *func; -} import_func_entry; - -static size_t import_funcs_size = 0; -static import_func_entry *import_funcs; - -__attribute__((export_name("ruby.c10zrt.add_import_func"))) void -ruby_c10zrt_add_import_func(const char *module_name, const char *name, - void *func) { - static size_t import_funcs_capacity = 0; - if (import_funcs_capacity == 0) { - import_funcs_capacity = 16; - import_funcs = malloc(import_funcs_capacity * sizeof(import_func_entry)); - } else if (import_funcs_size == import_funcs_capacity) { - import_funcs_capacity *= 2; - import_funcs = realloc(import_funcs, - import_funcs_capacity * sizeof(import_func_entry)); - } - import_funcs[import_funcs_size].module_name = module_name; - import_funcs[import_funcs_size].name = name; - import_funcs[import_funcs_size].func = func; - import_funcs_size++; -} - -static import_func_entry *find_import_func(const char *module_name, - const char *name) { - for (size_t i = 0; i < import_funcs_size; i++) { - if (strcmp(import_funcs[i].module_name, module_name) == 0 && - strcmp(import_funcs[i].name, name) == 0) { - return &import_funcs[i]; - } - } - return NULL; -} - -__attribute__((export_name("ruby.c10zrt.init_context"))) VALUE -ruby_c10zrt_init_context(void) { - return rb_ary_new(); -} - -__attribute__((export_name("ruby.c10zrt.call_export_func"))) void -ruby_c10zrt_call_export_func(const char *name, VALUE ctx) { - VALUE func = rb_funcall(rb_mWaddle, rb_intern("export_func"), 1, - rb_str_new_cstr(name)); - rb_funcall(func, rb_intern("call"), 1, ctx); -} - -__attribute__((export_name("ruby.c10zrt.push_i32"))) void -ruby_c10zrt_push_i32(int32_t value, VALUE ctx) { - rb_ary_push(ctx, INT2NUM(value)); -} - -__attribute__((export_name("ruby.c10zrt.push_i64"))) void -ruby_c10zrt_push_i64(int64_t value, VALUE ctx) { - rb_ary_push(ctx, LL2NUM(value)); -} - -__attribute__((export_name("ruby.c10zrt.push_f32"))) void -ruby_c10zrt_push_f32(float value, VALUE ctx) { - rb_ary_push(ctx, DBL2NUM(value)); -} - -__attribute__((export_name("ruby.c10zrt.push_f64"))) void -ruby_c10zrt_push_f64(double value, VALUE ctx) { - rb_ary_push(ctx, DBL2NUM(value)); -} - -__attribute__((export_name("ruby.c10zrt.pop_i32"))) int32_t -ruby_c10zrt_pop_i32(VALUE ctx) { - return NUM2INT(rb_ary_pop(ctx)); -} - -__attribute__((export_name("ruby.c10zrt.pop_i64"))) int64_t -ruby_c10zrt_pop_i64(VALUE ctx) { - return NUM2LL(rb_ary_pop(ctx)); -} - -__attribute__((export_name("ruby.c10zrt.pop_f32"))) float -ruby_c10zrt_pop_f32(VALUE ctx) { - return NUM2DBL(rb_ary_pop(ctx)); -} - -__attribute__((export_name("ruby.c10zrt.pop_f64"))) double -ruby_c10zrt_pop_f64(VALUE ctx) { - return NUM2DBL(rb_ary_pop(ctx)); -} - -__attribute__((export_name("cabi_realloc"))) void *cabi_realloc(void *ptr, - size_t size) { - return realloc(ptr, size); -} - -__attribute__((export_name("cabi_free"))) void cabi_free(void *ptr) { - free(ptr); -} - -extern void ruby_c10zrt_invoke_import(void *func, VALUE *argv); - -static VALUE rb_call_wasm_import(int argc, VALUE *argv, VALUE self) { - // Exctract (module_name, name, *args) from argv - if (argc < 2) { - rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2+)", - argc); - } - VALUE module_name = argv[0]; - VALUE name = argv[1]; - VALUE *args = argv + 2; - argc -= 2; - - if (TYPE(module_name) != T_STRING) { - rb_raise(rb_eTypeError, "wrong argument type %s (expected String)", - rb_obj_classname(module_name)); - } - - if (TYPE(name) != T_STRING) { - rb_raise(rb_eTypeError, "wrong argument type %s (expected String)", - rb_obj_classname(name)); - } - - const char *module_name_cstr = RSTRING_PTR(module_name); - const char *name_cstr = RSTRING_PTR(name); - - import_func_entry *entry = find_import_func(module_name_cstr, name_cstr); - if (entry == NULL) { - rb_raise(rb_eRuntimeError, "import function %s.%s is not registered", - module_name_cstr, name_cstr); - } - - ruby_c10zrt_invoke_import(entry->func, args); - return Qnil; -} - -void Init_c10zrt() { - rb_mWaddle = rb_define_module("Waddle"); - rb_define_module_function(rb_mWaddle, "call_wasm_import", rb_call_wasm_import, - -1); - - // 0 is reserved for import registration function - ruby_c10zrt_invoke_import(0, 0); -} diff --git a/ext/c10zrt/c10zrt_core.s b/ext/c10zrt/c10zrt_core.s deleted file mode 100644 index 9c4f5c8b0c..0000000000 --- a/ext/c10zrt/c10zrt_core.s +++ /dev/null @@ -1,13 +0,0 @@ -.globl ruby.c10zrt.imports_table -ruby.c10zrt.imports_table: - .tabletype ruby.c10zrt.imports_table, funcref -.export_name ruby.c10zrt.imports_table, ruby.c10zrt.imports_table - -.globl ruby_c10zrt_invoke_import -.type ruby_c10zrt_invoke_import, @function -ruby_c10zrt_invoke_import: - .functype ruby_c10zrt_invoke_import (i32, i32) -> () - local.get 1 - local.get 0 - call_indirect ruby.c10zrt.imports_table, (i32) -> () - end_function diff --git a/ext/c10zrt/depend b/ext/c10zrt/depend deleted file mode 100644 index 65e2c17f9c..0000000000 --- a/ext/c10zrt/depend +++ /dev/null @@ -1,9 +0,0 @@ -link.filelist: - echo $(foreach obj,$(OBJS),$(abspath $(obj))) > $@ - -c10zrt.a: link.filelist - -c10zrt.o: $(srcdir)/c10zrt.c -c10zrt_core.o: $(srcdir)/c10zrt_core.s -# reference-types feature is required to have multiple tables - $(CC) -mreference-types -target wasm32-unknown-unknown-wasm -c $< -o $@ diff --git a/ext/c10zrt/extconf.rb b/ext/c10zrt/extconf.rb deleted file mode 100644 index 2a88bd590d..0000000000 --- a/ext/c10zrt/extconf.rb +++ /dev/null @@ -1,5 +0,0 @@ -require "mkmf" -$objs = %w[c10zrt.o c10zrt_core.o] -$CFLAGS += " -mreference-types" - -create_makefile("c10zrt") diff --git a/ext/ruby_wasm/Cargo.toml b/ext/ruby_wasm/Cargo.toml index 0858e5d39f..912021e309 100644 --- a/ext/ruby_wasm/Cargo.toml +++ b/ext/ruby_wasm/Cargo.toml @@ -14,6 +14,5 @@ magnus = "0.6.2" wizer = "3.0.0" wasmtime-wasi = "9.0.4" wasi-cap-std-sync = "9.0.4" -wasm-inject = { path = "../../packages/crates/wasm-inject" } # wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", tag = "v0.5.0" } wasi-vfs-cli = { path = "/home/katei/ghq/github.com/kateinoigakukun/wasi-vfs/crates/wasi-vfs-cli" } diff --git a/ext/ruby_wasm/src/lib.rs b/ext/ruby_wasm/src/lib.rs index 838da9ea9d..cd907aa22f 100644 --- a/ext/ruby_wasm/src/lib.rs +++ b/ext/ruby_wasm/src/lib.rs @@ -1,14 +1,11 @@ -mod types; - use std::path::PathBuf; use magnus::{ eval, exception, function, method, prelude::*, value::{self, InnerValue}, - wrap, Error, ExceptionClass, RArray, RModule, Ruby, + wrap, Error, ExceptionClass, RModule, Ruby, }; -use types::ValType; use wizer::Wizer; static RUBY_WASM: value::Lazy = @@ -30,67 +27,6 @@ fn preinit(core_module: Vec) -> Result, Error> { .map_err(|e| Error::new(rbwasm_error, format!("failed to run wizer: {}", e))) } -#[wrap(class = "RubyWasmExt::WasmInject")] -struct WasmInject(std::cell::RefCell); - -impl WasmInject { - fn new(core_module: Vec) -> Result { - let inner = wasm_inject::WasmInject::new(&core_module, "ruby.c10zrt").map_err(|e| { - Error::new( - exception::standard_error(), - format!("failed to create componentizer: {}", e), - ) - })?; - Ok(Self(std::cell::RefCell::new(inner))) - } - - fn add_export_func(&self, name: String, params: RArray, results: RArray) -> Result<(), Error> { - let params = ValType::vec_from_rarray(params)?; - let results = ValType::vec_from_rarray(results)?; - self.0 - .borrow_mut() - .add_export_func(&name, ¶ms, &results) - .map_err(|e| { - Error::new( - exception::standard_error(), - format!("failed to add export func: {}", e), - ) - })?; - Ok(()) - } - - fn add_import_func( - &self, - module: String, - name: String, - params: RArray, - results: RArray, - ) -> Result<(), Error> { - let params = ValType::vec_from_rarray(params)?; - let results = ValType::vec_from_rarray(results)?; - self.0 - .borrow_mut() - .add_import_func(&module, &name, ¶ms, &results) - .map_err(|e| { - Error::new( - exception::standard_error(), - format!("failed to add import func: {}", e), - ) - })?; - Ok(()) - } - - fn run(&self) -> Result, Error> { - let wasm = self.0.borrow_mut().run().map_err(|e| { - Error::new( - exception::standard_error(), - format!("failed to run componentizer: {}", e), - ) - })?; - Ok(wasm) - } -} - struct WasiVfsInner { map_dirs: Vec<(PathBuf, PathBuf)>, } @@ -125,18 +61,9 @@ fn init(ruby: &Ruby) -> Result<(), Error> { module.define_singleton_method("preinitialize", function!(preinit, 1))?; - let wasm_inject = module.define_class("WasmInject", ruby.class_object())?; - wasm_inject.define_singleton_method("new", function!(WasmInject::new, 1))?; - wasm_inject.define_method("add_export_func", method!(WasmInject::add_export_func, 3))?; - wasm_inject.define_method("add_import_func", method!(WasmInject::add_import_func, 4))?; - wasm_inject.define_method("run", method!(WasmInject::run, 0))?; - let wasi_vfs = module.define_class("WasiVfs", ruby.class_object())?; wasi_vfs.define_singleton_method("new", function!(WasiVfs::new, 0))?; wasi_vfs.define_method("map_dir", method!(WasiVfs::map_dir, 2))?; wasi_vfs.define_method("pack", method!(WasiVfs::pack, 1))?; - - let val_type = module.define_class("ValType", ruby.class_object())?; - val_type.define_singleton_method("new", function!(ValType::new, 1))?; Ok(()) } diff --git a/ext/ruby_wasm/src/types.rs b/ext/ruby_wasm/src/types.rs deleted file mode 100644 index 92023e415f..0000000000 --- a/ext/ruby_wasm/src/types.rs +++ /dev/null @@ -1,42 +0,0 @@ -use magnus::{exception, wrap, Error, RArray, Symbol}; - -#[wrap(class = "ComponentizerExt::ValType")] -pub(crate) struct ValType(wasm_inject::walrus::ValType); - -impl ValType { - pub(crate) fn new(ty: Symbol) -> Result { - let ty = match ty.to_string().as_str() { - "i32" => wasm_inject::walrus::ValType::I32, - "i64" => wasm_inject::walrus::ValType::I64, - "f32" => wasm_inject::walrus::ValType::F32, - "f64" => wasm_inject::walrus::ValType::F64, - "v128" => wasm_inject::walrus::ValType::V128, - "externref" => wasm_inject::walrus::ValType::Externref, - "funcref" => wasm_inject::walrus::ValType::Funcref, - _ => { - return Err(Error::new( - exception::standard_error(), - format!("invalid type: {}", ty), - )) - } - }; - Ok(Self(ty)) - } - - pub(crate) fn vec_from_rarray(tys: RArray) -> Result, Error> { - let tys = unsafe { tys.as_slice() } - .iter() - .map(|v| -> Result { - let sym = Symbol::from_value(*v).ok_or_else(|| { - Error::new( - exception::standard_error(), - format!("invalid type: {}", v), - ) - })?; - let ty = ValType::new(sym)?; - Ok(ty.0) - }) - .collect::, _>>()?; - Ok(tys) - } -} diff --git a/packages/crates/wasm-inject/Cargo.toml b/packages/crates/wasm-inject/Cargo.toml deleted file mode 100644 index 75a17f590c..0000000000 --- a/packages/crates/wasm-inject/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "wasm-inject" -version = "0.1.0" -edition = "2021" -authors = ["Yuta Saito "] -license = "MIT" -publish = false - -[dependencies] -walrus = "0.20.3" -anyhow = "1.0.75" - -[dev-dependencies] -wasmparser = "0.118.1" -wasmprinter = "0.2.75" diff --git a/packages/crates/wasm-inject/src/bin/wasm-inject.rs b/packages/crates/wasm-inject/src/bin/wasm-inject.rs deleted file mode 100644 index 4df2290741..0000000000 --- a/packages/crates/wasm-inject/src/bin/wasm-inject.rs +++ /dev/null @@ -1,29 +0,0 @@ -fn main() { - // Parse CLI args - // ``` - // wasm-inject - // ``` - - let args: Vec = std::env::args().collect(); - let file = args.get(1).expect("missing file"); - let output = args.get(2).expect("missing output"); - componentize(file, output); -} - -fn componentize(file: &str, output: &str) { - let core_module = - std::fs::read(file).unwrap_or_else(|_| panic!("failed to read file: {}", file)); - let mut c10zer = wasm_inject::WasmInject::new(&core_module, "ruby.c10zrt") - .expect("failed to create componentizer"); - - c10zer - .add_import_func("my_module", "hello", &[], &[]) - .expect("failed to add import func"); - c10zer - .add_export_func("greet", &[], &[]) - .expect("failed to add export func"); - - let wasm = c10zer.run().expect("failed to run componentizer"); - - std::fs::write(output, wasm).expect("failed to write output"); -} diff --git a/packages/crates/wasm-inject/src/lib.rs b/packages/crates/wasm-inject/src/lib.rs deleted file mode 100644 index 6c6dcbcff0..0000000000 --- a/packages/crates/wasm-inject/src/lib.rs +++ /dev/null @@ -1,362 +0,0 @@ -use std::collections::HashMap; - -use anyhow::{anyhow, Ok, Result}; -use walrus::{ir::Value, ModuleConfig, ValType}; - -pub use walrus; - -struct InjectedImportFunc { - name: String, - trampoline: walrus::FunctionId, -} -pub struct WasmInject { - prefix: String, - module: walrus::Module, - injected_imports: HashMap>, - imports_table: walrus::TableId, -} - -mod abi { - // Canonical ABI - pub(crate) const CABI_REALLOC: &str = "cabi_realloc"; - pub(crate) const CABI_FREE: &str = "cabi_free"; - - // wasm-inject ABI - // fn push_{T}(val: T, ctx: ptr) -> () - pub(crate) const PUSH_I32: &str = "push_i32"; - pub(crate) const PUSH_I64: &str = "push_i64"; - pub(crate) const PUSH_F32: &str = "push_f32"; - pub(crate) const PUSH_F64: &str = "push_f64"; - // fn pop_{T}(ctx: ptr) -> T - pub(crate) const POP_I32: &str = "pop_i32"; - pub(crate) const POP_I64: &str = "pop_i64"; - pub(crate) const POP_F32: &str = "pop_f32"; - pub(crate) const POP_F64: &str = "pop_f64"; - // fn call_export_func(name: *const u8, ctx: ptr) -> () - pub(crate) const CALL_EXPORT_FUNC: &str = "call_export_func"; - // fn add_import_func(module_name: *const u8, name: *const u8, func: ptr) -> () - pub(crate) const ADD_IMPORT_FUNC: &str = "add_import_func"; - // fn init_context() -> ptr - pub(crate) const INIT_CONTEXT: &str = "init_context"; - // fn destroy_context(ctx: ptr) -> () - pub(crate) const DESTROY_CONTEXT: &str = "destroy_context"; -} - -impl WasmInject { - pub fn new(core_module: &[u8], prefix: &str) -> Result { - let mut module_config = ModuleConfig::new(); - module_config.generate_name_section(true); - let module = module_config.parse(core_module)?; - let export_name = format!("{}.imports_table", prefix); - let imports_table = Self::imports_table(&module, &export_name)?; - Ok(Self { - prefix: prefix.to_string(), - module, - injected_imports: HashMap::new(), - imports_table, - }) - } - - fn get_export_func(&self, name: &str) -> Result { - self.module - .exports - .get_func(format!("{}.{}", self.prefix, name)) - } - - pub fn add_export_func( - &mut self, - name: &str, - params: &[ValType], - results: &[ValType], - ) -> Result<()> { - let mut func = walrus::FunctionBuilder::new(&mut self.module.types, params, results); - func.name(name.to_string()); - let mut body = func.func_body(); - - let memory = Self::get_memory(&self.module)?; - - let name_ptr = self.module.locals.add(ValType::I32); - let name_bytes = [name.as_bytes(), b"\0"].concat(); - - let cabi_realloc = self.get_cabi_func(abi::CABI_REALLOC)?; - - body.i32_const(0) - .i32_const(name_bytes.len() as i32) - .call(cabi_realloc) - .local_set(name_ptr); - - // 1. Allocate a string for the name - store_string_at(&mut body, memory, &name_bytes, name_ptr, 0); - - let call_export_func = self.get_export_func(abi::CALL_EXPORT_FUNC)?; - let init_ctx = self.get_export_func(abi::INIT_CONTEXT)?; - - let ctx = self.module.locals.add(ValType::I32); - - // 2. Allocate a stack context - body.call(init_ctx).local_set(ctx); - - // 3. Push parameters to the stack - let args = params.iter().map(|p| { - self.module.locals.add(*p) - }).collect::>(); - for (idx, param) in params.iter().enumerate().rev() { - let local = args[idx]; - match param { - ValType::I32 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_I32)?), - ValType::I64 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_I64)?), - ValType::F32 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_F32)?), - ValType::F64 => body.local_get(local).local_get(ctx).call(self.get_export_func(abi::PUSH_F64)?), - _ => unimplemented!("unsupported param type: {:?}", param), - }; - } - - // 4. Call the exposed function by function name - body.local_get(ctx) - .local_get(name_ptr) - .call(call_export_func); - - // 5. Deallocate the function name - body.local_get(name_ptr).call(self.get_cabi_func(abi::CABI_FREE)?); - - // 6. Pop results from the stack - for result in results.iter() { - match result { - ValType::I32 => body.local_get(ctx).call(self.get_export_func(abi::POP_I32)?), - ValType::I64 => body.local_get(ctx).call(self.get_export_func(abi::POP_I64)?), - ValType::F32 => body.local_get(ctx).call(self.get_export_func(abi::POP_F32)?), - ValType::F64 => body.local_get(ctx).call(self.get_export_func(abi::POP_F64)?), - _ => unimplemented!("unsupported result type: {:?}", result), - }; - } - - // 7. Deallocate the stack context - body.local_get(ctx).call(self.get_export_func(abi::DESTROY_CONTEXT)?); - - body.return_(); - - let func = func.finish(args, &mut self.module.funcs); - self.module.exports.add(name, func); - - Ok(()) - } - - pub fn add_import_func( - &mut self, - module: &str, - name: &str, - params: &[ValType], - results: &[ValType], - ) -> Result<()> { - let ty = self - .module - .types - .find(params, results) - .unwrap_or_else(|| self.module.types.add(params, results)); - self.add_import_func_with_tyid(module, name, ty) - } - - fn add_import_func_with_tyid( - &mut self, - module: &str, - name: &str, - ty: walrus::TypeId, - ) -> Result<()> { - let (import_func, _) = self.module.add_import_func(module, name, ty); - let ty = self.module.types.get(ty); - let params = ty.params().to_vec(); - let results = ty.results().to_vec(); - - let ctx_ty = ValType::I32; - let mut builder = walrus::FunctionBuilder::new(&mut self.module.types, &[ctx_ty], &[]); - let ctx = self.module.locals.add(ctx_ty); - let mut body = builder.func_body(); - - // 1. Pop parameters from the stack - for param in params.iter() { - match param { - ValType::I32 => body.local_get(ctx).call(self.get_export_func(abi::POP_I32)?), - ValType::I64 => body.local_get(ctx).call(self.get_export_func(abi::POP_I64)?), - ValType::F32 => body.local_get(ctx).call(self.get_export_func(abi::POP_F32)?), - ValType::F64 => body.local_get(ctx).call(self.get_export_func(abi::POP_F64)?), - _ => unimplemented!("unsupported param type: {:?}", param), - }; - } - - // 2. Call the import function - body.call(import_func); - - // 3. Push results to the stack - for result in results.iter().rev() { - match result { - ValType::I32 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_I32)?), - ValType::I64 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_I64)?), - ValType::F32 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_F32)?), - ValType::F64 => body.local_get(ctx).call(self.get_export_func(abi::PUSH_F64)?), - _ => unimplemented!("unsupported result type: {:?}", result), - }; - } - - let func = builder.finish(vec![ctx], &mut self.module.funcs); - self.injected_imports - .entry(module.to_string()) - .or_default() - .push(InjectedImportFunc { - name: name.to_string(), - trampoline: func, - }); - Ok(()) - } - - pub fn run(&mut self) -> Result> { - // 1. Create a new registrar function - let registrar = self.make_import_registrar()?; - // 2. Create a new Element segment with the import trampolines - let mut members = vec![Some(registrar)]; - for (_, entries) in self.injected_imports.iter() { - for entry in entries { - members.push(Some(entry.trampoline)); - } - } - let members_len = members.len(); - - self.module.elements.add( - walrus::ElementKind::Active { - table: self.imports_table, - offset: walrus::InitExpr::Value(Value::I32(0)), - }, - ValType::Funcref, - members, - ); - self.module.tables.get_mut(self.imports_table).initial = members_len as u32; - Ok(self.module.emit_wasm()) - } - - fn make_import_registrar(&mut self) -> Result { - let params = [ValType::I32]; // unused context to be called by ruby_c10zrt_invoke_import - let mut builder = walrus::FunctionBuilder::new(&mut self.module.types, ¶ms, &[]); - builder.name(format!("{}.init_add_import_func", self.prefix)); - let mut body = builder.func_body(); - - let cabi_realloc = self.get_cabi_func(abi::CABI_REALLOC)?; - let add_import_func = self.get_export_func(abi::ADD_IMPORT_FUNC)?; - let memory = Self::get_memory(&self.module)?; - let module_name_ptr = self.module.locals.add(ValType::I32); - let name_ptr = self.module.locals.add(ValType::I32); - - let mut element_index = 1; // 0 is preserved for this registrar function - - for (module_name, entries) in self.injected_imports.iter() { - let module_name = [module_name.as_bytes(), b"\0"].concat(); - body.i32_const(0) - .i32_const(module_name.len() as i32) - .call(cabi_realloc) - .local_set(module_name_ptr); - store_string_at(&mut body, memory, &module_name, module_name_ptr, 0); - - for entry in entries { - let name = [entry.name.as_bytes(), b"\0"].concat(); - // Allocate a string for the name - body.i32_const(0) - .i32_const(name.len() as i32) - .call(cabi_realloc) - .local_set(name_ptr); - store_string_at(&mut body, memory, &name, name_ptr, 0); - - // Register the import - body.local_get(module_name_ptr) - .local_get(name_ptr) - .i32_const(element_index) - .call(add_import_func); - - element_index += 1; - } - } - // No need to free the strings, since the buffer ownership is transferred to the application - - Ok(builder.finish(vec![], &mut self.module.funcs)) - } - - fn imports_table(module: &walrus::Module, export_name: &str) -> Result { - for export in module.exports.iter() { - if export.name == export_name { - return match export.item { - walrus::ExportItem::Table(id) => Ok(id), - _ => return Err(anyhow!("export named {} is not a table", export_name)), - }; - } - } - Err(anyhow!("no export named {} found", export_name)) - } - - fn get_memory(module: &walrus::Module) -> Result { - for export in module.exports.iter() { - if export.name == "memory" { - return match export.item { - walrus::ExportItem::Memory(id) => Ok(id), - _ => return Err(anyhow!("export named memory is not a memory")), - }; - } - } - Err(anyhow!("no export named memory found")) - } - - fn get_cabi_func(&self, name: &str) -> Result { - self.module.exports.get_func(name) - } -} - -fn usize_to_wasm_i32(x: usize) -> Value { - Value::I32(i32::from_le_bytes((x as u32).to_le_bytes())) -} - -fn store_string_at( - builder: &mut walrus::InstrSeqBuilder, - memory: walrus::MemoryId, - bytes: &[u8], - base: walrus::LocalId, - offset: usize, -) { - let mut written = 0; - for chunk_size in [8, 4, 2, 1] { - let chunk_count = (bytes.len() - written) / chunk_size; - for _ in 0..chunk_count { - use walrus::ir::{BinaryOp, MemArg, StoreKind}; - - let chunk = &bytes[written..written + chunk_size]; - let (v, kind) = match chunk_size { - 8 => ( - Value::I64(i64::from_le_bytes(chunk.try_into().unwrap())), - StoreKind::I64 { atomic: false }, - ), - 4 => ( - Value::I32(i32::from_le_bytes(chunk.try_into().unwrap())), - StoreKind::I32 { atomic: false }, - ), - 2 => ( - Value::I32(i16::from_le_bytes(chunk.try_into().unwrap()) as i32), - StoreKind::I32_16 { atomic: false }, - ), - 1 => ( - Value::I32(i8::from_le_bytes(chunk.try_into().unwrap()) as i32), - StoreKind::I32_8 { atomic: false }, - ), - _ => unreachable!(), - }; - builder - .local_get(base) - .const_(usize_to_wasm_i32(offset + written)) - .binop(BinaryOp::I32Add) - .const_(v) - .store( - memory, - kind, - MemArg { - align: 1, - offset: 0, - }, - ); - written += chunk_size; - } - } -} diff --git a/packages/crates/wasm-inject/tests/snapshots/export.wat b/packages/crates/wasm-inject/tests/snapshots/export.wat deleted file mode 100644 index 7101a82eb9..0000000000 --- a/packages/crates/wasm-inject/tests/snapshots/export.wat +++ /dev/null @@ -1,281 +0,0 @@ -(module - (type (;0;) (func)) - (type (;1;) (func (result i32))) - (type (;2;) (func (result i32 i64))) - (type (;3;) (func (param i32))) - (type (;4;) (func (param i32) (result i32))) - (type (;5;) (func (param i32) (result i64))) - (type (;6;) (func (param i32) (result f32))) - (type (;7;) (func (param i32) (result f64))) - (type (;8;) (func (param i32 i32))) - (type (;9;) (func (param i32 i32) (result i32))) - (type (;10;) (func (param i32 i32 i32))) - (type (;11;) (func (param i32 i64))) - (type (;12;) (func (param i32 i64) (result f32 f64))) - (type (;13;) (func (param i64 i32))) - (type (;14;) (func (param f32 i32))) - (type (;15;) (func (param f64 i32))) - (func $p_i32_i64_r_f32_f64 (;0;) (type 12) (param i32 i64) (result f32 f64) - (local i32 i32) - i32.const 0 - i32.const 20 - call $cabi_realloc - local.set 2 - local.get 2 - i32.const 0 - i32.add - i64.const 3920769619818274672 - i64.store align=1 - local.get 2 - i32.const 8 - i32.add - i64.const 6859601697219698484 - i64.store align=1 - local.get 2 - i32.const 16 - i32.add - i32.const 3421798 - i32.store align=1 - call $my-mod.init_context - local.set 3 - local.get 1 - local.get 3 - call $my-mod.push_i64 - local.get 0 - local.get 3 - call $my-mod.push_i32 - local.get 3 - local.get 2 - call $my-mod.call_export_func - local.get 2 - call $cabi_free - local.get 3 - call $my-mod.pop_f32 - local.get 3 - call $my-mod.pop_f64 - local.get 3 - call $my-mod.destroy_context - return - ) - (func $p_i32_i64 (;1;) (type 11) (param i32 i64) - (local i32 i32) - i32.const 0 - i32.const 10 - call $cabi_realloc - local.set 2 - local.get 2 - i32.const 0 - i32.add - i64.const 3920769619818274672 - i64.store align=1 - local.get 2 - i32.const 8 - i32.add - i32.const 52 - i32.store16 align=1 - call $my-mod.init_context - local.set 3 - local.get 1 - local.get 3 - call $my-mod.push_i64 - local.get 0 - local.get 3 - call $my-mod.push_i32 - local.get 3 - local.get 2 - call $my-mod.call_export_func - local.get 2 - call $cabi_free - local.get 3 - call $my-mod.destroy_context - return - ) - (func $r_i32_i64 (;2;) (type 2) (result i32 i64) - (local i32 i32) - i32.const 0 - i32.const 10 - call $cabi_realloc - local.set 0 - local.get 0 - i32.const 0 - i32.add - i64.const 3920769619818274674 - i64.store align=1 - local.get 0 - i32.const 8 - i32.add - i32.const 52 - i32.store16 align=1 - call $my-mod.init_context - local.set 1 - local.get 1 - local.get 0 - call $my-mod.call_export_func - local.get 0 - call $cabi_free - local.get 1 - call $my-mod.pop_i32 - local.get 1 - call $my-mod.pop_i64 - local.get 1 - call $my-mod.destroy_context - return - ) - (func $p_i32 (;3;) (type 3) (param i32) - (local i32 i32) - i32.const 0 - i32.const 6 - call $cabi_realloc - local.set 1 - local.get 1 - i32.const 0 - i32.add - i32.const 862543728 - i32.store align=1 - local.get 1 - i32.const 4 - i32.add - i32.const 50 - i32.store16 align=1 - call $my-mod.init_context - local.set 2 - local.get 0 - local.get 2 - call $my-mod.push_i32 - local.get 2 - local.get 1 - call $my-mod.call_export_func - local.get 1 - call $cabi_free - local.get 2 - call $my-mod.destroy_context - return - ) - (func $r_i32 (;4;) (type 1) (result i32) - (local i32 i32) - i32.const 0 - i32.const 6 - call $cabi_realloc - local.set 0 - local.get 0 - i32.const 0 - i32.add - i32.const 862543730 - i32.store align=1 - local.get 0 - i32.const 4 - i32.add - i32.const 50 - i32.store16 align=1 - call $my-mod.init_context - local.set 1 - local.get 1 - local.get 0 - call $my-mod.call_export_func - local.get 0 - call $cabi_free - local.get 1 - call $my-mod.pop_i32 - local.get 1 - call $my-mod.destroy_context - return - ) - (func $empty (;5;) (type 0) - (local i32 i32) - i32.const 0 - i32.const 6 - call $cabi_realloc - local.set 0 - local.get 0 - i32.const 0 - i32.add - i32.const 1953525093 - i32.store align=1 - local.get 0 - i32.const 4 - i32.add - i32.const 121 - i32.store16 align=1 - call $my-mod.init_context - local.set 1 - local.get 1 - local.get 0 - call $my-mod.call_export_func - local.get 0 - call $cabi_free - local.get 1 - call $my-mod.destroy_context - return - ) - (func $my-mod.call_export_func (;6;) (type 8) (param i32 i32) - unreachable - ) - (func $my-mod.add_import_func (;7;) (type 10) (param i32 i32 i32) - unreachable - ) - (func $my-mod.init_context (;8;) (type 1) (result i32) - unreachable - ) - (func $my-mod.destroy_context (;9;) (type 3) (param i32) - unreachable - ) - (func $my-mod.push_i32 (;10;) (type 8) (param i32 i32) - unreachable - ) - (func $my-mod.push_i64 (;11;) (type 13) (param i64 i32) - unreachable - ) - (func $my-mod.push_f32 (;12;) (type 14) (param f32 i32) - unreachable - ) - (func $my-mod.push_f64 (;13;) (type 15) (param f64 i32) - unreachable - ) - (func $my-mod.pop_i32 (;14;) (type 4) (param i32) (result i32) - unreachable - ) - (func $my-mod.pop_i64 (;15;) (type 5) (param i32) (result i64) - unreachable - ) - (func $my-mod.pop_f32 (;16;) (type 6) (param i32) (result f32) - unreachable - ) - (func $my-mod.pop_f64 (;17;) (type 7) (param i32) (result f64) - unreachable - ) - (func $cabi_realloc (;18;) (type 9) (param i32 i32) (result i32) - unreachable - ) - (func $cabi_free (;19;) (type 3) (param i32) - unreachable - ) - (func $my-mod.init_add_import_func (;20;) (type 3) (param i32)) - (table (;0;) 1 funcref) - (memory (;0;) 1) - (export "my-mod.imports_table" (table 0)) - (export "memory" (memory 0)) - (export "my-mod.call_export_func" (func $my-mod.call_export_func)) - (export "my-mod.add_import_func" (func $my-mod.add_import_func)) - (export "my-mod.init_context" (func $my-mod.init_context)) - (export "my-mod.destroy_context" (func $my-mod.destroy_context)) - (export "my-mod.push_i32" (func $my-mod.push_i32)) - (export "my-mod.push_i64" (func $my-mod.push_i64)) - (export "my-mod.push_f32" (func $my-mod.push_f32)) - (export "my-mod.push_f64" (func $my-mod.push_f64)) - (export "my-mod.pop_i32" (func $my-mod.pop_i32)) - (export "my-mod.pop_i64" (func $my-mod.pop_i64)) - (export "my-mod.pop_f32" (func $my-mod.pop_f32)) - (export "my-mod.pop_f64" (func $my-mod.pop_f64)) - (export "cabi_realloc" (func $cabi_realloc)) - (export "cabi_free" (func $cabi_free)) - (export "empty" (func $empty)) - (export "p_i32" (func $p_i32)) - (export "p_i32_i64" (func $p_i32_i64)) - (export "r_i32" (func $r_i32)) - (export "r_i32_i64" (func $r_i32_i64)) - (export "p_i32_i64_r_f32_f64" (func $p_i32_i64_r_f32_f64)) - (elem (;0;) (i32.const 0) func $my-mod.init_add_import_func) - (@producers - (processed-by "walrus" "0.20.3") - ) -) \ No newline at end of file diff --git a/packages/crates/wasm-inject/tests/snapshots/import.wat b/packages/crates/wasm-inject/tests/snapshots/import.wat deleted file mode 100644 index 6fe2f49a23..0000000000 --- a/packages/crates/wasm-inject/tests/snapshots/import.wat +++ /dev/null @@ -1,251 +0,0 @@ -(module - (type (;0;) (func)) - (type (;1;) (func (result i32))) - (type (;2;) (func (result i32 i64))) - (type (;3;) (func (param i32))) - (type (;4;) (func (param i32) (result i32))) - (type (;5;) (func (param i32) (result i64))) - (type (;6;) (func (param i32) (result f32))) - (type (;7;) (func (param i32) (result f64))) - (type (;8;) (func (param i32 i32))) - (type (;9;) (func (param i32 i32) (result i32))) - (type (;10;) (func (param i32 i32 i32))) - (type (;11;) (func (param i32 i64))) - (type (;12;) (func (param i32 i64) (result f32 f64))) - (type (;13;) (func (param i64 i32))) - (type (;14;) (func (param f32 i32))) - (type (;15;) (func (param f64 i32))) - (import "t" "empty" (func (;0;) (type 0))) - (import "t" "p_i32" (func (;1;) (type 3))) - (import "t" "p_i32_i64" (func (;2;) (type 11))) - (import "t" "r_i32" (func (;3;) (type 1))) - (import "t" "r_i32_i64" (func (;4;) (type 2))) - (import "t" "p_i32_i64_r_f32_f64" (func (;5;) (type 12))) - (func $my-mod.init_add_import_func (;6;) (type 3) (param i32) - (local i32 i32) - i32.const 0 - i32.const 2 - call $cabi_realloc - local.set 0 - local.get 0 - i32.const 0 - i32.add - i32.const 116 - i32.store16 align=1 - i32.const 0 - i32.const 6 - call $cabi_realloc - local.set 1 - local.get 1 - i32.const 0 - i32.add - i32.const 1953525093 - i32.store align=1 - local.get 1 - i32.const 4 - i32.add - i32.const 121 - i32.store16 align=1 - local.get 0 - local.get 1 - i32.const 1 - call $my-mod.add_import_func - i32.const 0 - i32.const 6 - call $cabi_realloc - local.set 1 - local.get 1 - i32.const 0 - i32.add - i32.const 862543728 - i32.store align=1 - local.get 1 - i32.const 4 - i32.add - i32.const 50 - i32.store16 align=1 - local.get 0 - local.get 1 - i32.const 2 - call $my-mod.add_import_func - i32.const 0 - i32.const 10 - call $cabi_realloc - local.set 1 - local.get 1 - i32.const 0 - i32.add - i64.const 3920769619818274672 - i64.store align=1 - local.get 1 - i32.const 8 - i32.add - i32.const 52 - i32.store16 align=1 - local.get 0 - local.get 1 - i32.const 3 - call $my-mod.add_import_func - i32.const 0 - i32.const 6 - call $cabi_realloc - local.set 1 - local.get 1 - i32.const 0 - i32.add - i32.const 862543730 - i32.store align=1 - local.get 1 - i32.const 4 - i32.add - i32.const 50 - i32.store16 align=1 - local.get 0 - local.get 1 - i32.const 4 - call $my-mod.add_import_func - i32.const 0 - i32.const 10 - call $cabi_realloc - local.set 1 - local.get 1 - i32.const 0 - i32.add - i64.const 3920769619818274674 - i64.store align=1 - local.get 1 - i32.const 8 - i32.add - i32.const 52 - i32.store16 align=1 - local.get 0 - local.get 1 - i32.const 5 - call $my-mod.add_import_func - i32.const 0 - i32.const 20 - call $cabi_realloc - local.set 1 - local.get 1 - i32.const 0 - i32.add - i64.const 3920769619818274672 - i64.store align=1 - local.get 1 - i32.const 8 - i32.add - i64.const 6859601697219698484 - i64.store align=1 - local.get 1 - i32.const 16 - i32.add - i32.const 3421798 - i32.store align=1 - local.get 0 - local.get 1 - i32.const 6 - call $my-mod.add_import_func - ) - (func (;7;) (type 3) (param i32) - local.get 0 - call $my-mod.pop_i32 - local.get 0 - call $my-mod.pop_i64 - call 5 - local.get 0 - call $my-mod.push_f64 - local.get 0 - call $my-mod.push_f32 - ) - (func (;8;) (type 3) (param i32) - local.get 0 - call $my-mod.pop_i32 - local.get 0 - call $my-mod.pop_i64 - call 2 - ) - (func (;9;) (type 3) (param i32) - call 4 - local.get 0 - call $my-mod.push_i64 - local.get 0 - call $my-mod.push_i32 - ) - (func (;10;) (type 3) (param i32) - local.get 0 - call $my-mod.pop_i32 - call 1 - ) - (func (;11;) (type 3) (param i32) - call 3 - local.get 0 - call $my-mod.push_i32 - ) - (func $my-mod.call_export_func (;12;) (type 8) (param i32 i32) - unreachable - ) - (func $my-mod.add_import_func (;13;) (type 10) (param i32 i32 i32) - unreachable - ) - (func $my-mod.init_context (;14;) (type 1) (result i32) - unreachable - ) - (func $my-mod.destroy_context (;15;) (type 3) (param i32) - unreachable - ) - (func $my-mod.push_i32 (;16;) (type 8) (param i32 i32) - unreachable - ) - (func $my-mod.push_i64 (;17;) (type 13) (param i64 i32) - unreachable - ) - (func $my-mod.push_f32 (;18;) (type 14) (param f32 i32) - unreachable - ) - (func $my-mod.push_f64 (;19;) (type 15) (param f64 i32) - unreachable - ) - (func $my-mod.pop_i32 (;20;) (type 4) (param i32) (result i32) - unreachable - ) - (func $my-mod.pop_i64 (;21;) (type 5) (param i32) (result i64) - unreachable - ) - (func $my-mod.pop_f32 (;22;) (type 6) (param i32) (result f32) - unreachable - ) - (func $my-mod.pop_f64 (;23;) (type 7) (param i32) (result f64) - unreachable - ) - (func $cabi_realloc (;24;) (type 9) (param i32 i32) (result i32) - unreachable - ) - (func $cabi_free (;25;) (type 3) (param i32) - unreachable - ) - (func (;26;) (type 3) (param i32) - call 0 - ) - (table (;0;) 7 funcref) - (memory (;0;) 1) - (export "my-mod.imports_table" (table 0)) - (export "memory" (memory 0)) - (export "my-mod.call_export_func" (func $my-mod.call_export_func)) - (export "my-mod.add_import_func" (func $my-mod.add_import_func)) - (export "my-mod.init_context" (func $my-mod.init_context)) - (export "my-mod.destroy_context" (func $my-mod.destroy_context)) - (export "my-mod.push_i32" (func $my-mod.push_i32)) - (export "my-mod.push_i64" (func $my-mod.push_i64)) - (export "my-mod.push_f32" (func $my-mod.push_f32)) - (export "my-mod.push_f64" (func $my-mod.push_f64)) - (export "my-mod.pop_i32" (func $my-mod.pop_i32)) - (export "my-mod.pop_i64" (func $my-mod.pop_i64)) - (export "my-mod.pop_f32" (func $my-mod.pop_f32)) - (export "my-mod.pop_f64" (func $my-mod.pop_f64)) - (export "cabi_realloc" (func $cabi_realloc)) - (export "cabi_free" (func $cabi_free)) - (elem (;0;) (i32.const 0) func $my-mod.init_add_import_func 26 10 8 11 9 7) - (@producers - (processed-by "walrus" "0.20.3") - ) -) \ No newline at end of file diff --git a/packages/crates/wasm-inject/tests/tests.rs b/packages/crates/wasm-inject/tests/tests.rs deleted file mode 100644 index 56b77346c8..0000000000 --- a/packages/crates/wasm-inject/tests/tests.rs +++ /dev/null @@ -1,116 +0,0 @@ -use walrus::{ExportItem, FunctionBuilder, Module, ModuleConfig, ValType}; -use wasm_inject::WasmInject; - -fn build_runtime_stub(m: &mut Module, prefix: &str) { - let imports_table = m.tables.add_local(0, None, ValType::Funcref); - m.exports.add( - format!("{}.imports_table", prefix).as_str(), - ExportItem::Table(imports_table), - ); - let memory = m.memories.add_local(false, 1, None); - m.exports.add("memory", ExportItem::Memory(memory)); - - fn export(module: &mut Module, name: impl AsRef, params: &[ValType], results: &[ValType]) { - let mut builder = FunctionBuilder::new(&mut module.types, params, results); - builder.name(name.as_ref().to_string()); - builder.func_body().unreachable(); - let func = builder.finish(vec![], &mut module.funcs); - - module - .exports - .add(name.as_ref(), ExportItem::Function(func)); - } - - { - use ValType::*; - let pfx = prefix; - - export(m, format!("{pfx}.call_export_func"), &[I32, I32], &[]); - export(m, format!("{pfx}.add_import_func"), &[I32, I32, I32], &[]); - export(m, format!("{pfx}.init_context"), &[], &[I32]); - export(m, format!("{pfx}.destroy_context"), &[I32], &[]); - export(m, format!("{pfx}.push_i32"), &[I32, I32], &[]); - export(m, format!("{pfx}.push_i64"), &[I64, I32], &[]); - export(m, format!("{pfx}.push_f32"), &[F32, I32], &[]); - export(m, format!("{pfx}.push_f64"), &[F64, I32], &[]); - export(m, format!("{pfx}.pop_i32"), &[I32], &[I32]); - export(m, format!("{pfx}.pop_i64"), &[I32], &[I64]); - export(m, format!("{pfx}.pop_f32"), &[I32], &[F32]); - export(m, format!("{pfx}.pop_f64"), &[I32], &[F64]); - export(m, "cabi_realloc", &[I32, I32], &[I32]); - export(m, "cabi_free", &[I32], &[]); - } -} - -fn snapshot(name: &str, wasm_bytes: &[u8]) { - use std::fs::File; - use std::io::Write; - - let path = format!("tests/snapshots/{name}.wat"); - let contents = wasmprinter::print_bytes(wasm_bytes).unwrap(); - - // Update snapshots if `UPDATE_SNAPSHOTS` env var is set or if the snapshot file doesn't exist - let update_snapshots = - std::env::var("UPDATE_SNAPSHOTS").is_ok() || !std::path::Path::new(&path).exists(); - - if update_snapshots { - let mut file = File::create(path).unwrap(); - file.write_all(contents.as_bytes()).unwrap(); - } else { - let existing = std::fs::read(path).unwrap_or_default(); - let existing = String::from_utf8_lossy(&existing); - assert_eq!(existing, contents); - } -} - -fn with_snapshot anyhow::Result<()>>(name: &str, f: F) { - let mut module = Module::with_config(ModuleConfig::new()); - build_runtime_stub(&mut module, "my-mod"); - - let mut inject = WasmInject::new(&module.emit_wasm(), "my-mod").unwrap(); - f(&mut inject).unwrap(); - let btyes = inject.run().expect("failed to run componentizer"); - - snapshot(name, &btyes); - wasmparser::validate(&btyes).expect("failed to validate wasm"); -} - -#[test] -fn import() { - with_snapshot("import", |inject| { - inject.add_import_func("t", "empty", &[], &[])?; - inject.add_import_func("t", "p_i32", &[ValType::I32], &[])?; - - inject.add_import_func("t", "p_i32_i64", &[ValType::I32, ValType::I64], &[])?; - - inject.add_import_func("t", "r_i32", &[], &[ValType::I32])?; - inject.add_import_func("t", "r_i32_i64", &[], &[ValType::I32, ValType::I64])?; - - inject.add_import_func( - "t", - "p_i32_i64_r_f32_f64", - &[ValType::I32, ValType::I64], - &[ValType::F32, ValType::F64], - )?; - Ok(()) - }); -} - -#[test] -fn export() { - with_snapshot("export", |inject| { - inject.add_export_func("empty", &[], &[])?; - inject.add_export_func("p_i32", &[ValType::I32], &[])?; - inject.add_export_func("p_i32_i64", &[ValType::I32, ValType::I64], &[])?; - - inject.add_export_func("r_i32", &[], &[ValType::I32])?; - inject.add_export_func("r_i32_i64", &[], &[ValType::I32, ValType::I64])?; - - inject.add_export_func( - "p_i32_i64_r_f32_f64", - &[ValType::I32, ValType::I64], - &[ValType::F32, ValType::F64], - )?; - Ok(()) - }); -} From 9ad2242b391a464fd9424f6a8065b98597adb1a9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 06:42:35 +0000 Subject: [PATCH 03/44] Skip build of extensions of default gems --- lib/ruby_wasm/packager/core.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 8dee79b199..f21cf63591 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -33,7 +33,16 @@ def build(executor) # Array of paths to extconf.rb files. def specs_with_extensions - @packager.specs.select { |spec| !spec.extensions.empty? } + @packager.specs.filter_map do |spec| + exts = + spec.extensions.select do |ext| + # Filter out extensions of default gems (e.g. json, openssl) + # for the exactly same gem version. + File.exist?(File.join(spec.full_gem_path, ext)) + end + next nil if exts.empty? + [spec, exts] + end end end @@ -60,8 +69,8 @@ def build(executor) def user_exts @user_exts ||= - specs_with_extensions.flat_map do |spec| - spec.extensions.map do |ext| + specs_with_extensions.flat_map do |spec, exts| + exts.map do |ext| ext_feature = File.dirname(ext) # e.g. "ext/cgi/escape" ext_srcdir = File.join(spec.full_gem_path, ext_feature) ext_relative_path = File.join(spec.full_name, ext_feature) @@ -82,7 +91,7 @@ def name base = "ruby-#{src_channel}-static-#{target_triplet}" exts = specs_with_extensions.sort hash = ::Digest::MD5.new - specs_with_extensions.each { |spec| hash << spec.full_name } + specs_with_extensions.each { |spec, _| hash << spec.full_name } exts.empty? ? base : "#{base}-#{hash.hexdigest}" end end From d1ef66a7606ff3db72e35fb7ca0827a4852b33ca Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 06:55:21 +0000 Subject: [PATCH 04/44] Add `--target` option to rbwasm build command --- Gemfile | 2 ++ lib/ruby_wasm/cli.rb | 12 ++++++------ lib/ruby_wasm/packager.rb | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index bae4d68274..5cb6599bc9 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gemspec + group :development do gem "rake" gem "rake-compiler" diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index e8a80ed849..98e69e4734 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -43,7 +43,7 @@ def build(args) options = { save_temps: false, optimize: false, - format: :single_module, + target_triplet: "wasm32-unknown-wasi", stdlib: true } OptionParser @@ -58,6 +58,10 @@ def build(args) options[:save_temps] = true end + opts.on("--target TRIPLET", "Target triplet") do |triplet| + options[:target_triplet] = triplet + end + opts.on("--optimize", "Optimize the output") do options[:optimize] = true end @@ -66,10 +70,6 @@ def build(args) options[:output] = file end - opts.on("--format FORMAT", "Output format") do |format| - options[:format] = format.to_sym - end - opts.on("--[no-]stdlib", "Include stdlib") do |stdlib| options[:stdlib] = stdlib end @@ -92,7 +92,7 @@ def build(args) end private def do_build(executor, tmp_dir, options) - packager = RubyWasm::Packager.new(tmp_dir) + packager = RubyWasm::Packager.new(tmp_dir, options[:target_triplet]) wasm_bytes = packager.package(executor, options) @stdout.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}" if options[:output] diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index 893996adb2..c96ad87ece 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -1,8 +1,8 @@ class RubyWasm::Packager - def initialize(dest_dir, definition = Bundler.definition) + def initialize(dest_dir, target_triplet, definition = Bundler.definition) @dest_dir = dest_dir @definition = definition - @target_triplet = "wasm32-unknown-wasi" + @target_triplet = target_triplet end def package(executor, options) From 82cd0431320bca0a18897ae07c1d4ae47c00c6cb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 09:52:20 +0000 Subject: [PATCH 05/44] Add `--remake` option to rbwasm build command --- lib/ruby_wasm/cli.rb | 8 +++++++- lib/ruby_wasm/packager.rb | 6 ++---- lib/ruby_wasm/packager/core.rb | 12 +++++++----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 98e69e4734..40f0a2923f 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -1,5 +1,6 @@ require "optparse" require "rbconfig" +require_relative "ruby_wasm.so" module RubyWasm class CLI @@ -33,7 +34,7 @@ def run(args) when "build" build(args) else - @stderr.puts "Unknown command: #{command}" + @stderr.puts parser exit end end @@ -43,6 +44,7 @@ def build(args) options = { save_temps: false, optimize: false, + remake: false, target_triplet: "wasm32-unknown-wasi", stdlib: true } @@ -66,6 +68,10 @@ def build(args) options[:optimize] = true end + opts.on("--remake", "Re-execute make for Ruby") do + options[:remake] = true + end + opts.on("-o", "--output FILE", "Output file") do |file| options[:output] = file end diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index c96ad87ece..2c524991e5 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -6,10 +6,8 @@ def initialize(dest_dir, target_triplet, definition = Bundler.definition) end def package(executor, options) - require_relative "ruby_wasm.so" - ruby_core = RubyWasm::Packager::Core.new(self) - tarball = ruby_core.build(executor) + tarball = ruby_core.build(executor, options) fs = RubyWasm::Packager::FileSystem.new(@dest_dir, self) fs.package_ruby_root(tarball, executor) @@ -45,7 +43,7 @@ def root @root ||= begin if explicit = ENV["RUBY_WASM_ROOT"] - explicit + File.expand_path(explicit) else Bundler.root end diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index f21cf63591..06227bcde9 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -3,9 +3,9 @@ def initialize(packager) @packager = packager end - def build(executor) + def build(executor, options) strategy = build_strategy - strategy.build(executor) + strategy.build(executor, options) end private @@ -27,7 +27,7 @@ def initialize(packager) @packager = packager end - def build(executor) + def build(executor, options) raise NotImplementedError end @@ -50,7 +50,7 @@ class DynamicLinking < BuildStrategy end class StaticLinking < BuildStrategy - def build(executor) + def build(executor, options) @build ||= RubyWasm::Build.new(name, **@packager.build_options) @build.crossruby.user_exts = user_exts @build.crossruby.debugflags = %w[-g] @@ -63,7 +63,9 @@ def build(executor) -Xlinker stack-size=16777216 ] - Bundler.with_unbundled_env { @build.crossruby.build(executor) } + Bundler.with_unbundled_env do + @build.crossruby.build(executor, remake: options[:remake]) + end @build.crossruby.artifact end From 339e91b07a372a17269d6f084b7230816df8d95c Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 10:10:22 +0000 Subject: [PATCH 06/44] Repair crossruby build from rake --- Rakefile | 2 +- lib/ruby_wasm/build/product/crossruby.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index fb1655fdc6..01d08cdb4a 100644 --- a/Rakefile +++ b/Rakefile @@ -150,7 +150,7 @@ namespace :build do toolchain = t.toolchain t.crossruby.user_exts = profile[:user_exts].map do |ext| - srcdir = File.join(LIB_ROOT, "ext", ext) + srcdir = File.join(LIB_ROOT, "packages", "gems", "js", "ext", ext) RubyWasm::CrossRubyExtProduct.new(srcdir, toolchain) end unless TOOLCHAINS.key? toolchain.name diff --git a/lib/ruby_wasm/build/product/crossruby.rb b/lib/ruby_wasm/build/product/crossruby.rb index 9f90fdb919..afb887b5f7 100644 --- a/lib/ruby_wasm/build/product/crossruby.rb +++ b/lib/ruby_wasm/build/product/crossruby.rb @@ -10,7 +10,7 @@ def initialize(srcdir, toolchain, ext_relative_path: nil) # ext_relative_path is relative path from build dir # e.g. cgi-0.3.6/ext/cgi/escape @ext_relative_path = ext_relative_path || File.basename(srcdir) - @name = ext_relative_path + @name = @ext_relative_path end def product_build_dir(crossruby) From 1ad13f9871efcf84c9584c88a694d53aa3b88319 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 11:37:00 +0000 Subject: [PATCH 07/44] Use rbwasm build for npm package instead of rake build system --- lib/ruby_wasm/packager/file_system.rb | 13 +-------- .../ruby-3.3-wasm-wasi/package.json | 2 +- .../npm-packages/ruby-wasm-wasi/src/index.ts | 3 ++- .../ruby-wasm-wasi/tools/pack-ruby-wasm.sh | 27 +++++++------------ .../ruby-wasm-wasi/tools/run-test-unit.mjs | 13 --------- tasks/packaging.rake | 2 +- 6 files changed, 15 insertions(+), 45 deletions(-) diff --git a/lib/ruby_wasm/packager/file_system.rb b/lib/ruby_wasm/packager/file_system.rb index e918b42303..0d3142d939 100644 --- a/lib/ruby_wasm/packager/file_system.rb +++ b/lib/ruby_wasm/packager/file_system.rb @@ -55,18 +55,7 @@ def package_gems end def setup_rb_content - content = <<~RUBY - kernel = (class << ::Kernel; self; end) - [kernel, ::Kernel].each do |k| - if k.private_method_defined?(:gem_original_require) - private_require = k.private_method_defined?(:require) - k.send(:remove_method, :require) - k.send(:define_method, :require, k.instance_method(:gem_original_require)) - k.send(:private, :require) if private_require - end - end - RUBY - + content = "" self.each_gem_require_path do |relative, _| content << %Q[$:.unshift File.expand_path("#{File.join("/", relative)}")\n] end diff --git a/packages/npm-packages/ruby-3.3-wasm-wasi/package.json b/packages/npm-packages/ruby-3.3-wasm-wasi/package.json index 33416719f1..394fb172fc 100644 --- a/packages/npm-packages/ruby-3.3-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-3.3-wasm-wasi/package.json @@ -28,7 +28,7 @@ "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-3.3-wasm-wasi", "build:static": "npm run build:static:files && npm run build:static:compat", - "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh ../../../rubies/3.3-wasm32-unknown-wasi-full-js-debug ./dist", + "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh ./dist", "build:rollup": "rollup -c rollup.config.mjs", "build": "npm run build:deps && npm run build:static && npm run build:wasm && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" }, diff --git a/packages/npm-packages/ruby-wasm-wasi/src/index.ts b/packages/npm-packages/ruby-wasm-wasi/src/index.ts index 32a73ed1f6..5f8b4abbe8 100644 --- a/packages/npm-packages/ruby-wasm-wasi/src/index.ts +++ b/packages/npm-packages/ruby-wasm-wasi/src/index.ts @@ -88,12 +88,13 @@ export class RubyVM { * an array of strings starting with the Ruby program name. */ initialize( - args: string[] = ["ruby.wasm", "--disable-gems", "-EUTF-8", "-e_=0"], + args: string[] = ["ruby.wasm", "-EUTF-8", "-e_=0"], ) { const c_args = args.map((arg) => arg + "\0"); this.guest.rubyInit(); this.guest.rubySysinit(c_args); this.guest.rubyOptions(c_args); + this.eval(`require "/bundle/setup"`); } /** diff --git a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh b/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh index 7bf6c57b37..3d4e372c71 100755 --- a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh +++ b/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh @@ -3,30 +3,23 @@ set -eu usage() { - echo "Usage: $(basename $0) ruby_root dist_dir" + echo "Usage: $(basename $0) dist_dir" exit 1 } -if [ $# -lt 2 ]; then +if [ $# -lt 1 ]; then usage fi -ruby_root="$1" -dist_dir="$2" +dist_dir="$PWD/$1" package_dir="$(cd "$(dirname "$0")/.." && pwd)" mkdir -p "$dist_dir" -"$WASMOPT" --strip-debug "$ruby_root/usr/local/bin/ruby" -o "$dist_dir/ruby.wasm" - -# Build +stdlib versions (removing files that are not used in normal use cases) - -workdir="$(mktemp -d)" -cp -R "$ruby_root" "$workdir/ruby-root" -rm -rf $workdir/ruby-root/usr/local/include -rm -f $workdir/ruby-root/usr/local/lib/libruby-static.a -rm -f $workdir/ruby-root/usr/local/bin/ruby -"$WASI_VFS_CLI" pack "$dist_dir/ruby.wasm" --mapdir /usr::$workdir/ruby-root/usr -o "$dist_dir/ruby+stdlib.wasm" -"$WASI_VFS_CLI" pack "$ruby_root/usr/local/bin/ruby" --mapdir /usr::$workdir/ruby-root/usr -o "$dist_dir/ruby.debug+stdlib.wasm" - -cp $dist_dir/*.wasm "$package_dir/dist/" +# Cache rubies in the package dir +export RUBY_WASM_ROOT="$package_dir/../../../" +cd "$package_dir" +bundle exec rbwasm build --no-stdlib -o "$dist_dir/ruby.wasm" +"$WASMOPT" --strip-debug "$dist_dir/ruby.wasm" -o "$dist_dir/ruby.wasm" +bundle exec rbwasm build -o "$dist_dir/ruby.debug+stdlib.wasm" +"$WASMOPT" --strip-debug "$dist_dir/ruby.debug+stdlib.wasm" -o "$dist_dir/ruby+stdlib.wasm" diff --git a/packages/npm-packages/ruby-wasm-wasi/tools/run-test-unit.mjs b/packages/npm-packages/ruby-wasm-wasi/tools/run-test-unit.mjs index 22422b2879..e2b0e7ee96 100755 --- a/packages/npm-packages/ruby-wasm-wasi/tools/run-test-unit.mjs +++ b/packages/npm-packages/ruby-wasm-wasi/tools/run-test-unit.mjs @@ -149,19 +149,6 @@ const test = async (instantiate) => { Error.stackTraceLimit = Infinity; await vm.evalAsync(` - # HACK: Until we've fixed the issue in the test-unit or power_assert - # See https://github.com/test-unit/test-unit/pull/221 - module Kernel - alias test_unit_original_require require - - def require(path) - if path == "power_assert" - raise LoadError, "power_assert is not supported in this environment" - end - test_unit_original_require(path) - end - end - require 'test/unit' require_relative '${rootTestFile}' Test::Unit::AutoRunner.run diff --git a/tasks/packaging.rake b/tasks/packaging.rake index f7fad2afe7..2c89c15e82 100644 --- a/tasks/packaging.rake +++ b/tasks/packaging.rake @@ -12,7 +12,7 @@ namespace :npm do namespace pkg[:name] do desc "Build npm package #{pkg[:name]}" - task "build" => ["build:#{pkg[:build]}"] do + task "build" do sh tools, "npm run build", chdir: pkg_dir end From bf32ca972a4a27fc41d4283f0e499b2a06ac22ac Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 11:38:04 +0000 Subject: [PATCH 08/44] Explicitly specify Gemfile for webrick --- packages/npm-packages/ruby-wasm-wasi/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/npm-packages/ruby-wasm-wasi/package.json b/packages/npm-packages/ruby-wasm-wasi/package.json index 77a502b443..e599366786 100644 --- a/packages/npm-packages/ruby-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-wasm-wasi/package.json @@ -47,7 +47,7 @@ "test:e2e": "playwright install && npm run test:e2e:examples && npm run test:e2e:integrations", "test:e2e:examples": "playwright test -c test-e2e/playwright.examples.config.ts", "test:e2e:integrations": "playwright test -c test-e2e/playwright.integrations.config.ts", - "serve:example": "bundle exec ruby -run -e httpd ./example -p 8085", + "serve:example": "BUNDLE_GEMFILE=../../../Gemfile bundle exec ruby -run -e httpd ./example -p 8085", "format": "prettier --write .", "build:static": "./tools/pack-bindgen-src.sh ./dist", "build:rollup": "rollup -c rollup.config.mjs", From a03a243c5f3d44597221cd78f5ef1f0602cf162e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 11:39:23 +0000 Subject: [PATCH 09/44] Print CLI help to stderr --- lib/ruby_wasm/cli.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 40f0a2923f..101ec57d1d 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -11,7 +11,7 @@ def initialize(stdout:, stderr:) def run(args) available_commands = %w[build] - OptionParser + parser = OptionParser .new do |opts| opts.banner = <<~USAGE Usage: rbwasm [options...] [command] @@ -20,14 +20,14 @@ def run(args) USAGE opts.version = RubyWasm::VERSION opts.on("-h", "--help", "Prints this help") do - @stdout.puts opts + @stderr.puts opts exit end opts.on("--log-level LEVEL", "Log level") do |level| RubyWasm.log_level = level.to_sym end end - .order!(args) + parser.order!(args) command = args.shift case command From 779ec3cba71ea09397822062b531fdc8bc9c84de Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 14:42:30 +0000 Subject: [PATCH 10/44] Migrate all rake tasks to use rbwasm build --- Rakefile | 124 +++--------------- lib/ruby_wasm/cli.rb | 54 ++++++-- lib/ruby_wasm/packager.rb | 106 +++++++++++++-- lib/ruby_wasm/packager/core.rb | 10 +- lib/ruby_wasm/packager/file_system.rb | 4 +- .../ruby-3.2-wasm-wasi/package.json | 2 +- .../ruby-3.3-wasm-wasi/package.json | 2 +- .../ruby-head-wasm-wasi/package.json | 2 +- .../ruby-wasm-wasi/tools/pack-ruby-wasm.sh | 11 +- 9 files changed, 175 insertions(+), 140 deletions(-) diff --git a/Rakefile b/Rakefile index 01d08cdb4a..cbabed47c6 100644 --- a/Rakefile +++ b/Rakefile @@ -8,78 +8,14 @@ require "ruby_wasm/rake_task" Dir.glob("tasks/**.rake").each { |f| import f } -BUILD_SOURCES = { - "head" => { - type: "github", - repo: "ruby/ruby", - rev: "master", - patches: Dir["./patches/*.patch"].map { |p| File.expand_path(p) } - }, - "3.3" => { - type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.0.tar.gz" - }, - "3.2" => { - type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.2.tar.gz" - } -} - -# Respect revisions specified in build_manifest.json, which is usually generated on GitHub Actions. -if File.exist?("build_manifest.json") - begin - manifest = JSON.parse(File.read("build_manifest.json")) - manifest["ruby_revisions"].each do |name, rev| - BUILD_SOURCES[name][:rev] = rev - end - rescue StandardError - $stderr.puts "Failed to load build_manifest.json" - end -end - -FULL_EXTS = - "bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl" +BUILD_SOURCES = ["3.3", "3.2", "head"] +BUILD_PROFILES = ["full", "minimal"] -BUILD_PROFILES = { - "minimal" => { - debug: false, - default_exts: "", - user_exts: [] - }, - "minimal-debug" => { - debug: true, - default_exts: "", - user_exts: [] - }, - "full" => { - debug: false, - default_exts: FULL_EXTS, - user_exts: [] - }, - "full-debug" => { - debug: true, - default_exts: FULL_EXTS, - user_exts: [] - }, - "full-js-debug" => { - debug: true, - default_exts: FULL_EXTS, - user_exts: %w[js witapi] - } -} - -BUILDS = - %w[wasm32-unknown-wasi wasm32-unknown-emscripten] - .product(BUILD_SOURCES.keys, BUILD_PROFILES.keys) - .select do |target, _, profile_name| - if target == "wasm32-unknown-emscripten" - # Builds only full for Emscripten since minimal, js, debug - # builds are rarely used with Emscripten. - next profile_name == "full" - end - next true - end - .map { |t, s, p| { src: s, target: t, profile: p } } +BUILDS = BUILD_SOURCES.product(BUILD_PROFILES).map do |src, profile| + [src, "wasm32-unknown-wasi", profile] +end + BUILD_SOURCES.map do |src| + [src, "wasm32-unknown-emscripten", "full"] +end NPM_PACKAGES = [ { @@ -117,45 +53,19 @@ STANDALONE_PACKAGES = [ LIB_ROOT = File.dirname(__FILE__) TOOLCHAINS = {} +BUILDS.map { |_, target, _| target }.uniq.each do |target| + toolchain = RubyWasm::Toolchain.get(target) + TOOLCHAINS[toolchain.name] = toolchain +end namespace :build do BUILD_TASKS = - BUILDS.map do |params| - name = "#{params[:src]}-#{params[:target]}-#{params[:profile]}" - source = BUILD_SOURCES[params[:src]].merge(name: params[:src]) - profile = BUILD_PROFILES[params[:profile]] - options = { - src: source, - target: params[:target], - default_exts: profile[:default_exts] - } - debug = profile[:debug] - RubyWasm::BuildTask.new(name, **options) do |t| - if debug - t.crossruby.debugflags = %w[-g] - t.crossruby.wasmoptflags = %w[-O3 -g] - t.crossruby.ldflags = %w[ - -Xlinker - --stack-first - -Xlinker - -z - -Xlinker - stack-size=16777216 - ] - else - t.crossruby.debugflags = %w[-g0] - t.crossruby.ldflags = %w[-Xlinker -zstack-size=16777216] - end - - toolchain = t.toolchain - t.crossruby.user_exts = - profile[:user_exts].map do |ext| - srcdir = File.join(LIB_ROOT, "packages", "gems", "js", "ext", ext) - RubyWasm::CrossRubyExtProduct.new(srcdir, toolchain) - end - unless TOOLCHAINS.key? toolchain.name - TOOLCHAINS[toolchain.name] = toolchain - end + BUILDS.map do |src, target, profile| + name = "#{src}-#{target}-#{profile}" + + desc "Cross-build Ruby for #{@target}" + task name do + sh *["exe/rbwasm", "build", "--ruby-version", src, "--target", target, "--build-profile", profile, "-o", "/dev/null"] end end diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 101ec57d1d..18b6acddd1 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -45,7 +45,9 @@ def build(args) save_temps: false, optimize: false, remake: false, + ruby_version: "3.3", target_triplet: "wasm32-unknown-wasi", + profile: "full", stdlib: true } OptionParser @@ -60,10 +62,18 @@ def build(args) options[:save_temps] = true end + opts.on("--ruby-version VERSION", "Ruby version") do |version| + options[:ruby_version] = version + end + opts.on("--target TRIPLET", "Target triplet") do |triplet| options[:target_triplet] = triplet end + opts.on("--build-profile PROFILE", "Build profile. full or minimal") do |profile| + options[:profile] = profile + end + opts.on("--optimize", "Optimize the output") do options[:optimize] = true end @@ -85,28 +95,54 @@ def build(args) verbose = RubyWasm.logger.level == :debug executor = RubyWasm::BuildExecutor.new(verbose: verbose) + unless options[:output] + @stderr.puts "Output file is not specified" + exit 1 + end + require "tmpdir" if options[:save_temps] tmpdir = Dir.mktmpdir self.do_build(executor, tmpdir, options) - @stdout.puts "Temporary files are saved to #{tmpdir}" + @stderr.puts "Temporary files are saved to #{tmpdir}" exit else Dir.mktmpdir { |tmpdir| self.do_build(executor, tmpdir, options) } end end - private def do_build(executor, tmp_dir, options) - packager = RubyWasm::Packager.new(tmp_dir, options[:target_triplet]) - wasm_bytes = packager.package(executor, options) - @stdout.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}" - if options[:output] - File.binwrite(options[:output], wasm_bytes.pack("C*")) - @stdout.puts "Wrote #{options[:output]}" + private + + def build_config(options) + config = { + target: options[:target_triplet], + src: options[:ruby_version], + } + case options[:profile] + when "full" + config[:profile] = RubyWasm::Packager::ALL_DEFAULT_EXTS + when "minimal" + config[:profile] = "" else - @stderr.puts "Writing to stdout" + RubyWasm.logger.error "Unknown profile: #{options[:profile]} (available: full, minimal)" + exit 1 + end + end + + def do_build(executor, tmp_dir, options) + if defined?(Bundler) + definition = Bundler.definition + end + packager = RubyWasm::Packager.new(tmp_dir, options[:target_triplet], definition) + wasm_bytes = packager.package(executor, options) + @stderr.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}" + case options[:output] + when "-" @stdout.write wasm_bytes.pack("C*") + else + File.binwrite(options[:output], wasm_bytes.pack("C*")) + @stderr.puts "Wrote #{options[:output]}" end end end diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index 2c524991e5..5246416af8 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -1,10 +1,21 @@ +# A class responsible for packaging whole Ruby project class RubyWasm::Packager - def initialize(dest_dir, target_triplet, definition = Bundler.definition) + # Initializes a new instance of the RubyWasm::Packager class. + # + # @param dest_dir [String] The destination used to construct the filesystem. + # @param config [Hash] The build config used for building Ruby. + # @param definition [Bundler::Definition] The Bundler definition. + def initialize(dest_dir, config = nil, definition = nil) @dest_dir = dest_dir @definition = definition - @target_triplet = target_triplet + @config = config end + # Packages the Ruby code into a Wasm binary. (including extensions) + # + # @param executor [RubyWasm::BuildExecutor] The executor for building the Wasm binary. + # @param options [Hash] The packaging options. + # @return [Array] The bytes of the packaged Wasm binary. def package(executor, options) ruby_core = RubyWasm::Packager::Core.new(self) tarball = ruby_core.build(executor, options) @@ -29,42 +40,111 @@ def package(executor, options) wasm_bytes end + # The list of excluded gems from the Bundler definition. EXCLUDED_GEMS = %w[ruby_wasm bundler] + # Retrieves the specs from the Bundler definition, excluding the excluded gems. def specs + return [] unless @definition @definition.specs.reject { |spec| EXCLUDED_GEMS.include?(spec.name) } end + # Checks if dynamic linking is supported. def support_dynamic_linking? @ruby_channel == "head" end + # Retrieves the root directory of the Ruby project. + # The root directory contains the following stuff: + # * patches/*.patch + # * build_manifest.json + # * rubies + # * build def root @root ||= begin if explicit = ENV["RUBY_WASM_ROOT"] File.expand_path(explicit) - else + elsif defined?(Bundler) Bundler.root + else + Dir.pwd end rescue Bundler::GemfileNotFound Dir.pwd end end - def build_options - { - target: @target_triplet, - src: { - name: "3.3", + # Retrieves the alias definitions for the Ruby sources. + def build_source_aliases + patches = Dir[File.join(root, "patches", "*.patch")] + sources = { + "head" => { + type: "github", + repo: "ruby/ruby", + rev: "master", + patches: patches.map { |p| File.expand_path(p) } + }, + "3.3" => { type: "tarball", url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.0.tar.gz" }, - default_exts: - "bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl", - toolchain: RubyWasm::Toolchain.get(@target_triplet), - build_dir: File.join(root, "build"), - rubies_dir: File.join(root, "rubies") + "3.2" => { + type: "tarball", + url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.2.tar.gz" + } + } + sources.each do |name, source| + source[:name] = name + end + + build_manifest = File.join(root, "build_manifest.json") + if File.exist?(build_manifest) + begin + manifest = JSON.parse(File.read(build_manifest)) + manifest["ruby_revisions"].each do |name, rev| + sources[name][:rev] = rev + end + rescue StandardError => e + RubyWasm.logger.warn "Failed to load build_manifest.json: #{e}" + end + end + sources + end + + ALL_DEFAULT_EXTS = "bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl" + + # Retrieves the build options used for building Ruby itself. + def build_options + default = { + target: "wasm32-unknown-wasi", + src: "3.3", + default_exts: ALL_DEFAULT_EXTS, } + override = {} + if @config + override = @config["build_options"] || {} + end + # Merge the default options with the config options + default.merge(override) + end + + # Retrieves the resolved build options + def full_build_options + options = build_options + toolchain = RubyWasm::Toolchain.get(options[:target]) + build_dir = File.join(root, "build") + rubies_dir = File.join(root, "rubies") + src = if options[:src].is_a?(Hash) + options[:src] + else + src_name = options[:src] + aliases = build_source_aliases + aliases[src_name] || raise("Unknown Ruby source: #{src_name} (available: #{aliases.keys.join(", ")})") + end + options.merge( + toolchain: toolchain, build_dir: build_dir, + rubies_dir: rubies_dir, src: src + ) end end diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 06227bcde9..de91b0206c 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -51,7 +51,7 @@ class DynamicLinking < BuildStrategy class StaticLinking < BuildStrategy def build(executor, options) - @build ||= RubyWasm::Build.new(name, **@packager.build_options) + @build ||= RubyWasm::Build.new(name, **@packager.full_build_options) @build.crossruby.user_exts = user_exts @build.crossruby.debugflags = %w[-g] @build.crossruby.wasmoptflags = %w[-O3 -g] @@ -63,7 +63,11 @@ def build(executor, options) -Xlinker stack-size=16777216 ] - Bundler.with_unbundled_env do + if defined?(Bundler) + Bundler.with_unbundled_env do + @build.crossruby.build(executor, remake: options[:remake]) + end + else @build.crossruby.build(executor, remake: options[:remake]) end @build.crossruby.artifact @@ -87,7 +91,7 @@ def user_exts def name require "digest" - options = @packager.build_options + options = @packager.full_build_options src_channel = options[:src][:name] target_triplet = options[:target] base = "ruby-#{src_channel}-static-#{target_triplet}" diff --git a/lib/ruby_wasm/packager/file_system.rb b/lib/ruby_wasm/packager/file_system.rb index 0d3142d939..d690fe7a9f 100644 --- a/lib/ruby_wasm/packager/file_system.rb +++ b/lib/ruby_wasm/packager/file_system.rb @@ -51,7 +51,9 @@ def package_gems setup_rb_path = File.join(bundle_relative_path, "setup.rb") RubyWasm.logger.info "Packaging setup.rb: #{setup_rb_path}" - File.write(File.join(@dest_dir, setup_rb_path), setup_rb_content) + full_setup_rb_path = File.join(@dest_dir, setup_rb_path) + FileUtils.mkdir_p File.dirname(full_setup_rb_path) + File.write(full_setup_rb_path, setup_rb_content) end def setup_rb_content diff --git a/packages/npm-packages/ruby-3.2-wasm-wasi/package.json b/packages/npm-packages/ruby-3.2-wasm-wasi/package.json index 2e67620c94..0ba21305f2 100644 --- a/packages/npm-packages/ruby-3.2-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-3.2-wasm-wasi/package.json @@ -28,7 +28,7 @@ "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-3.2-wasm-wasi", "build:static": "npm run build:static:files && npm run build:static:compat", - "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh ../../../rubies/3.2-wasm32-unknown-wasi-full-js-debug ./dist", + "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh 3.2 ./dist", "build:rollup": "rollup -c rollup.config.mjs", "build": "npm run build:deps && npm run build:static && npm run build:wasm && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" }, diff --git a/packages/npm-packages/ruby-3.3-wasm-wasi/package.json b/packages/npm-packages/ruby-3.3-wasm-wasi/package.json index 394fb172fc..cd7fd0756c 100644 --- a/packages/npm-packages/ruby-3.3-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-3.3-wasm-wasi/package.json @@ -28,7 +28,7 @@ "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-3.3-wasm-wasi", "build:static": "npm run build:static:files && npm run build:static:compat", - "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh ./dist", + "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh 3.3 ./dist", "build:rollup": "rollup -c rollup.config.mjs", "build": "npm run build:deps && npm run build:static && npm run build:wasm && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" }, diff --git a/packages/npm-packages/ruby-head-wasm-wasi/package.json b/packages/npm-packages/ruby-head-wasm-wasi/package.json index 5de9cfd4de..b210043556 100644 --- a/packages/npm-packages/ruby-head-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-head-wasm-wasi/package.json @@ -28,7 +28,7 @@ "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-head-wasm-wasi", "build:static": "npm run build:static:files && npm run build:static:compat", - "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh ../../../rubies/head-wasm32-unknown-wasi-full-js-debug ./dist", + "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh head ./dist", "build:rollup": "rollup -c rollup.config.mjs", "build": "npm run build:deps && npm run build:static && npm run build:wasm && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" }, diff --git a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh b/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh index 3d4e372c71..342f3f0a3b 100755 --- a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh +++ b/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh @@ -3,7 +3,7 @@ set -eu usage() { - echo "Usage: $(basename $0) dist_dir" + echo "Usage: $(basename $0) ruby_version dist_dir" exit 1 } @@ -11,7 +11,8 @@ if [ $# -lt 1 ]; then usage fi -dist_dir="$PWD/$1" +ruby_version="$1" +dist_dir="$PWD/$2" package_dir="$(cd "$(dirname "$0")/.." && pwd)" mkdir -p "$dist_dir" @@ -19,7 +20,9 @@ mkdir -p "$dist_dir" # Cache rubies in the package dir export RUBY_WASM_ROOT="$package_dir/../../../" cd "$package_dir" -bundle exec rbwasm build --no-stdlib -o "$dist_dir/ruby.wasm" + +rbwasm_options="--ruby-version $ruby_version --target wasm32-unknown-wasi --build-profile full" +bundle exec rbwasm build ${rbwasm_options[@]} --no-stdlib -o "$dist_dir/ruby.wasm" "$WASMOPT" --strip-debug "$dist_dir/ruby.wasm" -o "$dist_dir/ruby.wasm" -bundle exec rbwasm build -o "$dist_dir/ruby.debug+stdlib.wasm" +bundle exec rbwasm build ${rbwasm_options[@]} -o "$dist_dir/ruby.debug+stdlib.wasm" "$WASMOPT" --strip-debug "$dist_dir/ruby.debug+stdlib.wasm" -o "$dist_dir/ruby+stdlib.wasm" From eecb8ab9186847b6f7d693360a72bb3030a9d08c Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 22:21:31 +0000 Subject: [PATCH 11/44] Fix tasks/ci.rake for build source aliases --- Rakefile | 4 +++- lib/ruby_wasm/packager.rb | 4 ++-- tasks/ci.rake | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Rakefile b/Rakefile index cbabed47c6..da145dd3a4 100644 --- a/Rakefile +++ b/Rakefile @@ -5,6 +5,7 @@ require "open-uri" $LOAD_PATH << File.join(File.dirname(__FILE__), "lib") require "ruby_wasm/rake_task" +require "ruby_wasm/packager" Dir.glob("tasks/**.rake").each { |f| import f } @@ -54,7 +55,8 @@ LIB_ROOT = File.dirname(__FILE__) TOOLCHAINS = {} BUILDS.map { |_, target, _| target }.uniq.each do |target| - toolchain = RubyWasm::Toolchain.get(target) + build_dir = File.join(LIB_ROOT, "build") + toolchain = RubyWasm::Toolchain.get(target, build_dir) TOOLCHAINS[toolchain.name] = toolchain end diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index 5246416af8..588407d8e5 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -76,7 +76,7 @@ def root end # Retrieves the alias definitions for the Ruby sources. - def build_source_aliases + def self.build_source_aliases(root) patches = Dir[File.join(root, "patches", "*.patch")] sources = { "head" => { @@ -139,7 +139,7 @@ def full_build_options options[:src] else src_name = options[:src] - aliases = build_source_aliases + aliases = self.class.build_source_aliases(root) aliases[src_name] || raise("Unknown Ruby source: #{src_name} (available: #{aliases.keys.join(", ")})") end options.merge( diff --git a/tasks/ci.rake b/tasks/ci.rake index a28db8afbe..4009b76b0d 100644 --- a/tasks/ci.rake +++ b/tasks/ci.rake @@ -1,6 +1,7 @@ def latest_build_sources BUILD_SOURCES - .filter_map do |name, src| + .filter_map do |name| + src = RubyWasm::Packager.build_source_aliases(LIB_ROOT)[name] case src[:type] when "github" url = "repos/#{src[:repo]}/commits/#{src[:rev]}" @@ -21,7 +22,8 @@ def release_note |:-------:|:------:| EOS - BUILD_SOURCES.each do |name, source| + BUILD_SOURCES.each do |name| + source = RubyWasm::Packager.build_source_aliases(LIB_ROOT)[name] case source[:type] when "github" output += From ed134ad68ee581f950b6f3a81e61625cbc502db9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 2 Jan 2024 22:22:15 +0000 Subject: [PATCH 12/44] rake format --- Rakefile | 40 +++++++++++++------ lib/ruby_wasm/cli.rb | 23 +++++------ lib/ruby_wasm/packager.rb | 37 +++++++++-------- .../npm-packages/ruby-wasm-wasi/src/index.ts | 4 +- 4 files changed, 58 insertions(+), 46 deletions(-) diff --git a/Rakefile b/Rakefile index da145dd3a4..0f31bbb9c6 100644 --- a/Rakefile +++ b/Rakefile @@ -9,14 +9,14 @@ require "ruby_wasm/packager" Dir.glob("tasks/**.rake").each { |f| import f } -BUILD_SOURCES = ["3.3", "3.2", "head"] -BUILD_PROFILES = ["full", "minimal"] +BUILD_SOURCES = %w[3.3 3.2 head] +BUILD_PROFILES = %w[full minimal] -BUILDS = BUILD_SOURCES.product(BUILD_PROFILES).map do |src, profile| - [src, "wasm32-unknown-wasi", profile] -end + BUILD_SOURCES.map do |src| - [src, "wasm32-unknown-emscripten", "full"] -end +BUILDS = + BUILD_SOURCES + .product(BUILD_PROFILES) + .map { |src, profile| [src, "wasm32-unknown-wasi", profile] } + + BUILD_SOURCES.map { |src| [src, "wasm32-unknown-emscripten", "full"] } NPM_PACKAGES = [ { @@ -54,11 +54,14 @@ STANDALONE_PACKAGES = [ LIB_ROOT = File.dirname(__FILE__) TOOLCHAINS = {} -BUILDS.map { |_, target, _| target }.uniq.each do |target| - build_dir = File.join(LIB_ROOT, "build") - toolchain = RubyWasm::Toolchain.get(target, build_dir) - TOOLCHAINS[toolchain.name] = toolchain -end +BUILDS + .map { |_, target, _| target } + .uniq + .each do |target| + build_dir = File.join(LIB_ROOT, "build") + toolchain = RubyWasm::Toolchain.get(target, build_dir) + TOOLCHAINS[toolchain.name] = toolchain + end namespace :build do BUILD_TASKS = @@ -67,7 +70,18 @@ namespace :build do desc "Cross-build Ruby for #{@target}" task name do - sh *["exe/rbwasm", "build", "--ruby-version", src, "--target", target, "--build-profile", profile, "-o", "/dev/null"] + sh *[ + "exe/rbwasm", + "build", + "--ruby-version", + src, + "--target", + target, + "--build-profile", + profile, + "-o", + "/dev/null" + ] end end diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 18b6acddd1..594938900b 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -11,8 +11,8 @@ def initialize(stdout:, stderr:) def run(args) available_commands = %w[build] - parser = OptionParser - .new do |opts| + parser = + OptionParser.new do |opts| opts.banner = <<~USAGE Usage: rbwasm [options...] [command] @@ -70,9 +70,10 @@ def build(args) options[:target_triplet] = triplet end - opts.on("--build-profile PROFILE", "Build profile. full or minimal") do |profile| - options[:profile] = profile - end + opts.on( + "--build-profile PROFILE", + "Build profile. full or minimal" + ) { |profile| options[:profile] = profile } opts.on("--optimize", "Optimize the output") do options[:optimize] = true @@ -115,10 +116,7 @@ def build(args) private def build_config(options) - config = { - target: options[:target_triplet], - src: options[:ruby_version], - } + config = { target: options[:target_triplet], src: options[:ruby_version] } case options[:profile] when "full" config[:profile] = RubyWasm::Packager::ALL_DEFAULT_EXTS @@ -131,10 +129,9 @@ def build_config(options) end def do_build(executor, tmp_dir, options) - if defined?(Bundler) - definition = Bundler.definition - end - packager = RubyWasm::Packager.new(tmp_dir, options[:target_triplet], definition) + definition = Bundler.definition if defined?(Bundler) + packager = + RubyWasm::Packager.new(tmp_dir, options[:target_triplet], definition) wasm_bytes = packager.package(executor, options) @stderr.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}" case options[:output] diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index 588407d8e5..693a6519d1 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -94,9 +94,7 @@ def self.build_source_aliases(root) url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.2.tar.gz" } } - sources.each do |name, source| - source[:name] = name - end + sources.each { |name, source| source[:name] = name } build_manifest = File.join(root, "build_manifest.json") if File.exist?(build_manifest) @@ -112,19 +110,18 @@ def self.build_source_aliases(root) sources end - ALL_DEFAULT_EXTS = "bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl" + ALL_DEFAULT_EXTS = + "bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl" # Retrieves the build options used for building Ruby itself. def build_options default = { target: "wasm32-unknown-wasi", src: "3.3", - default_exts: ALL_DEFAULT_EXTS, + default_exts: ALL_DEFAULT_EXTS } override = {} - if @config - override = @config["build_options"] || {} - end + override = @config["build_options"] || {} if @config # Merge the default options with the config options default.merge(override) end @@ -135,16 +132,22 @@ def full_build_options toolchain = RubyWasm::Toolchain.get(options[:target]) build_dir = File.join(root, "build") rubies_dir = File.join(root, "rubies") - src = if options[:src].is_a?(Hash) - options[:src] - else - src_name = options[:src] - aliases = self.class.build_source_aliases(root) - aliases[src_name] || raise("Unknown Ruby source: #{src_name} (available: #{aliases.keys.join(", ")})") - end + src = + if options[:src].is_a?(Hash) + options[:src] + else + src_name = options[:src] + aliases = self.class.build_source_aliases(root) + aliases[src_name] || + raise( + "Unknown Ruby source: #{src_name} (available: #{aliases.keys.join(", ")})" + ) + end options.merge( - toolchain: toolchain, build_dir: build_dir, - rubies_dir: rubies_dir, src: src + toolchain: toolchain, + build_dir: build_dir, + rubies_dir: rubies_dir, + src: src ) end end diff --git a/packages/npm-packages/ruby-wasm-wasi/src/index.ts b/packages/npm-packages/ruby-wasm-wasi/src/index.ts index 5f8b4abbe8..46f6c2130a 100644 --- a/packages/npm-packages/ruby-wasm-wasi/src/index.ts +++ b/packages/npm-packages/ruby-wasm-wasi/src/index.ts @@ -87,9 +87,7 @@ export class RubyVM { * @param args The command line arguments to pass to Ruby. Must be * an array of strings starting with the Ruby program name. */ - initialize( - args: string[] = ["ruby.wasm", "-EUTF-8", "-e_=0"], - ) { + initialize(args: string[] = ["ruby.wasm", "-EUTF-8", "-e_=0"]) { const c_args = args.map((arg) => arg + "\0"); this.guest.rubyInit(); this.guest.rubySysinit(c_args); From f4fd002b838db5c5ba1421d4fe8ed061daadc666 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 00:31:55 +0000 Subject: [PATCH 13/44] Fix `rake ci:*` tasks --- Rakefile | 51 +++++++++++++++----- lib/ruby_wasm/build.rb | 6 ++- lib/ruby_wasm/build/product/crossruby.rb | 2 +- lib/ruby_wasm/cli.rb | 60 ++++++++++++++++++++---- lib/ruby_wasm/packager.rb | 18 +++---- lib/ruby_wasm/packager/core.rb | 46 ++++++++++++++---- lib/ruby_wasm/packager/file_system.rb | 9 +++- tasks/ci.rake | 7 +-- tasks/gem.rake | 7 ++- 9 files changed, 155 insertions(+), 51 deletions(-) diff --git a/Rakefile b/Rakefile index 0f31bbb9c6..bdc05347c6 100644 --- a/Rakefile +++ b/Rakefile @@ -63,26 +63,51 @@ BUILDS TOOLCHAINS[toolchain.name] = toolchain end +class BuildTask < Struct.new(:name, :target, :build_command) + def ruby_cache_key + return @key if @key + require "open3" + cmd = build_command + ["--print-ruby-cache-key"] + stdout, status = Open3.capture2(*cmd) + unless status.success? + raise "Command failed with status (#{status.exitstatus}): #{cmd.join ""}" + end + require "json" + @key = JSON.parse(stdout) + end + + def hexdigest + ruby_cache_key["hexdigest"] + end + def artifact + ruby_cache_key["artifact"] + end +end + namespace :build do BUILD_TASKS = BUILDS.map do |src, target, profile| name = "#{src}-#{target}-#{profile}" - desc "Cross-build Ruby for #{@target}" + build_command = [ + "exe/rbwasm", + "build", + "--ruby-version", + src, + "--target", + target, + "--build-profile", + profile, + "--disable-gems", + "-o", + "/dev/null" + ] + desc "Cross-build Ruby for #{target}" task name do - sh *[ - "exe/rbwasm", - "build", - "--ruby-version", - src, - "--target", - target, - "--build-profile", - profile, - "-o", - "/dev/null" - ] + sh *build_command end + + BuildTask.new(name, target, build_command) end desc "Clean build directories" diff --git a/lib/ruby_wasm/build.rb b/lib/ruby_wasm/build.rb index 00d0191dcf..8c075969ac 100644 --- a/lib/ruby_wasm/build.rb +++ b/lib/ruby_wasm/build.rb @@ -51,7 +51,11 @@ def initialize( @openssl = RubyWasm::OpenSSLProduct.new(@build_dir, @target, @toolchain) build_params = - RubyWasm::BuildParams.new(options.merge(name: name, target: target)) + RubyWasm::BuildParams.new( + name: name, + target: target, + default_exts: options[:default_exts] + ) @crossruby = RubyWasm::CrossRubyProduct.new( diff --git a/lib/ruby_wasm/build/product/crossruby.rb b/lib/ruby_wasm/build/product/crossruby.rb index afb887b5f7..323ddce315 100644 --- a/lib/ruby_wasm/build/product/crossruby.rb +++ b/lib/ruby_wasm/build/product/crossruby.rb @@ -257,7 +257,7 @@ def dest_dir end def artifact - File.join(@rubies_dir, "ruby-#{name}.tar.gz") + File.join(@rubies_dir, "#{name}.tar.gz") end def extinit_obj diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 594938900b..182aaead13 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -48,7 +48,8 @@ def build(args) ruby_version: "3.3", target_triplet: "wasm32-unknown-wasi", profile: "full", - stdlib: true + stdlib: true, + disable_gems: false } OptionParser .new do |opts| @@ -90,11 +91,29 @@ def build(args) opts.on("--[no-]stdlib", "Include stdlib") do |stdlib| options[:stdlib] = stdlib end + + opts.on("--disable-gems", "Disable gems") do + options[:disable_gems] = true + end + + opts.on("--format FORMAT", "Output format") do |format| + options[:format] = format + end + + opts.on("--print-ruby-cache-key", "Print Ruby cache key") do + options[:print_ruby_cache_key] = true + end end .parse!(args) verbose = RubyWasm.logger.level == :debug executor = RubyWasm::BuildExecutor.new(verbose: verbose) + packager = self.derive_packager(options) + + if options[:print_ruby_cache_key] + self.do_print_ruby_cache_key(packager) + exit + end unless options[:output] @stderr.puts "Output file is not specified" @@ -105,11 +124,13 @@ def build(args) if options[:save_temps] tmpdir = Dir.mktmpdir - self.do_build(executor, tmpdir, options) + self.do_build(executor, tmpdir, packager, options) @stderr.puts "Temporary files are saved to #{tmpdir}" exit else - Dir.mktmpdir { |tmpdir| self.do_build(executor, tmpdir, options) } + Dir.mktmpdir do |tmpdir| + self.do_build(executor, tmpdir, packager, options) + end end end @@ -119,20 +140,39 @@ def build_config(options) config = { target: options[:target_triplet], src: options[:ruby_version] } case options[:profile] when "full" - config[:profile] = RubyWasm::Packager::ALL_DEFAULT_EXTS + config[:default_exts] = RubyWasm::Packager::ALL_DEFAULT_EXTS when "minimal" - config[:profile] = "" + config[:default_exts] = "" else RubyWasm.logger.error "Unknown profile: #{options[:profile]} (available: full, minimal)" exit 1 end + config[:suffix] = "-#{options[:profile]}" + config + end + + def derive_packager(options) + if defined?(Bundler) && !options[:disable_gems] + definition = Bundler.definition + end + RubyWasm::Packager.new(build_config(options), definition) + end + + def do_print_ruby_cache_key(packager) + ruby_core_build = packager.ruby_core_build + require "digest" + digest = Digest::SHA256.new + ruby_core_build.cache_key(digest) + hexdigest = digest.hexdigest + require "json" + @stdout.puts JSON.generate( + hexdigest: hexdigest, + artifact: ruby_core_build.artifact + ) end - def do_build(executor, tmp_dir, options) - definition = Bundler.definition if defined?(Bundler) - packager = - RubyWasm::Packager.new(tmp_dir, options[:target_triplet], definition) - wasm_bytes = packager.package(executor, options) + def do_build(executor, tmpdir, packager, options) + wasm_bytes = packager.package(executor, tmpdir, options) @stderr.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}" case options[:output] when "-" diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index 693a6519d1..c923c63830 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -2,11 +2,9 @@ class RubyWasm::Packager # Initializes a new instance of the RubyWasm::Packager class. # - # @param dest_dir [String] The destination used to construct the filesystem. # @param config [Hash] The build config used for building Ruby. # @param definition [Bundler::Definition] The Bundler definition. - def initialize(dest_dir, config = nil, definition = nil) - @dest_dir = dest_dir + def initialize(config = nil, definition = nil) @definition = definition @config = config end @@ -14,13 +12,14 @@ def initialize(dest_dir, config = nil, definition = nil) # Packages the Ruby code into a Wasm binary. (including extensions) # # @param executor [RubyWasm::BuildExecutor] The executor for building the Wasm binary. + # @param dest_dir [String] The destination used to construct the filesystem. # @param options [Hash] The packaging options. # @return [Array] The bytes of the packaged Wasm binary. - def package(executor, options) - ruby_core = RubyWasm::Packager::Core.new(self) + def package(executor, dest_dir, options) + ruby_core = self.ruby_core_build() tarball = ruby_core.build(executor, options) - fs = RubyWasm::Packager::FileSystem.new(@dest_dir, self) + fs = RubyWasm::Packager::FileSystem.new(dest_dir, self) fs.package_ruby_root(tarball, executor) ruby_wasm_bin = File.expand_path("bin/ruby", fs.ruby_root) @@ -40,6 +39,10 @@ def package(executor, options) wasm_bytes end + def ruby_core_build + @ruby_core_build ||= RubyWasm::Packager::Core.new(self) + end + # The list of excluded gems from the Bundler definition. EXCLUDED_GEMS = %w[ruby_wasm bundler] @@ -120,8 +123,7 @@ def build_options src: "3.3", default_exts: ALL_DEFAULT_EXTS } - override = {} - override = @config["build_options"] || {} if @config + override = @config || {} # Merge the default options with the config options default.merge(override) end diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index de91b0206c..408423ac37 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -1,3 +1,5 @@ +require "forwardable" + class RubyWasm::Packager::Core def initialize(packager) @packager = packager @@ -8,6 +10,10 @@ def build(executor, options) strategy.build(executor, options) end + extend Forwardable + + def_delegators :build_strategy, :cache_key, :artifact + private def build_strategy @@ -44,6 +50,14 @@ def specs_with_extensions [spec, exts] end end + + def cache_key(digest) + raise NotImplementedError + end + + def artifact + raise NotImplementedError + end end class DynamicLinking < BuildStrategy @@ -51,6 +65,27 @@ class DynamicLinking < BuildStrategy class StaticLinking < BuildStrategy def build(executor, options) + build = derive_build + if defined?(Bundler) + Bundler.with_unbundled_env do + build.crossruby.build(executor, remake: options[:remake]) + end + else + build.crossruby.build(executor, remake: options[:remake]) + end + build.crossruby.artifact + end + + def cache_key(digest) + derive_build.cache_key(digest) + end + + def artifact + derive_build.crossruby.artifact + end + + def derive_build + return @build if @build @build ||= RubyWasm::Build.new(name, **@packager.full_build_options) @build.crossruby.user_exts = user_exts @build.crossruby.debugflags = %w[-g] @@ -63,14 +98,7 @@ def build(executor, options) -Xlinker stack-size=16777216 ] - if defined?(Bundler) - Bundler.with_unbundled_env do - @build.crossruby.build(executor, remake: options[:remake]) - end - else - @build.crossruby.build(executor, remake: options[:remake]) - end - @build.crossruby.artifact + @build end def user_exts @@ -94,7 +122,7 @@ def name options = @packager.full_build_options src_channel = options[:src][:name] target_triplet = options[:target] - base = "ruby-#{src_channel}-static-#{target_triplet}" + base = "ruby-#{src_channel}-#{target_triplet}#{options[:suffix]}" exts = specs_with_extensions.sort hash = ::Digest::MD5.new specs_with_extensions.each { |spec, _| hash << spec.full_name } diff --git a/lib/ruby_wasm/packager/file_system.rb b/lib/ruby_wasm/packager/file_system.rb index d690fe7a9f..99d6bc02cc 100644 --- a/lib/ruby_wasm/packager/file_system.rb +++ b/lib/ruby_wasm/packager/file_system.rb @@ -83,8 +83,13 @@ def remove_non_runtime_files(executor) end end - def bundle_dir = File.join(@dest_dir, bundle_relative_path) - def ruby_root = @ruby_root + def bundle_dir + File.join(@dest_dir, bundle_relative_path) + end + + def ruby_root + @ruby_root + end private diff --git a/tasks/ci.rake b/tasks/ci.rake index 4009b76b0d..b02a885487 100644 --- a/tasks/ci.rake +++ b/tasks/ci.rake @@ -54,11 +54,8 @@ def rake_task_matrix { task: "build:#{build.name}", artifact: - Pathname - .new(build.crossruby.artifact) - .relative_path_from(LIB_ROOT) - .to_s, - artifact_name: File.basename(build.crossruby.artifact, ".tar.gz"), + Pathname.new(build.artifact).relative_path_from(LIB_ROOT).to_s, + artifact_name: File.basename(build.artifact, ".tar.gz"), builder: build.target, rubies_cache_key: ruby_cache_keys[build.name] } diff --git a/tasks/gem.rake b/tasks/gem.rake index acf41b7003..096af20c1c 100644 --- a/tasks/gem.rake +++ b/tasks/gem.rake @@ -7,6 +7,9 @@ Rake::TestTask.new(:test) do |t| t.test_files = FileList["test/**/test_*.rb"] end -require "rb_sys/extensiontask" +begin + require "rb_sys/extensiontask" -RbSys::ExtensionTask.new("ruby_wasm") { |ext| ext.lib_dir = "lib/ruby_wasm" } + RbSys::ExtensionTask.new("ruby_wasm") { |ext| ext.lib_dir = "lib/ruby_wasm" } +rescue LoadError +end From 5be8b84b4e4846cb09c673e7edf71a86688aa5a9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 00:35:05 +0000 Subject: [PATCH 14/44] Write to logger instead of stderr directly --- lib/ruby_wasm/cli.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 182aaead13..19b0193faa 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -173,13 +173,13 @@ def do_print_ruby_cache_key(packager) def do_build(executor, tmpdir, packager, options) wasm_bytes = packager.package(executor, tmpdir, options) - @stderr.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}" + RubyWasm.logger.info "Size: #{SizeFormatter.format(wasm_bytes.size)}" case options[:output] when "-" @stdout.write wasm_bytes.pack("C*") else File.binwrite(options[:output], wasm_bytes.pack("C*")) - @stderr.puts "Wrote #{options[:output]}" + RubyWasm.logger.debug "Wrote #{options[:output]}" end end end From 86950875fb8647d0c47f798f3346528254aff899 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 00:48:36 +0000 Subject: [PATCH 15/44] Defer loading of ruby_wasm.so until it is needed --- lib/ruby_wasm/cli.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 19b0193faa..6db713be59 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -1,6 +1,5 @@ require "optparse" require "rbconfig" -require_relative "ruby_wasm.so" module RubyWasm class CLI @@ -172,6 +171,7 @@ def do_print_ruby_cache_key(packager) end def do_build(executor, tmpdir, packager, options) + require_relative "ruby_wasm.so" wasm_bytes = packager.package(executor, tmpdir, options) RubyWasm.logger.info "Size: #{SizeFormatter.format(wasm_bytes.size)}" case options[:output] From 20b769fcefb4b78e12a4f9a916fc692be91c783b Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 00:50:41 +0000 Subject: [PATCH 16/44] Set the build directory for the toolchain --- lib/ruby_wasm/packager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index c923c63830..bb219178d8 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -131,9 +131,9 @@ def build_options # Retrieves the resolved build options def full_build_options options = build_options - toolchain = RubyWasm::Toolchain.get(options[:target]) build_dir = File.join(root, "build") rubies_dir = File.join(root, "rubies") + toolchain = RubyWasm::Toolchain.get(options[:target], build_dir) src = if options[:src].is_a?(Hash) options[:src] From a9e0a086504ad0f3458a6b70d78dde50d8ca080b Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 00:54:49 +0000 Subject: [PATCH 17/44] `rake compile` before build --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f15dd59eb..85f59edd32 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -117,6 +117,7 @@ jobs: ruby-version: "3.2" bundler-cache: true - run: ./bin/setup + - run: rake compile - name: Pre-release configuration run: rake ${{ matrix.entry.prerelease }}[${{ inputs.prerel_name }}] if: ${{ inputs.prerel_name != '' && matrix.entry.prerelease != '' }} From 56aa83a8fe354c20013a39b324941516d32c1088 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 02:11:59 +0000 Subject: [PATCH 18/44] Fix `rake check:type` --- Steepfile | 4 ++ lib/ruby_wasm/build.rb | 6 +- lib/ruby_wasm/cli.rb | 7 ++- lib/ruby_wasm/packager.rb | 21 ++++--- lib/ruby_wasm/packager/core.rb | 25 ++++---- lib/ruby_wasm/packager/file_system.rb | 4 +- sig/ruby_wasm/build.rbs | 49 +++++++++++++-- sig/ruby_wasm/cli.rbs | 31 +++------ sig/ruby_wasm/ext.rbs | 11 ++++ sig/ruby_wasm/packager.rbs | 91 +++++++++++++++++++++++++++ sig/ruby_wasm/util.rbs | 5 ++ 11 files changed, 201 insertions(+), 53 deletions(-) create mode 100644 sig/ruby_wasm/ext.rbs create mode 100644 sig/ruby_wasm/packager.rbs create mode 100644 sig/ruby_wasm/util.rbs diff --git a/Steepfile b/Steepfile index 5f3a0304af..6bb7851cc5 100644 --- a/Steepfile +++ b/Steepfile @@ -15,6 +15,10 @@ target :lib do library "shellwords" library "io-console" library "optparse" + library "json" + library "logger" + library "pathname" + library "forwardable" configure_code_diagnostics(D::Ruby.default) end diff --git a/lib/ruby_wasm/build.rb b/lib/ruby_wasm/build.rb index 8c075969ac..ade5366533 100644 --- a/lib/ruby_wasm/build.rb +++ b/lib/ruby_wasm/build.rb @@ -33,9 +33,9 @@ def initialize( name, target:, src:, - toolchain: nil, - build_dir: nil, - rubies_dir: nil, + toolchain:, + build_dir:, + rubies_dir:, **options ) @target = target diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 6db713be59..324d9d9058 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -151,9 +151,10 @@ def build_config(options) end def derive_packager(options) - if defined?(Bundler) && !options[:disable_gems] - definition = Bundler.definition - end + __skip__ = + if defined?(Bundler) && !options[:disable_gems] + definition = Bundler.definition + end RubyWasm::Packager.new(build_config(options), definition) end diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index bb219178d8..fad0a162fa 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -64,18 +64,19 @@ def support_dynamic_linking? # * rubies # * build def root - @root ||= - begin - if explicit = ENV["RUBY_WASM_ROOT"] - File.expand_path(explicit) - elsif defined?(Bundler) - Bundler.root - else + __skip__ = + @root ||= + begin + if explicit = ENV["RUBY_WASM_ROOT"] + File.expand_path(explicit) + elsif defined?(Bundler) + Bundler.root + else + Dir.pwd + end + rescue Bundler::GemfileNotFound Dir.pwd end - rescue Bundler::GemfileNotFound - Dir.pwd - end end # Retrieves the alias definitions for the Ruby sources. diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 408423ac37..9c77f64101 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -66,13 +66,14 @@ class DynamicLinking < BuildStrategy class StaticLinking < BuildStrategy def build(executor, options) build = derive_build - if defined?(Bundler) - Bundler.with_unbundled_env do + __skip__ = + if defined?(Bundler) + Bundler.with_unbundled_env do + build.crossruby.build(executor, remake: options[:remake]) + end + else build.crossruby.build(executor, remake: options[:remake]) end - else - build.crossruby.build(executor, remake: options[:remake]) - end build.crossruby.artifact end @@ -86,11 +87,12 @@ def artifact def derive_build return @build if @build - @build ||= RubyWasm::Build.new(name, **@packager.full_build_options) - @build.crossruby.user_exts = user_exts - @build.crossruby.debugflags = %w[-g] - @build.crossruby.wasmoptflags = %w[-O3 -g] - @build.crossruby.ldflags = %w[ + __skip__ = + build ||= RubyWasm::Build.new(name, **@packager.full_build_options) + build.crossruby.user_exts = user_exts + build.crossruby.debugflags = %w[-g] + build.crossruby.wasmoptflags = %w[-O3 -g] + build.crossruby.ldflags = %w[ -Xlinker --stack-first -Xlinker @@ -98,7 +100,8 @@ def derive_build -Xlinker stack-size=16777216 ] - @build + @build = build + build end def user_exts diff --git a/lib/ruby_wasm/packager/file_system.rb b/lib/ruby_wasm/packager/file_system.rb index 99d6bc02cc..8aebb70a87 100644 --- a/lib/ruby_wasm/packager/file_system.rb +++ b/lib/ruby_wasm/packager/file_system.rb @@ -150,6 +150,8 @@ def ruby_version end def rubyarchdir - Dir.glob(File.join(@ruby_root, "lib", "ruby", "*", "wasm32-wasi")).first + maybe = + Dir.glob(File.join(@ruby_root, "lib", "ruby", "*", "wasm32-wasi")).first + maybe || raise("Cannot find rubyarchdir") end end diff --git a/sig/ruby_wasm/build.rbs b/sig/ruby_wasm/build.rbs index 9a9838f672..8b0b9a0a26 100644 --- a/sig/ruby_wasm/build.rbs +++ b/sig/ruby_wasm/build.rbs @@ -1,10 +1,45 @@ module RubyWasm VERSION: String + interface _Cacheable + def cache_key: (Digest::SHA256 digest) -> void + end + + class Build + include _Cacheable + + @target: String + @build_dir: String + @rubies_dir: String + @toolchain: Toolchain + @libyaml: LibYAMLProduct + @zlib: ZlibProduct + @openssl: OpenSSLProduct + @wasi_vfs: WasiVfsProduct + @baseruby: BaseRubyProduct + @crossruby: CrossRubyProduct + @source: BuildSource + + attr_reader toolchain: Toolchain + + def initialize: ( + string name, + target: String, + src: Packager::build_source, + toolchain: Toolchain?, + build_dir: string, + rubies_dir: string, + **untyped + ) -> void + def crossruby: () -> CrossRubyProduct + end + class BuildParams attr_accessor name: String attr_accessor target: String attr_accessor default_exts: String + + def initialize: (name: string, target: string, default_exts: string) -> void end class BuildProduct @@ -109,21 +144,28 @@ module RubyWasm end class CrossRubyExtProduct < BuildProduct + include RubyWasm::_Cacheable + @toolchain: Toolchain @srcdir: String + @ext_relative_path: String attr_reader name: String - def initialize: (String srcdir, Toolchain toolchain, ?name: nil) -> void + def initialize: (String srcdir, Toolchain toolchain, ?ext_relative_path: String?) -> void def product_build_dir: (CrossRubyProduct crossruby) -> String def linklist: (CrossRubyProduct crossruby) -> String + def metadata_json: (CrossRubyProduct crossruby) -> String + def feature_name: (CrossRubyProduct crossruby) -> String + def make_args: (CrossRubyProduct crossruby) -> Array[String] def build: (BuildExecutor executor, CrossRubyProduct crossruby) -> void def do_extconf: (BuildExecutor executor, CrossRubyProduct crossruby) -> void def do_install_rb: (BuildExecutor executor, CrossRubyProduct crossruby) -> void - def cache_key: (Digest::SHA256 digest) -> void end class CrossRubyProduct < AutoconfProduct + include RubyWasm::_Cacheable + @params: BuildParams @rubies_dir: String @build_dir: String @@ -143,13 +185,12 @@ module RubyWasm attr_accessor debugflags: Array[String] attr_accessor xcflags: Array[String] attr_accessor xldflags: Array[String] - def initialize: (BuildParams params, String build_dir, String rubies_dir, BaseRubyProduct baseruby, BuildSource source, Toolchain toolchain, ?user_exts: Array[CrossRubyExtProduct]) -> void + def initialize: (BuildParams params, String build_dir, String rubies_dir, BaseRubyProduct baseruby, BuildSource source, Toolchain toolchain) -> void def configure: (BuildExecutor executor, ?reconfigure: bool) -> void def build_exts: (BuildExecutor executor) -> void def build: (BuildExecutor executor, ?remake: bool, ?reconfigure: bool) -> void def clean: (BuildExecutor executor) -> void def name: -> String - def cache_key: (Digest::SHA256 digest) -> void def build_dir: -> String def ext_build_dir: -> String def with_libyaml: (LibYAMLProduct libyaml) -> LibYAMLProduct diff --git a/sig/ruby_wasm/cli.rbs b/sig/ruby_wasm/cli.rbs index 88ebe7749b..1392ef3079 100644 --- a/sig/ruby_wasm/cli.rbs +++ b/sig/ruby_wasm/cli.rbs @@ -4,32 +4,21 @@ module RubyWasm @stdout: IO @stderr: IO - @rubies_dir: string - def initialize: (stdout: IO, stderr: IO, rubies_dir: string) -> void + def initialize: (stdout: IO, stderr: IO) -> void def build: (Array[String] args) -> void - private def do_build: (String tarball, RubyWasm::BuildExecutor executor, String output_dir, Hash[untyped, untyped] options) -> void - end - - class RubyWasmExt - def self.preinitialize: (Array[Integer] module_bytes) -> Array[Integer] - - class WasmInject - def initialize: (Array[Integer] module_bytes) -> void - - def add_import_func: (String module_name, String func_name, Array[Symbol] params, Array[Symbol] return_type) -> void + private - def run: () -> Array[Integer] - end + def build_config: (Hash[untyped, untyped] options) -> Hash[untyped, untyped] - class WasiVfs - def initialize: () -> void - - def map_dir: (String guest_path, String host_path) -> void - - def pack: (Array[Integer] module_bytes) -> Array[Integer] - end + def derive_packager: (Hash[untyped, untyped] options) -> Packager + def do_print_ruby_cache_key: (Packager) -> void + def do_build: (BuildExecutor, string tmpdir, Packager, Hash[untyped, untyped] options) -> void end + + self.@logger: Logger? + def self.logger: () -> Logger + attr_accessor self.log_level: Symbol end diff --git a/sig/ruby_wasm/ext.rbs b/sig/ruby_wasm/ext.rbs new file mode 100644 index 0000000000..115fd7b87f --- /dev/null +++ b/sig/ruby_wasm/ext.rbs @@ -0,0 +1,11 @@ +module RubyWasmExt + def self.preinitialize: (Array[Integer] module_bytes) -> Array[Integer] + + class WasiVfs + def initialize: () -> void + + def map_dir: (String guest_path, String host_path) -> void + + def pack: (Array[Integer] module_bytes) -> Array[Integer] + end +end \ No newline at end of file diff --git a/sig/ruby_wasm/packager.rbs b/sig/ruby_wasm/packager.rbs new file mode 100644 index 0000000000..f0220c78ba --- /dev/null +++ b/sig/ruby_wasm/packager.rbs @@ -0,0 +1,91 @@ + +class RubyWasm::Packager + @definition: untyped + @config: Hash[untyped, untyped] + + def initialize: (untyped?, Hash[untyped, untyped]?) -> void + + def package: (RubyWasm::BuildExecutor, string dest_dir, untyped options) -> Array[Integer] + + @ruby_core_build: RubyWasm::Packager::Core? + def ruby_core_build: () -> RubyWasm::Packager::Core + + EXCLUDED_GEMS: Array[string] + + def specs: () -> Array[untyped] + def support_dynamic_linking?: () -> bool + + def root: () -> string + + type build_source = Hash[Symbol, (String | Array[String])] + def self.build_source_aliases: (string root) -> Hash[string, build_source] + + ALL_DEFAULT_EXTS: string + + def build_options: () -> Hash[Symbol, untyped] + def full_build_options: () -> Hash[Symbol, untyped] + + class Core + include RubyWasm::_Cacheable + + @packager: RubyWasm::Packager + def initialize: (RubyWasm::Packager) -> void + def build: (RubyWasm::BuildExecutor, untyped options) -> String + + extend Forwardable + + def artifact: () -> string + + private + + @build_strategy: BuildStrategy? + def build_strategy: () -> BuildStrategy + + class BuildStrategy + @packager: RubyWasm::Packager + def initialize: (RubyWasm::Packager) -> void + def build: (RubyWasm::BuildExecutor, untyped options) -> String + def specs_with_extensions: () -> Array[[untyped, Array[string]]] + end + + class DynamicLinking < RubyWasm::Packager::Core::BuildStrategy + end + + class StaticLinking < RubyWasm::Packager::Core::BuildStrategy + @build: RubyWasm::Build + def derive_build: () -> RubyWasm::Build + @user_exts: Array[RubyWasm::CrossRubyExtProduct]? + def user_exts: () -> Array[RubyWasm::CrossRubyExtProduct] + + def name: () -> string + end + end + + class FileSystem + @dest_dir: string + @packager: RubyWasm::Packager + @ruby_root: string + + def initialize: (string dest_dir, RubyWasm::Packager) -> void + def package_ruby_root: (String tarball, RubyWasm::BuildExecutor) -> void + def remove_stdlib: (RubyWasm::BuildExecutor) -> void + def package_gems: () -> void + + def setup_rb_content: () -> String + + def remove_non_runtime_files: (RubyWasm::BuildExecutor) -> void + + def bundle_dir: () -> String + def ruby_root: () -> string + + private + + def each_gem_content_path: () { (String, String) -> void } -> void + def each_gem_require_path: () { (String, String) -> void } -> void + def each_gem_extension_path: () { (String, String) -> void } -> void + + def bundle_relative_path: () -> String + def ruby_version: () -> String + def rubyarchdir: () -> String + end +end \ No newline at end of file diff --git a/sig/ruby_wasm/util.rbs b/sig/ruby_wasm/util.rbs new file mode 100644 index 0000000000..d011e7a87f --- /dev/null +++ b/sig/ruby_wasm/util.rbs @@ -0,0 +1,5 @@ +module RubyWasm + module SizeFormatter + def self?.format: (Integer size) -> String + end +end \ No newline at end of file From c166dfad7ea365e10b104603aa44859b24a37cae Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 02:16:54 +0000 Subject: [PATCH 19/44] Add error message when gem compilation is skipped --- tasks/gem.rake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tasks/gem.rake b/tasks/gem.rake index 096af20c1c..b051a8272a 100644 --- a/tasks/gem.rake +++ b/tasks/gem.rake @@ -11,5 +11,9 @@ begin require "rb_sys/extensiontask" RbSys::ExtensionTask.new("ruby_wasm") { |ext| ext.lib_dir = "lib/ruby_wasm" } -rescue LoadError +rescue LoadError => e + task :compile do + $stderr.puts "Skipping compilation of ruby_wasm extension: #{e.message}" + exit 1 + end end From d7b7c65a0e5a9c8c60b5480f147291d3cab233e5 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 02:25:41 +0000 Subject: [PATCH 20/44] rake in bundle exec --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85f59edd32..c6506bdd1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -117,9 +117,9 @@ jobs: ruby-version: "3.2" bundler-cache: true - run: ./bin/setup - - run: rake compile + - run: bundle exec rake compile - name: Pre-release configuration - run: rake ${{ matrix.entry.prerelease }}[${{ inputs.prerel_name }}] + run: bundle exec rake ${{ matrix.entry.prerelease }}[${{ inputs.prerel_name }}] if: ${{ inputs.prerel_name != '' && matrix.entry.prerelease != '' }} - name: rake ${{ matrix.entry.task }} run: docker run -v "$GITHUB_WORKSPACE:/home/me/build" -w /home/me/build -e "ENABLE_GITHUB_ACTIONS_MARKUP=true" -e "RUBYWASM_UID=$(id -u)" -e "RUBYWASM_GID=$(id -g)" -e "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" ${{ steps.builder-image.outputs.imageid }} rake --verbose ${{ matrix.entry.task }} From 4e1dd12c71eb5e4c88e3591c22362d197a421212 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 02:29:14 +0000 Subject: [PATCH 21/44] Remove local path from Cargo.toml --- Cargo.lock | 1 + ext/ruby_wasm/Cargo.toml | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e6d70cde3..e70cbebd3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1792,6 +1792,7 @@ dependencies = [ [[package]] name = "wasi-vfs-cli" version = "0.4.0" +source = "git+https://github.com/kateinoigakukun/wasi-vfs/?rev=b1e4e5d9cd6322e8745e67c092b495973835a94f#b1e4e5d9cd6322e8745e67c092b495973835a94f" dependencies = [ "anyhow", "structopt", diff --git a/ext/ruby_wasm/Cargo.toml b/ext/ruby_wasm/Cargo.toml index 912021e309..82672798e6 100644 --- a/ext/ruby_wasm/Cargo.toml +++ b/ext/ruby_wasm/Cargo.toml @@ -14,5 +14,4 @@ magnus = "0.6.2" wizer = "3.0.0" wasmtime-wasi = "9.0.4" wasi-cap-std-sync = "9.0.4" -# wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", tag = "v0.5.0" } -wasi-vfs-cli = { path = "/home/katei/ghq/github.com/kateinoigakukun/wasi-vfs/crates/wasi-vfs-cli" } +wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", rev = "b1e4e5d9cd6322e8745e67c092b495973835a94f" } From 2a800080443dd64ee5dcce82fcbc39da27617b72 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 02:32:20 +0000 Subject: [PATCH 22/44] Fix paths to .wit files in check.rake --- tasks/check.rake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/check.rake b/tasks/check.rake index c02e82999d..95b1f1fa5a 100644 --- a/tasks/check.rake +++ b/tasks/check.rake @@ -5,8 +5,8 @@ namespace :check do end task bindgen_c: :install_wit_bindgen do wits = [ - %w[ext/witapi/bindgen/rb-abi-guest.wit --export], - %w[ext/js/bindgen/rb-js-abi-host.wit --import] + %w[packages/gems/js/ext/witapi/bindgen/rb-abi-guest.wit --export], + %w[packages/gems/js/ext/js/bindgen/rb-js-abi-host.wit --import] ] wits.each do |wit| path, mode = wit @@ -20,9 +20,9 @@ namespace :check do "host", "js", "--import", - "ext/witapi/bindgen/rb-abi-guest.wit", + "packages/gems/js/ext/witapi/bindgen/rb-abi-guest.wit", "--export", - "ext/js/bindgen/rb-js-abi-host.wit", + "packages/gems/js/ext/js/bindgen/rb-js-abi-host.wit", "--out-dir", "packages/npm-packages/ruby-wasm-wasi/src/bindgen" ] From a9623e2f8891a2a592e14e7268b7e7f7fed6c74f Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 02:46:46 +0000 Subject: [PATCH 23/44] Install ruby-bundler package in builder --- builders/wasm32-unknown-emscripten/Dockerfile | 2 +- builders/wasm32-unknown-wasi/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builders/wasm32-unknown-emscripten/Dockerfile b/builders/wasm32-unknown-emscripten/Dockerfile index 35148ac156..fbe6b2694a 100644 --- a/builders/wasm32-unknown-emscripten/Dockerfile +++ b/builders/wasm32-unknown-emscripten/Dockerfile @@ -4,7 +4,7 @@ ARG NODE_MAJOR_VERSION=20 RUN set -eux; \ apt-get update; \ - apt-get install ruby bison make autoconf git curl build-essential libyaml-dev zlib1g-dev gosu -y; \ + apt-get install ruby ruby-bundler bison make autoconf git curl build-essential libyaml-dev zlib1g-dev gosu -y; \ apt-get install -y ca-certificates curl gnupg; \ mkdir -p /etc/apt/keyrings; \ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \ diff --git a/builders/wasm32-unknown-wasi/Dockerfile b/builders/wasm32-unknown-wasi/Dockerfile index 270d6e0056..13b06cd1f4 100644 --- a/builders/wasm32-unknown-wasi/Dockerfile +++ b/builders/wasm32-unknown-wasi/Dockerfile @@ -5,7 +5,7 @@ ARG NODE_MAJOR_VERSION=20 RUN set -eux; \ apt-get update; \ - apt-get install ruby bison make autoconf git curl build-essential libyaml-dev zlib1g-dev gosu -y; \ + apt-get install ruby ruby-bundler bison make autoconf git curl build-essential libyaml-dev zlib1g-dev gosu -y; \ apt-get install -y ca-certificates curl gnupg; \ mkdir -p /etc/apt/keyrings; \ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \ From acf5e12ef25e1045ddced46b8e37e9cab050d627 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 02:48:21 +0000 Subject: [PATCH 24/44] Pin ruby/ruby master version The latest master broke the build. This commit pins the version to the last known working commit. --- lib/ruby_wasm/packager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index fad0a162fa..9d3bb21042 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -86,7 +86,7 @@ def self.build_source_aliases(root) "head" => { type: "github", repo: "ruby/ruby", - rev: "master", + rev: "997124fc0b7697bb9383e8feb8e1d88017c4bcb8", patches: patches.map { |p| File.expand_path(p) } }, "3.3" => { From ad9091063fb5ea68fefa276154d59c7d3e3c540b Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 14:48:22 +0000 Subject: [PATCH 25/44] Build ext library on builder container Since there is no guarantee that the host system and the builder container use the same Ruby ABI --- .github/workflows/build.yml | 23 ++++++++++++------- .gitignore | 2 ++ Gemfile | 5 +++- bin/setup | 5 +++- builders/wasm32-unknown-emscripten/Dockerfile | 21 +++++++++++++++-- builders/wasm32-unknown-wasi/Dockerfile | 21 +++++++++++++++-- lib/ruby_wasm/packager/core.rb | 6 ++--- .../ruby-wasm-wasi/tools/pack-ruby-wasm.sh | 5 +++- sig/ruby_wasm/packager.rbs | 4 ++-- 9 files changed, 72 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c6506bdd1d..e0c330be98 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,17 +112,24 @@ jobs: load: true cache-from: type=gha cache-to: type=gha,mode=max - - uses: ruby/setup-ruby@v1 - with: - ruby-version: "3.2" - bundler-cache: true - - run: ./bin/setup - - run: bundle exec rake compile + - name: Setup builder container + run: | + docker run -dit -v "$GITHUB_WORKSPACE:/home/me/build" -w /home/me/build \ + -e "ENABLE_GITHUB_ACTIONS_MARKUP=true" \ + -e "RUBYWASM_UID=$(id -u)" -e "RUBYWASM_GID=$(id -g)" \ + -e "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" \ + --name builder \ + ${{ steps.builder-image.outputs.imageid }} /bin/sh + echo 'docker exec -u me builder "$@"' > ./build-exec + chmod +x ./build-exec + - run: ./build-exec bundle config set --local without check + - run: ./build-exec ./bin/setup + - run: ./build-exec bundle exec rake compile - name: Pre-release configuration - run: bundle exec rake ${{ matrix.entry.prerelease }}[${{ inputs.prerel_name }}] + run: ./build-exec bundle exec rake ${{ matrix.entry.prerelease }}[${{ inputs.prerel_name }}] if: ${{ inputs.prerel_name != '' && matrix.entry.prerelease != '' }} - name: rake ${{ matrix.entry.task }} - run: docker run -v "$GITHUB_WORKSPACE:/home/me/build" -w /home/me/build -e "ENABLE_GITHUB_ACTIONS_MARKUP=true" -e "RUBYWASM_UID=$(id -u)" -e "RUBYWASM_GID=$(id -g)" -e "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" ${{ steps.builder-image.outputs.imageid }} rake --verbose ${{ matrix.entry.task }} + run: ./build-exec rake --verbose ${{ matrix.entry.task }} - name: rake ${{ matrix.entry.test }} run: rake ${{ matrix.entry.test }} if: ${{ matrix.entry.test != '' }} diff --git a/.gitignore b/.gitignore index a342a45097..49fde32b63 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ compile_commands.json html build_manifest.json + +vendor/bundle diff --git a/Gemfile b/Gemfile index 5cb6599bc9..a3ef79e8b7 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,10 @@ group :development do gem "rake" gem "rake-compiler" gem "rb_sys", "~> 0.9.63" - gem "syntax_tree", "~> 3.5" gem "webrick" +end + +group :check do + gem "syntax_tree", "~> 3.5" gem "steep" end diff --git a/bin/setup b/bin/setup index ffa93c6bcc..b98faf9dbc 100755 --- a/bin/setup +++ b/bin/setup @@ -3,5 +3,8 @@ set -euo pipefail IFS=$'\n\t' set -vx -bundle install +root="$(cd "$(dirname "$0")/.." && pwd)" + +env BUNDLE_GEMFILE="$root/Gemfile" bundle install +env BUNDLE_GEMFILE="$root/packages/npm-packages/ruby-wasm-wasi/Gemfile" bundle install npm ci diff --git a/builders/wasm32-unknown-emscripten/Dockerfile b/builders/wasm32-unknown-emscripten/Dockerfile index fbe6b2694a..931480b61c 100644 --- a/builders/wasm32-unknown-emscripten/Dockerfile +++ b/builders/wasm32-unknown-emscripten/Dockerfile @@ -4,16 +4,33 @@ ARG NODE_MAJOR_VERSION=20 RUN set -eux; \ apt-get update; \ - apt-get install ruby ruby-bundler bison make autoconf git curl build-essential libyaml-dev zlib1g-dev gosu -y; \ apt-get install -y ca-certificates curl gnupg; \ mkdir -p /etc/apt/keyrings; \ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR_VERSION.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list; \ apt-get update; \ - apt-get install nodejs -y; \ + apt-get install ruby ruby-dev ruby-bundler nodejs \ + bison make autoconf git curl build-essential \ + libyaml-dev zlib1g-dev gosu \ + libclang-13-dev -y; \ apt-get clean; \ rm -r /var/lib/apt/lists/* +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH \ + RUST_VERSION=1.74 + +RUN set -eux pipefail; \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ + sh -s -- -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION; \ + chmod -R a+w $RUSTUP_HOME $CARGO_HOME + +ENV BUNDLE_PATH=/usr/local/gems +RUN set -eux; \ + mkdir -p $BUNDLE_PATH; \ + chmod -R 777 $BUNDLE_PATH + COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/builders/wasm32-unknown-wasi/Dockerfile b/builders/wasm32-unknown-wasi/Dockerfile index 13b06cd1f4..a02cd36a7e 100644 --- a/builders/wasm32-unknown-wasi/Dockerfile +++ b/builders/wasm32-unknown-wasi/Dockerfile @@ -5,13 +5,15 @@ ARG NODE_MAJOR_VERSION=20 RUN set -eux; \ apt-get update; \ - apt-get install ruby ruby-bundler bison make autoconf git curl build-essential libyaml-dev zlib1g-dev gosu -y; \ apt-get install -y ca-certificates curl gnupg; \ mkdir -p /etc/apt/keyrings; \ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; \ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR_VERSION.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list; \ apt-get update; \ - apt-get install nodejs -y; \ + apt-get install ruby ruby-dev ruby-bundler nodejs \ + bison make autoconf git curl build-essential \ + libyaml-dev zlib1g-dev gosu \ + libclang-13-dev -y; \ apt-get clean; \ rm -r /var/lib/apt/lists/* @@ -21,6 +23,21 @@ RUN set -eux pipefail; \ unzip wasi-preset-args-x86_64-unknown-linux-gnu.zip; \ mv wasi-preset-args /usr/local/bin/wasi-preset-args +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH \ + RUST_VERSION=1.74 + +RUN set -eux pipefail; \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ + sh -s -- -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION; \ + chmod -R a+w $RUSTUP_HOME $CARGO_HOME + +ENV BUNDLE_PATH=/usr/local/gems +RUN set -eux; \ + mkdir -p $BUNDLE_PATH; \ + chmod -R 777 $BUNDLE_PATH + COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 9c77f64101..83dfdc9d87 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -89,7 +89,7 @@ def derive_build return @build if @build __skip__ = build ||= RubyWasm::Build.new(name, **@packager.full_build_options) - build.crossruby.user_exts = user_exts + build.crossruby.user_exts = user_exts(build) build.crossruby.debugflags = %w[-g] build.crossruby.wasmoptflags = %w[-O3 -g] build.crossruby.ldflags = %w[ @@ -104,7 +104,7 @@ def derive_build build end - def user_exts + def user_exts(build) @user_exts ||= specs_with_extensions.flat_map do |spec, exts| exts.map do |ext| @@ -113,7 +113,7 @@ def user_exts ext_relative_path = File.join(spec.full_name, ext_feature) RubyWasm::CrossRubyExtProduct.new( ext_srcdir, - @build.toolchain, + build.toolchain, ext_relative_path: ext_relative_path ) end diff --git a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh b/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh index 342f3f0a3b..66f3d0ba29 100755 --- a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh +++ b/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh @@ -18,9 +18,12 @@ package_dir="$(cd "$(dirname "$0")/.." && pwd)" mkdir -p "$dist_dir" # Cache rubies in the package dir -export RUBY_WASM_ROOT="$package_dir/../../../" +export RUBY_WASM_ROOT="$package_dir/../../.." +export BUNDLE_GEMFILE="$package_dir/Gemfile" cd "$package_dir" +echo "$0: Entering $package_dir" + rbwasm_options="--ruby-version $ruby_version --target wasm32-unknown-wasi --build-profile full" bundle exec rbwasm build ${rbwasm_options[@]} --no-stdlib -o "$dist_dir/ruby.wasm" "$WASMOPT" --strip-debug "$dist_dir/ruby.wasm" -o "$dist_dir/ruby.wasm" diff --git a/sig/ruby_wasm/packager.rbs b/sig/ruby_wasm/packager.rbs index f0220c78ba..999d147f3e 100644 --- a/sig/ruby_wasm/packager.rbs +++ b/sig/ruby_wasm/packager.rbs @@ -55,7 +55,7 @@ class RubyWasm::Packager @build: RubyWasm::Build def derive_build: () -> RubyWasm::Build @user_exts: Array[RubyWasm::CrossRubyExtProduct]? - def user_exts: () -> Array[RubyWasm::CrossRubyExtProduct] + def user_exts: (RubyWasm::Build) -> Array[RubyWasm::CrossRubyExtProduct] def name: () -> string end @@ -88,4 +88,4 @@ class RubyWasm::Packager def ruby_version: () -> String def rubyarchdir: () -> String end -end \ No newline at end of file +end From 2b3e7570449d9bb3200f2e9ba004b0c4dbe1df81 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 15:26:59 +0000 Subject: [PATCH 26/44] Remove required_ruby_version from js.gemspec to allow host Ruby to install it --- packages/gems/js/js.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/gems/js/js.gemspec b/packages/gems/js/js.gemspec index a62cd88f3e..dbe8becb6a 100644 --- a/packages/gems/js/js.gemspec +++ b/packages/gems/js/js.gemspec @@ -11,7 +11,6 @@ Gem::Specification.new do |spec| spec.summary = "JavaScript bindings for ruby.wasm" spec.license = "MIT" - spec.required_ruby_version = ">= 3.2.0" spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject do |f| From 9a58a8503faa8da1f97f1a912515ae5e46605fae Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 15:30:20 +0000 Subject: [PATCH 27/44] Remove ruby-wasm-wasi from the list of packages to test This package is alread tested by each ruby-version-specific package --- Rakefile | 5 ----- packages/npm-packages/ruby-wasm-wasi/package.json | 2 -- 2 files changed, 7 deletions(-) diff --git a/Rakefile b/Rakefile index bdc05347c6..87f8aaed1c 100644 --- a/Rakefile +++ b/Rakefile @@ -24,11 +24,6 @@ NPM_PACKAGES = [ build: "head-wasm32-unknown-emscripten-full", target: "wasm32-unknown-emscripten" }, - { - name: "ruby-wasm-wasi", - build: "head-wasm32-unknown-wasi-full-js-debug", - target: "wasm32-unknown-wasi" - }, { name: "ruby-head-wasm-wasi", build: "head-wasm32-unknown-wasi-full-js-debug", diff --git a/packages/npm-packages/ruby-wasm-wasi/package.json b/packages/npm-packages/ruby-wasm-wasi/package.json index e599366786..d509312757 100644 --- a/packages/npm-packages/ruby-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-wasm-wasi/package.json @@ -40,8 +40,6 @@ "license": "MIT", "scripts": { "test:run": "npm run test:unit && npm run test:jest && npm run test:e2e", - "test:head": "RUBY_ROOT=../../../rubies/head-wasm32-unknown-wasi-full-js-debug npm run test:run", - "test": "npm run test:head", "test:jest": "NODE_OPTIONS=\"--experimental-wasi-unstable-preview1\" jest --coverage", "test:unit": "./tools/run-test-unit.mjs", "test:e2e": "playwright install && npm run test:e2e:examples && npm run test:e2e:integrations", From fbe64ab7002026cae985d899f43a337c930e2f9e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 15:40:03 +0000 Subject: [PATCH 28/44] Fix packaging task for standalone packages --- tasks/packaging.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/packaging.rake b/tasks/packaging.rake index 2c89c15e82..19f038ef19 100644 --- a/tasks/packaging.rake +++ b/tasks/packaging.rake @@ -72,7 +72,7 @@ namespace :standalone do wasi_sdk.install_binaryen base_dir = Dir.pwd sh tools, - "./build-package.sh #{base_dir}/rubies/#{pkg[:build]}", + "./build-package.sh #{base_dir}/rubies/ruby-#{pkg[:build]}", chdir: pkg_dir end end From 3cc776c0c90789c422eee60a39148e9f343721ca Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 15:41:53 +0000 Subject: [PATCH 29/44] Prefer apt-installed nodejs instead of emsdk's old one. --- builders/wasm32-unknown-emscripten/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builders/wasm32-unknown-emscripten/Dockerfile b/builders/wasm32-unknown-emscripten/Dockerfile index 931480b61c..0bd16e0cd5 100644 --- a/builders/wasm32-unknown-emscripten/Dockerfile +++ b/builders/wasm32-unknown-emscripten/Dockerfile @@ -16,6 +16,9 @@ RUN set -eux; \ apt-get clean; \ rm -r /var/lib/apt/lists/* +# Prefer apt-installed nodejs instead of emsdk's old one. +ENV PATH=/usr/bin:$PATH + ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo \ PATH=/usr/local/cargo/bin:$PATH \ From 1c56f892d6cb6ee5937adddb84f10a726b2e6176 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 15:51:06 +0000 Subject: [PATCH 30/44] `bundle install` on host machine before running test --- .github/workflows/build.yml | 4 +++- Gemfile | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0c330be98..1f023443d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -131,7 +131,9 @@ jobs: - name: rake ${{ matrix.entry.task }} run: ./build-exec rake --verbose ${{ matrix.entry.task }} - name: rake ${{ matrix.entry.test }} - run: rake ${{ matrix.entry.test }} + run: | + bundle install --with=check --without=development + rake ${{ matrix.entry.test }} if: ${{ matrix.entry.test != '' }} - uses: actions/upload-artifact@v4 if: ${{ matrix.entry.artifact }} diff --git a/Gemfile b/Gemfile index a3ef79e8b7..b1f12da929 100644 --- a/Gemfile +++ b/Gemfile @@ -8,10 +8,10 @@ group :development do gem "rake" gem "rake-compiler" gem "rb_sys", "~> 0.9.63" - gem "webrick" end group :check do + gem "webrick" gem "syntax_tree", "~> 3.5" gem "steep" end From 7042418bfa59c7d7a0f7c799c9ab8731e0850dd1 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 16:16:25 +0000 Subject: [PATCH 31/44] Trigger build of ruby for emscripten in npm package --- packages/npm-packages/ruby-head-wasm-emscripten/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/npm-packages/ruby-head-wasm-emscripten/package.json b/packages/npm-packages/ruby-head-wasm-emscripten/package.json index 0cb0b43311..00cb900129 100644 --- a/packages/npm-packages/ruby-head-wasm-emscripten/package.json +++ b/packages/npm-packages/ruby-head-wasm-emscripten/package.json @@ -11,7 +11,9 @@ "README.md" ], "scripts": { - "build": "./build-package.sh ../../../rubies/head-wasm32-unknown-emscripten-full" + "build:ruby": "rake build:head-wasm32-unknown-emscripten-full", + "build:package": "./build-package.sh ../../../rubies/ruby-head-wasm32-unknown-emscripten-full", + "build": "npm run build:ruby && npm run build:package" }, "repository": "https://github.com/ruby/ruby.wasm", "homepage": "https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-head-wasm-emscripten", From 4421643dd4a2ea7f9bb373ce734f65d6295e9021 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 16:17:20 +0000 Subject: [PATCH 32/44] Restore build:*:remake, reconfigure, and clean tasks --- CONTRIBUTING.md | 6 +++--- Rakefile | 11 +++++++++++ lib/ruby_wasm/packager/core.rb | 18 ++++++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1abe34f3d7..8d6d1a3d15 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,11 +46,11 @@ To select a build profile, see [profiles section in README](https://github.com/r ```console # Build only a specific combination of ruby version, profile, and target -$ rake build:head-wasm32-unknown-wasi-full-js +$ rake build:head-wasm32-unknown-wasi-full # Clean up the build directory -$ rake build:head-wasm32-unknown-wasi-full-js:clean +$ rake build:head-wasm32-unknown-wasi-full:clean # Force to re-execute "make install" -$ rake build:head-wasm32-unknown-wasi-full-js:remake +$ rake build:head-wasm32-unknown-wasi-full:remake # Output is in the `rubies` directory $ tree -L 3 rubies/head-wasm32-unknown-wasi-full-js diff --git a/Rakefile b/Rakefile index 87f8aaed1c..6eb819cf1a 100644 --- a/Rakefile +++ b/Rakefile @@ -101,6 +101,17 @@ namespace :build do task name do sh *build_command end + namespace name do + task :remake do + sh *build_command, "--remake" + end + task :reconfigure do + sh *build_command, "--reconfigure" + end + task :clean do + sh *build_command, "--clean" + end + end BuildTask.new(name, target, build_command) end diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 83dfdc9d87..937f7a6a0c 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -66,13 +66,23 @@ class DynamicLinking < BuildStrategy class StaticLinking < BuildStrategy def build(executor, options) build = derive_build + force_rebuild = options[:remake] || options[:clean] || options[:reconfigure] + if File.exist?(build.crossruby.artifact) && !force_rebuild + return build.crossruby.artifact + end + if options[:clean] + build.crossruby.clean(executor) + end + + do_build = proc do + build.crossruby.build(executor, remake: options[:remake], reconfigure: options[:reconfigure]) + end + __skip__ = if defined?(Bundler) - Bundler.with_unbundled_env do - build.crossruby.build(executor, remake: options[:remake]) - end + Bundler.with_unbundled_env(&do_build) else - build.crossruby.build(executor, remake: options[:remake]) + do_build.call end build.crossruby.artifact end From 07fe1839249188e2c97796ed4161db0cbb8e4cd0 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 16:17:54 +0000 Subject: [PATCH 33/44] Install ruby before testing to allow host to use Ruby and Bundler --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f023443d6..8ef50cdb12 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -130,6 +130,11 @@ jobs: if: ${{ inputs.prerel_name != '' && matrix.entry.prerelease != '' }} - name: rake ${{ matrix.entry.task }} run: ./build-exec rake --verbose ${{ matrix.entry.task }} + - uses: ruby/setup-ruby@v1 + if: ${{ matrix.entry.test != '' }} + with: + ruby-version: "3.2" + bundler-cache: false - name: rake ${{ matrix.entry.test }} run: | bundle install --with=check --without=development From eef3f6b4c7c3e09ff59af95b870f3a9135e4b93e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 16:21:23 +0000 Subject: [PATCH 34/44] Exclude stack-size option from ldflags when building for emscripten --- lib/ruby_wasm/packager/core.rb | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 937f7a6a0c..13d9eeb1ef 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -102,14 +102,19 @@ def derive_build build.crossruby.user_exts = user_exts(build) build.crossruby.debugflags = %w[-g] build.crossruby.wasmoptflags = %w[-O3 -g] - build.crossruby.ldflags = %w[ - -Xlinker - --stack-first - -Xlinker - -z - -Xlinker - stack-size=16777216 - ] + # Emscripten uses --global-base=1024 by default, but it conflicts with + # --stack-first and -z stack-size since global-base 1024 is smaller than + # the large stack size. + if @packager.full_build_options[:target] != "wasm32-unknown-emscripten" + build.crossruby.ldflags = %w[ + -Xlinker + --stack-first + -Xlinker + -z + -Xlinker + stack-size=16777216 + ] + end @build = build build end From 989ef458e9deb2e2aebd182d359127134c9b1bca Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 16:57:35 +0000 Subject: [PATCH 35/44] Exclude -g flags for Emscripten build --- lib/ruby_wasm/packager/core.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 13d9eeb1ef..2470a0c5a0 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -100,12 +100,14 @@ def derive_build __skip__ = build ||= RubyWasm::Build.new(name, **@packager.full_build_options) build.crossruby.user_exts = user_exts(build) - build.crossruby.debugflags = %w[-g] - build.crossruby.wasmoptflags = %w[-O3 -g] # Emscripten uses --global-base=1024 by default, but it conflicts with # --stack-first and -z stack-size since global-base 1024 is smaller than # the large stack size. + # Also -g produces some warnings on Emscripten and it confuses the configure + # script of Ruby. if @packager.full_build_options[:target] != "wasm32-unknown-emscripten" + build.crossruby.debugflags = %w[-g] + build.crossruby.wasmoptflags = %w[-O3 -g] build.crossruby.ldflags = %w[ -Xlinker --stack-first From 60391fad9de725223bbe4596fedf1226e429b15d Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 16:57:54 +0000 Subject: [PATCH 36/44] Add --reconfigure and --clean options to CLI --- lib/ruby_wasm/cli.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 324d9d9058..83fc6880d5 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -44,6 +44,8 @@ def build(args) save_temps: false, optimize: false, remake: false, + reconfigure: false, + clean: false, ruby_version: "3.3", target_triplet: "wasm32-unknown-wasi", profile: "full", @@ -83,6 +85,14 @@ def build(args) options[:remake] = true end + opts.on("--reconfigure", "Re-execute configure for Ruby") do + options[:reconfigure] = true + end + + opts.on("--clean", "Clean build artifacts") do + options[:clean] = true + end + opts.on("-o", "--output FILE", "Output file") do |file| options[:output] = file end From 21fd9af2869e4dffbd8b5c88839092a5b479c574 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 16:59:31 +0000 Subject: [PATCH 37/44] rake format --- lib/ruby_wasm/cli.rb | 4 +--- lib/ruby_wasm/packager/core.rb | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 83fc6880d5..7d6d48c37f 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -89,9 +89,7 @@ def build(args) options[:reconfigure] = true end - opts.on("--clean", "Clean build artifacts") do - options[:clean] = true - end + opts.on("--clean", "Clean build artifacts") { options[:clean] = true } opts.on("-o", "--output FILE", "Output file") do |file| options[:output] = file diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 2470a0c5a0..b1049f27f7 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -66,17 +66,21 @@ class DynamicLinking < BuildStrategy class StaticLinking < BuildStrategy def build(executor, options) build = derive_build - force_rebuild = options[:remake] || options[:clean] || options[:reconfigure] + force_rebuild = + options[:remake] || options[:clean] || options[:reconfigure] if File.exist?(build.crossruby.artifact) && !force_rebuild return build.crossruby.artifact end - if options[:clean] - build.crossruby.clean(executor) - end - - do_build = proc do - build.crossruby.build(executor, remake: options[:remake], reconfigure: options[:reconfigure]) - end + build.crossruby.clean(executor) if options[:clean] + + do_build = + proc do + build.crossruby.build( + executor, + remake: options[:remake], + reconfigure: options[:reconfigure] + ) + end __skip__ = if defined?(Bundler) From 9d8c66dd41fb44c02cdb9b836b6b5e3c207fa494 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 17:12:47 +0000 Subject: [PATCH 38/44] Remove stub wit-bindgen-ruby for now --- Cargo.lock | 73 +++------------------ Cargo.toml | 2 +- packages/crates/wit-bindgen-ruby/Cargo.toml | 10 --- packages/crates/wit-bindgen-ruby/src/lib.rs | 18 ----- 4 files changed, 11 insertions(+), 92 deletions(-) delete mode 100644 packages/crates/wit-bindgen-ruby/Cargo.toml delete mode 100644 packages/crates/wit-bindgen-ruby/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e70cbebd3c..d546e2afdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -562,12 +562,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "errno" version = "0.2.8" @@ -707,7 +701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" dependencies = [ "fallible-iterator", - "indexmap 1.9.3", + "indexmap", "stable_deref_trait", ] @@ -732,12 +726,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - [[package]] name = "heck" version = "0.3.3" @@ -801,17 +789,6 @@ dependencies = [ "serde", ] -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", - "serde", -] - [[package]] name = "io-extras" version = "0.13.2" @@ -1071,7 +1048,7 @@ checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "crc32fast", "hashbrown 0.13.2", - "indexmap 1.9.3", + "indexmap", "memchr", ] @@ -1395,12 +1372,6 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - [[package]] name = "seq-macro" version = "0.3.5" @@ -1825,7 +1796,7 @@ version = "0.103.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c437373cac5ea84f1113d648d51f71751ffbe3d90c00ae67618cf20d0b5ee7b" dependencies = [ - "indexmap 1.9.3", + "indexmap", "url", ] @@ -1835,7 +1806,7 @@ version = "0.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d014e33793cab91655fa6349b0bc974984de106b2e0f6b0dfe6f6594b260624d" dependencies = [ - "indexmap 1.9.3", + "indexmap", "url", ] @@ -1851,7 +1822,7 @@ dependencies = [ "bumpalo", "cfg-if", "fxprof-processed-profile", - "indexmap 1.9.3", + "indexmap", "libc", "log", "object", @@ -1915,7 +1886,7 @@ dependencies = [ "syn 1.0.109", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.7.1", + "wit-parser", ] [[package]] @@ -1972,7 +1943,7 @@ dependencies = [ "anyhow", "cranelift-entity", "gimli", - "indexmap 1.9.3", + "indexmap", "log", "object", "serde", @@ -2051,7 +2022,7 @@ dependencies = [ "anyhow", "cc", "cfg-if", - "indexmap 1.9.3", + "indexmap", "libc", "log", "mach", @@ -2101,7 +2072,7 @@ checksum = "421f0d16cc5c612b35ae53a0be3d3124c72296f18e5be3468263c745d56d37ab" dependencies = [ "anyhow", "heck 0.4.1", - "wit-parser 0.7.1", + "wit-parser", ] [[package]] @@ -2371,13 +2342,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "wit-bindgen-ruby" -version = "0.1.0" -dependencies = [ - "wit-parser 0.13.0", -] - [[package]] name = "wit-parser" version = "0.7.1" @@ -2386,30 +2350,13 @@ checksum = "5ca2581061573ef6d1754983d7a9b3ed5871ef859d52708ea9a0f5af32919172" dependencies = [ "anyhow", "id-arena", - "indexmap 1.9.3", + "indexmap", "log", "pulldown-cmark", "unicode-xid", "url", ] -[[package]] -name = "wit-parser" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.1.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", -] - [[package]] name = "witx" version = "0.9.1" diff --git a/Cargo.toml b/Cargo.toml index 4448fa6ca1..3719c31f51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,5 @@ # in the ext/ directory. [workspace] -members = ["./ext/ruby_wasm", "packages/crates/wit-bindgen-ruby"] +members = ["./ext/ruby_wasm"] resolver = "2" diff --git a/packages/crates/wit-bindgen-ruby/Cargo.toml b/packages/crates/wit-bindgen-ruby/Cargo.toml deleted file mode 100644 index eceb027645..0000000000 --- a/packages/crates/wit-bindgen-ruby/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "wit-bindgen-ruby" -version = "0.1.0" -edition = "2021" -authors = ["Yuta Saito "] -license = "MIT" -publish = false - -[dependencies] -wit-parser = "0.13.0" diff --git a/packages/crates/wit-bindgen-ruby/src/lib.rs b/packages/crates/wit-bindgen-ruby/src/lib.rs deleted file mode 100644 index c885e4a499..0000000000 --- a/packages/crates/wit-bindgen-ruby/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -pub fn bindgen() { - let mut resolve = wit_parser::Resolve::new(); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} From 2bdc25a6fb3e551724136cbcb185a391c676f939 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 17:32:59 +0000 Subject: [PATCH 39/44] Pack fs only when target is wasi --- lib/ruby_wasm/packager.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index 9d3bb21042..3a95446609 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -29,11 +29,14 @@ def package(executor, dest_dir, options) fs.remove_non_runtime_files(executor) fs.remove_stdlib(executor) unless options[:stdlib] - wasi_vfs = RubyWasmExt::WasiVfs.new - wasi_vfs.map_dir("/bundle", fs.bundle_dir) - wasi_vfs.map_dir("/usr", File.dirname(fs.ruby_root)) + if full_build_options[:target] == "wasm32-unknown-wasi" + # wasi-vfs supports only WASI target + wasi_vfs = RubyWasmExt::WasiVfs.new + wasi_vfs.map_dir("/bundle", fs.bundle_dir) + wasi_vfs.map_dir("/usr", File.dirname(fs.ruby_root)) - wasm_bytes = wasi_vfs.pack(wasm_bytes) + wasm_bytes = wasi_vfs.pack(wasm_bytes) + end wasm_bytes = RubyWasmExt.preinitialize(wasm_bytes) if options[:optimize] wasm_bytes From 09b408744d3a8d877be021329ae3378fce6dd223 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 18:34:39 +0000 Subject: [PATCH 40/44] Move `rbwasm build` invocation to rake task to control cache key in rake --- Rakefile | 12 +++-- .../ruby-3.2-wasm-wasi/package.json | 3 +- .../ruby-3.3-wasm-wasi/package.json | 3 +- .../ruby-head-wasm-emscripten/package.json | 4 +- .../ruby-head-wasm-wasi/package.json | 3 +- .../ruby-wasm-wasi/tools/pack-ruby-wasm.sh | 31 ------------ tasks/ci.rake | 2 +- tasks/packaging.rake | 50 ++++++++++++++++++- 8 files changed, 62 insertions(+), 46 deletions(-) delete mode 100755 packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh diff --git a/Rakefile b/Rakefile index 6eb819cf1a..268f3b7e2f 100644 --- a/Rakefile +++ b/Rakefile @@ -21,22 +21,26 @@ BUILDS = NPM_PACKAGES = [ { name: "ruby-head-wasm-emscripten", - build: "head-wasm32-unknown-emscripten-full", + ruby_version: "head", + gemfile: nil, target: "wasm32-unknown-emscripten" }, { name: "ruby-head-wasm-wasi", - build: "head-wasm32-unknown-wasi-full-js-debug", + ruby_version: "head", + gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile", target: "wasm32-unknown-wasi" }, { name: "ruby-3.3-wasm-wasi", - build: "3.3-wasm32-unknown-wasi-full-js-debug", + ruby_version: "3.3", + gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile", target: "wasm32-unknown-wasi" }, { name: "ruby-3.2-wasm-wasi", - build: "3.2-wasm32-unknown-wasi-full-js-debug", + ruby_version: "3.2", + gemfile: "packages/npm-packages/ruby-wasm-wasi/Gemfile", target: "wasm32-unknown-wasi" } ] diff --git a/packages/npm-packages/ruby-3.2-wasm-wasi/package.json b/packages/npm-packages/ruby-3.2-wasm-wasi/package.json index 0ba21305f2..e390b32ae7 100644 --- a/packages/npm-packages/ruby-3.2-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-3.2-wasm-wasi/package.json @@ -28,9 +28,8 @@ "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-3.2-wasm-wasi", "build:static": "npm run build:static:files && npm run build:static:compat", - "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh 3.2 ./dist", "build:rollup": "rollup -c rollup.config.mjs", - "build": "npm run build:deps && npm run build:static && npm run build:wasm && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" + "build": "npm run build:deps && npm run build:static && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" }, "repository": "https://github.com/ruby/ruby.wasm", "homepage": "https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-3.2-wasm-wasi", diff --git a/packages/npm-packages/ruby-3.3-wasm-wasi/package.json b/packages/npm-packages/ruby-3.3-wasm-wasi/package.json index cd7fd0756c..be1b7d4cf2 100644 --- a/packages/npm-packages/ruby-3.3-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-3.3-wasm-wasi/package.json @@ -28,9 +28,8 @@ "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-3.3-wasm-wasi", "build:static": "npm run build:static:files && npm run build:static:compat", - "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh 3.3 ./dist", "build:rollup": "rollup -c rollup.config.mjs", - "build": "npm run build:deps && npm run build:static && npm run build:wasm && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" + "build": "npm run build:deps && npm run build:static && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" }, "repository": "https://github.com/ruby/ruby.wasm", "homepage": "https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-3.3-wasm-wasi", diff --git a/packages/npm-packages/ruby-head-wasm-emscripten/package.json b/packages/npm-packages/ruby-head-wasm-emscripten/package.json index 00cb900129..a1960af56b 100644 --- a/packages/npm-packages/ruby-head-wasm-emscripten/package.json +++ b/packages/npm-packages/ruby-head-wasm-emscripten/package.json @@ -11,9 +11,7 @@ "README.md" ], "scripts": { - "build:ruby": "rake build:head-wasm32-unknown-emscripten-full", - "build:package": "./build-package.sh ../../../rubies/ruby-head-wasm32-unknown-emscripten-full", - "build": "npm run build:ruby && npm run build:package" + "build": "./build-package.sh ../../../rubies/ruby-head-wasm32-unknown-emscripten-full" }, "repository": "https://github.com/ruby/ruby.wasm", "homepage": "https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-head-wasm-emscripten", diff --git a/packages/npm-packages/ruby-head-wasm-wasi/package.json b/packages/npm-packages/ruby-head-wasm-wasi/package.json index b210043556..f8258e2174 100644 --- a/packages/npm-packages/ruby-head-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-head-wasm-wasi/package.json @@ -28,9 +28,8 @@ "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-head-wasm-wasi", "build:static": "npm run build:static:files && npm run build:static:compat", - "build:wasm": "../ruby-wasm-wasi/tools/pack-ruby-wasm.sh head ./dist", "build:rollup": "rollup -c rollup.config.mjs", - "build": "npm run build:deps && npm run build:static && npm run build:wasm && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" + "build": "npm run build:deps && npm run build:static && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist" }, "repository": "https://github.com/ruby/ruby.wasm", "homepage": "https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-head-wasm-wasi", diff --git a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh b/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh deleted file mode 100755 index 66f3d0ba29..0000000000 --- a/packages/npm-packages/ruby-wasm-wasi/tools/pack-ruby-wasm.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -eu - -usage() { - echo "Usage: $(basename $0) ruby_version dist_dir" - exit 1 -} - -if [ $# -lt 1 ]; then - usage -fi - -ruby_version="$1" -dist_dir="$PWD/$2" -package_dir="$(cd "$(dirname "$0")/.." && pwd)" - -mkdir -p "$dist_dir" - -# Cache rubies in the package dir -export RUBY_WASM_ROOT="$package_dir/../../.." -export BUNDLE_GEMFILE="$package_dir/Gemfile" -cd "$package_dir" - -echo "$0: Entering $package_dir" - -rbwasm_options="--ruby-version $ruby_version --target wasm32-unknown-wasi --build-profile full" -bundle exec rbwasm build ${rbwasm_options[@]} --no-stdlib -o "$dist_dir/ruby.wasm" -"$WASMOPT" --strip-debug "$dist_dir/ruby.wasm" -o "$dist_dir/ruby.wasm" -bundle exec rbwasm build ${rbwasm_options[@]} -o "$dist_dir/ruby.debug+stdlib.wasm" -"$WASMOPT" --strip-debug "$dist_dir/ruby.debug+stdlib.wasm" -o "$dist_dir/ruby+stdlib.wasm" diff --git a/tasks/ci.rake b/tasks/ci.rake index b02a885487..5057310c52 100644 --- a/tasks/ci.rake +++ b/tasks/ci.rake @@ -68,7 +68,7 @@ def rake_task_matrix artifact: "packages/npm-packages/#{pkg[:name]}/#{pkg[:name]}-*.tgz", artifact_name: "npm-#{pkg[:name]}", builder: pkg[:target], - rubies_cache_key: ruby_cache_keys[pkg[:build]] + rubies_cache_key: npm_pkg_rubies_cache_key(pkg) } # Run tests only if the package has 'test' script package_json = diff --git a/tasks/packaging.rake b/tasks/packaging.rake index 19f038ef19..16c3afec16 100644 --- a/tasks/packaging.rake +++ b/tasks/packaging.rake @@ -5,14 +5,62 @@ tools = { "WASMOPT" => wasi_sdk.wasm_opt } +def npm_pkg_build_command(pkg) + [ + "bundle", "exec", "rbwasm", + "build", + "--ruby-version", pkg[:ruby_version], + "--target", pkg[:target], + "--build-profile", "full", + ] +end + +def npm_pkg_rubies_cache_key(pkg) + build_command = npm_pkg_build_command(pkg) + require "open3" + cmd = build_command + ["--print-ruby-cache-key"] + stdout, status = Open3.capture2(*cmd) + unless status.success? + raise "Command failed with status (#{status.exitstatus}): #{cmd.join ""}" + end + require "json" + JSON.parse(stdout)["hexdigest"] +end + namespace :npm do NPM_PACKAGES.each do |pkg| base_dir = Dir.pwd pkg_dir = "#{Dir.pwd}/packages/npm-packages/#{pkg[:name]}" namespace pkg[:name] do + build_command = npm_pkg_build_command(pkg) + + desc "Build ruby for npm package #{pkg[:name]}" + task "ruby" do + env = { + # Share ./build and ./rubies in the same workspace + "RUBY_WASM_ROOT" => base_dir, + } + if gemfile_path = pkg[:gemfile] + env["BUNDLE_GEMFILE"] = File.join(base_dir, gemfile_path) + else + # Explicitly disable rubygems integration since Bundler finds + # Gemfile in the repo root directory. + build_command.push "--disable-gems" + end + dist_dir = File.join(pkg_dir, "dist") + if pkg[:target] == "wasm32-unknown-wasi" + sh env, *build_command, "--no-stdlib", "-o", File.join(dist_dir, "ruby.wasm") + sh env, *build_command, "-o", File.join(dist_dir, "ruby.debug+stdlib.wasm") + sh wasi_sdk.wasm_opt, "--strip-debug", File.join(dist_dir, "ruby.wasm"), "-o", File.join(dist_dir, "ruby.wasm") + sh wasi_sdk.wasm_opt, "--strip-debug", File.join(dist_dir, "ruby.debug+stdlib.wasm"), "-o", File.join(dist_dir, "ruby+stdlib.wasm") + elsif pkg[:target] == "wasm32-unknown-emscripten" + sh env, *build_command, "-o", "/dev/null" + end + end + desc "Build npm package #{pkg[:name]}" - task "build" do + task "build" => ["ruby"] do sh tools, "npm run build", chdir: pkg_dir end From cf92cd93596a830c89aedc617bc77e82fb751ceb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 18:44:35 +0000 Subject: [PATCH 41/44] Setup ruby to build ci matrix to use Bundler in rbwasm command --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8ef50cdb12..89218e0422 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,6 +75,10 @@ jobs: entries: ${{ steps.set-matrix.outputs.entries }} steps: - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.2" + bundler-cache: true - run: rake ci:pin_build_manifest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 53b586284ad94c3bceffff7caef6420ac0914320 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 18:59:39 +0000 Subject: [PATCH 42/44] rake format --- tasks/packaging.rake | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/tasks/packaging.rake b/tasks/packaging.rake index 16c3afec16..155dfbcfd9 100644 --- a/tasks/packaging.rake +++ b/tasks/packaging.rake @@ -7,11 +7,16 @@ tools = { def npm_pkg_build_command(pkg) [ - "bundle", "exec", "rbwasm", + "bundle", + "exec", + "rbwasm", "build", - "--ruby-version", pkg[:ruby_version], - "--target", pkg[:target], - "--build-profile", "full", + "--ruby-version", + pkg[:ruby_version], + "--target", + pkg[:target], + "--build-profile", + "full" ] end @@ -39,7 +44,7 @@ namespace :npm do task "ruby" do env = { # Share ./build and ./rubies in the same workspace - "RUBY_WASM_ROOT" => base_dir, + "RUBY_WASM_ROOT" => base_dir } if gemfile_path = pkg[:gemfile] env["BUNDLE_GEMFILE"] = File.join(base_dir, gemfile_path) @@ -50,10 +55,25 @@ namespace :npm do end dist_dir = File.join(pkg_dir, "dist") if pkg[:target] == "wasm32-unknown-wasi" - sh env, *build_command, "--no-stdlib", "-o", File.join(dist_dir, "ruby.wasm") - sh env, *build_command, "-o", File.join(dist_dir, "ruby.debug+stdlib.wasm") - sh wasi_sdk.wasm_opt, "--strip-debug", File.join(dist_dir, "ruby.wasm"), "-o", File.join(dist_dir, "ruby.wasm") - sh wasi_sdk.wasm_opt, "--strip-debug", File.join(dist_dir, "ruby.debug+stdlib.wasm"), "-o", File.join(dist_dir, "ruby+stdlib.wasm") + sh env, + *build_command, + "--no-stdlib", + "-o", + File.join(dist_dir, "ruby.wasm") + sh env, + *build_command, + "-o", + File.join(dist_dir, "ruby.debug+stdlib.wasm") + sh wasi_sdk.wasm_opt, + "--strip-debug", + File.join(dist_dir, "ruby.wasm"), + "-o", + File.join(dist_dir, "ruby.wasm") + sh wasi_sdk.wasm_opt, + "--strip-debug", + File.join(dist_dir, "ruby.debug+stdlib.wasm"), + "-o", + File.join(dist_dir, "ruby+stdlib.wasm") elsif pkg[:target] == "wasm32-unknown-emscripten" sh env, *build_command, "-o", "/dev/null" end From ad53e0c8ab6117dbc72f902b042d0821965168ec Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 19:01:34 +0000 Subject: [PATCH 43/44] Revert adding ruby tarball to gem package --- ruby_wasm.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby_wasm.gemspec b/ruby_wasm.gemspec index 74ee69f214..8315e8d9b6 100644 --- a/ruby_wasm.gemspec +++ b/ruby_wasm.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |spec| `git ls-files -z`.split("\x0").reject do |f| (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}) end - end + ["rubies/ruby-head-wasm32-unknown-wasi-full.tar.gz"] + end spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] From 7d551b0e5dc56780b45be6d1e86f06b239df3240 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 3 Jan 2024 19:08:36 +0000 Subject: [PATCH 44/44] Fix npm package build --- tasks/packaging.rake | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks/packaging.rake b/tasks/packaging.rake index 155dfbcfd9..606d8b8709 100644 --- a/tasks/packaging.rake +++ b/tasks/packaging.rake @@ -54,6 +54,7 @@ namespace :npm do build_command.push "--disable-gems" end dist_dir = File.join(pkg_dir, "dist") + mkdir_p dist_dir if pkg[:target] == "wasm32-unknown-wasi" sh env, *build_command,