ruby_wasm 2.6.1 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +5 -2
- data/Cargo.lock +778 -314
- data/README.md +1 -1
- data/Rakefile +3 -2
- data/docs/cheat_sheet.md +8 -8
- data/ext/ruby_wasm/Cargo.toml +3 -1
- data/ext/ruby_wasm/src/lib.rs +102 -1
- data/lib/ruby_wasm/build/product/crossruby.rb +5 -2
- data/lib/ruby_wasm/build/toolchain.rb +1 -1
- data/lib/ruby_wasm/cli.rb +4 -2
- data/lib/ruby_wasm/packager/core.rb +31 -29
- data/lib/ruby_wasm/packager.rb +17 -2
- data/lib/ruby_wasm/version.rb +1 -1
- data/package-lock.json +840 -386
- data/package.json +2 -2
- data/rakelib/packaging.rake +4 -3
- data/sig/ruby_wasm/build.rbs +1 -0
- data/sig/ruby_wasm/ext.rbs +8 -0
- data/sig/ruby_wasm/packager.rbs +2 -1
- metadata +6 -6
data/README.md
CHANGED
@@ -23,7 +23,7 @@ Create and save `index.html` page with the following contents:
|
|
23
23
|
|
24
24
|
```html
|
25
25
|
<html>
|
26
|
-
<script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.
|
26
|
+
<script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.2/dist/browser.script.iife.js"></script>
|
27
27
|
<script type="text/ruby">
|
28
28
|
require "js"
|
29
29
|
|
data/Rakefile
CHANGED
@@ -68,10 +68,11 @@ class BuildTask < Struct.new(:name, :target, :build_command)
|
|
68
68
|
def ruby_cache_key
|
69
69
|
return @key if @key
|
70
70
|
require "open3"
|
71
|
+
env = { "RUBY_WASM_ROOT" => LIB_ROOT }
|
71
72
|
cmd = build_command + ["--print-ruby-cache-key"]
|
72
|
-
stdout, status = Open3.capture2(*cmd)
|
73
|
+
stdout, status = Open3.capture2(env, *cmd)
|
73
74
|
unless status.success?
|
74
|
-
raise "Command failed with status (#{status.exitstatus}): #{cmd.join ""}"
|
75
|
+
raise "Command failed with status (#{status.exitstatus}): #{cmd.join " "}"
|
75
76
|
end
|
76
77
|
require "json"
|
77
78
|
@key = JSON.parse(stdout)
|
data/docs/cheat_sheet.md
CHANGED
@@ -38,7 +38,7 @@ The easiest way to run Ruby on browser is to use `browser.script.iife.js` script
|
|
38
38
|
|
39
39
|
```html
|
40
40
|
<html>
|
41
|
-
<script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.
|
41
|
+
<script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.2/dist/browser.script.iife.js"></script>
|
42
42
|
<script type="text/ruby">
|
43
43
|
require "js"
|
44
44
|
JS.global[:document].write "Hello, world!"
|
@@ -51,8 +51,8 @@ If you want to control Ruby VM from JavaScript, you can use `@ruby/wasm-wasi` pa
|
|
51
51
|
```html
|
52
52
|
<html>
|
53
53
|
<script type="module">
|
54
|
-
import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.6.
|
55
|
-
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.
|
54
|
+
import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.6.2/dist/browser/+esm";
|
55
|
+
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.2/dist/ruby+stdlib.wasm");
|
56
56
|
const module = await WebAssembly.compileStreaming(response);
|
57
57
|
const { vm } = await DefaultRubyVM(module);
|
58
58
|
|
@@ -69,11 +69,11 @@ If you want to control Ruby VM from JavaScript, you can use `@ruby/wasm-wasi` pa
|
|
69
69
|
|
70
70
|
```html
|
71
71
|
<html>
|
72
|
-
<script src="https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.6.
|
72
|
+
<script src="https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.6.2/dist/browser.umd.js"></script>
|
73
73
|
<script>
|
74
74
|
const main = async () => {
|
75
75
|
const { DefaultRubyVM } = window["ruby-wasm-wasi"];
|
76
|
-
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.
|
76
|
+
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.2/dist/ruby+stdlib.wasm");
|
77
77
|
const module = await WebAssembly.compileStreaming(response);
|
78
78
|
const { vm } = await DefaultRubyVM(module);
|
79
79
|
|
@@ -128,7 +128,7 @@ end
|
|
128
128
|
|
129
129
|
```html
|
130
130
|
<html>
|
131
|
-
<script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.
|
131
|
+
<script src="https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.2/dist/browser.script.iife.js"></script>
|
132
132
|
<script type="text/ruby" data-eval="async">
|
133
133
|
require "js"
|
134
134
|
|
@@ -143,8 +143,8 @@ Or using `@ruby/wasm-wasi` package API `RubyVM#evalAsync`:
|
|
143
143
|
```html
|
144
144
|
<html>
|
145
145
|
<script type="module">
|
146
|
-
import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.6.
|
147
|
-
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.
|
146
|
+
import { DefaultRubyVM } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.6.2/dist/browser/+esm";
|
147
|
+
const response = await fetch("https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi@2.6.2/dist/ruby+stdlib.wasm");
|
148
148
|
const module = await WebAssembly.compileStreaming(response);
|
149
149
|
const { vm } = await DefaultRubyVM(module);
|
150
150
|
|
data/ext/ruby_wasm/Cargo.toml
CHANGED
@@ -15,4 +15,6 @@ bytes = "1"
|
|
15
15
|
wizer = "4.0.0"
|
16
16
|
wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", tag = "0.5.2" }
|
17
17
|
structopt = "0.3.26"
|
18
|
-
wit-component = "0.
|
18
|
+
wit-component = "0.212.0"
|
19
|
+
wasm-compose = "0.212.0"
|
20
|
+
wasi-virt = { git = "https://github.com/bytecodealliance/wasi-virt", rev = "02de7b495eba3f1bf786bf17cf2236b7277be7b0", default-features = false }
|
data/ext/ruby_wasm/src/lib.rs
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
use std::{collections::HashMap, path::PathBuf};
|
1
|
+
use std::{collections::HashMap, env, path::PathBuf, time::SystemTime};
|
2
2
|
|
3
3
|
use magnus::{
|
4
4
|
eval, exception, function, method,
|
@@ -8,6 +8,7 @@ use magnus::{
|
|
8
8
|
};
|
9
9
|
use structopt::StructOpt;
|
10
10
|
use wizer::Wizer;
|
11
|
+
use wasi_virt;
|
11
12
|
|
12
13
|
static RUBY_WASM: value::Lazy<RModule> =
|
13
14
|
value::Lazy::new(|ruby| ruby.define_module("RubyWasmExt").unwrap());
|
@@ -222,6 +223,99 @@ impl ComponentEncode {
|
|
222
223
|
}
|
223
224
|
}
|
224
225
|
|
226
|
+
#[wrap(class = "RubyWasmExt::WasiVirt")]
|
227
|
+
struct WasiVirt(std::cell::RefCell<Option<wasi_virt::WasiVirt>>);
|
228
|
+
|
229
|
+
impl WasiVirt {
|
230
|
+
fn new() -> Self {
|
231
|
+
Self(std::cell::RefCell::new(Some(wasi_virt::WasiVirt::new())))
|
232
|
+
}
|
233
|
+
|
234
|
+
fn virt<R>(
|
235
|
+
&self,
|
236
|
+
body: impl FnOnce(&mut wasi_virt::WasiVirt) -> Result<R, Error>,
|
237
|
+
) -> Result<R, Error> {
|
238
|
+
let mut virt = self.0.take().ok_or_else(|| {
|
239
|
+
Error::new(
|
240
|
+
exception::standard_error(),
|
241
|
+
"wasi virt is already consumed".to_string(),
|
242
|
+
)
|
243
|
+
})?;
|
244
|
+
let result = body(&mut virt)?;
|
245
|
+
self.0.replace(Some(virt));
|
246
|
+
Ok(result)
|
247
|
+
}
|
248
|
+
|
249
|
+
fn allow_all(&self) -> Result<(), Error> {
|
250
|
+
self.virt(|virt| {
|
251
|
+
virt.allow_all();
|
252
|
+
// Disable sockets for now since `sockets/ip-name-lookup` is not
|
253
|
+
// supported by @bytecodealliance/preview2-shim yet
|
254
|
+
virt.sockets(false);
|
255
|
+
Ok(())
|
256
|
+
})
|
257
|
+
}
|
258
|
+
|
259
|
+
fn map_dir(&self, guest_dir: String, host_dir: String) -> Result<(), Error> {
|
260
|
+
self.virt(|virt| {
|
261
|
+
virt.fs().virtual_preopen(guest_dir, host_dir);
|
262
|
+
Ok(())
|
263
|
+
})
|
264
|
+
}
|
265
|
+
|
266
|
+
fn finish(&self) -> Result<bytes::Bytes, Error> {
|
267
|
+
self.virt(|virt| {
|
268
|
+
let result = virt.finish().map_err(|e| {
|
269
|
+
Error::new(
|
270
|
+
exception::standard_error(),
|
271
|
+
format!("failed to generate virtualization adapter: {}", e),
|
272
|
+
)
|
273
|
+
})?;
|
274
|
+
Ok(result.adapter.into())
|
275
|
+
})
|
276
|
+
}
|
277
|
+
|
278
|
+
fn compose(&self, component_bytes: bytes::Bytes) -> Result<bytes::Bytes, Error> {
|
279
|
+
let virt_adapter = self.finish()?;
|
280
|
+
let tmpdir = env::temp_dir();
|
281
|
+
let tmp_virt = tmpdir.join(format!("virt{}.wasm", timestamp()));
|
282
|
+
std::fs::write(&tmp_virt, &virt_adapter).map_err(|e| {
|
283
|
+
Error::new(
|
284
|
+
exception::standard_error(),
|
285
|
+
format!("failed to write virt adapter: {}", e),
|
286
|
+
)
|
287
|
+
})?;
|
288
|
+
let tmp_component = tmpdir.join(format!("component{}.wasm", timestamp()));
|
289
|
+
std::fs::write(&tmp_component, &component_bytes).map_err(|e| {
|
290
|
+
Error::new(
|
291
|
+
exception::standard_error(),
|
292
|
+
format!("failed to write component: {}", e),
|
293
|
+
)
|
294
|
+
})?;
|
295
|
+
|
296
|
+
use wasm_compose::{composer, config};
|
297
|
+
let config = config::Config {
|
298
|
+
definitions: vec![tmp_virt],
|
299
|
+
..Default::default()
|
300
|
+
};
|
301
|
+
let composer = composer::ComponentComposer::new(&tmp_component, &config);
|
302
|
+
let composed = composer.compose().map_err(|e| {
|
303
|
+
Error::new(
|
304
|
+
exception::standard_error(),
|
305
|
+
format!("failed to compose component: {}", e),
|
306
|
+
)
|
307
|
+
})?;
|
308
|
+
return Ok(composed.into());
|
309
|
+
|
310
|
+
fn timestamp() -> u64 {
|
311
|
+
match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
312
|
+
Ok(n) => n.as_secs(),
|
313
|
+
Err(_) => panic!(),
|
314
|
+
}
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
225
319
|
#[magnus::init]
|
226
320
|
fn init(ruby: &Ruby) -> Result<(), Error> {
|
227
321
|
let module = RUBY_WASM.get_inner_with(ruby);
|
@@ -266,5 +360,12 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
266
360
|
)?;
|
267
361
|
component_encode.define_method("encode", method!(ComponentEncode::encode, 0))?;
|
268
362
|
|
363
|
+
let wasi_virt = module.define_class("WasiVirt", ruby.class_object())?;
|
364
|
+
wasi_virt.define_singleton_method("new", function!(WasiVirt::new, 0))?;
|
365
|
+
wasi_virt.define_method("allow_all", method!(WasiVirt::allow_all, 0))?;
|
366
|
+
wasi_virt.define_method("map_dir", method!(WasiVirt::map_dir, 2))?;
|
367
|
+
wasi_virt.define_method("finish", method!(WasiVirt::finish, 0))?;
|
368
|
+
wasi_virt.define_method("compose", method!(WasiVirt::compose, 1))?;
|
369
|
+
|
269
370
|
Ok(())
|
270
371
|
}
|
@@ -71,7 +71,6 @@ module RubyWasm
|
|
71
71
|
return
|
72
72
|
end
|
73
73
|
objdir = product_build_dir crossruby
|
74
|
-
source = crossruby.source
|
75
74
|
rbconfig_rb = Dir.glob(File.join(crossruby.dest_dir, "usr/local/lib/ruby/*/wasm32-wasi/rbconfig.rb")).first
|
76
75
|
raise "rbconfig.rb not found" unless rbconfig_rb
|
77
76
|
extconf_args = [
|
@@ -80,7 +79,7 @@ module RubyWasm
|
|
80
79
|
"--target-rbconfig=#{rbconfig_rb}",
|
81
80
|
]
|
82
81
|
extconf_args << "--enable-component-model" if @features.support_component_model?
|
83
|
-
executor.system
|
82
|
+
executor.system crossruby.baseruby_path, *extconf_args
|
84
83
|
end
|
85
84
|
|
86
85
|
def do_legacy_extconf(executor, crossruby)
|
@@ -359,6 +358,10 @@ module RubyWasm
|
|
359
358
|
args << %Q(wasmoptflags=#{@wasmoptflags.join(" ")})
|
360
359
|
end
|
361
360
|
args << "--disable-install-doc"
|
361
|
+
unless @params.target.pic?
|
362
|
+
# TODO: Remove this hack after dropping Ruby 3.2 support
|
363
|
+
args << "ac_cv_func_dlopen=no"
|
364
|
+
end
|
362
365
|
args
|
363
366
|
end
|
364
367
|
end
|
data/lib/ruby_wasm/cli.rb
CHANGED
@@ -216,7 +216,9 @@ module RubyWasm
|
|
216
216
|
local_source = { type: "local", path: src_name }
|
217
217
|
# @type var local_source: RubyWasm::Packager::build_source
|
218
218
|
local_source = local_source.merge(name: "local", patches: [])
|
219
|
-
|
219
|
+
# FIXME: We should have a way to specify extensions to be included by users.
|
220
|
+
# For now, assume all default extensions available in the head revision are available.
|
221
|
+
return [local_source, RubyWasm::Packager::ALL_DEFAULT_EXTS]
|
220
222
|
end
|
221
223
|
# Otherwise, it's an unknown source.
|
222
224
|
raise(
|
@@ -242,7 +244,7 @@ module RubyWasm
|
|
242
244
|
},
|
243
245
|
"3.3" => {
|
244
246
|
type: "tarball",
|
245
|
-
url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.
|
247
|
+
url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz",
|
246
248
|
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",
|
247
249
|
},
|
248
250
|
"3.2" => {
|
@@ -67,6 +67,14 @@ class RubyWasm::Packager::Core
|
|
67
67
|
use_js_gem ? "reactor" : "command"
|
68
68
|
end
|
69
69
|
|
70
|
+
def with_unbundled_env(&block)
|
71
|
+
__skip__ = if defined?(Bundler)
|
72
|
+
Bundler.with_unbundled_env(&block)
|
73
|
+
else
|
74
|
+
block.call
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
70
78
|
def cache_key(digest)
|
71
79
|
raise NotImplementedError
|
72
80
|
end
|
@@ -87,27 +95,22 @@ class RubyWasm::Packager::Core
|
|
87
95
|
end
|
88
96
|
build.crossruby.clean(executor) if options[:clean]
|
89
97
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
+
self.with_unbundled_env do
|
99
|
+
build.crossruby.build(
|
100
|
+
executor,
|
101
|
+
remake: options[:remake],
|
102
|
+
reconfigure: options[:reconfigure]
|
103
|
+
)
|
104
|
+
end
|
98
105
|
|
99
|
-
__skip__ =
|
100
|
-
if defined?(Bundler)
|
101
|
-
Bundler.with_unbundled_env(&do_build)
|
102
|
-
else
|
103
|
-
do_build.call
|
104
|
-
end
|
105
106
|
build.crossruby.artifact
|
106
107
|
end
|
107
108
|
|
108
109
|
def build_gem_exts(executor, gem_home)
|
109
110
|
build = derive_build
|
110
|
-
self.
|
111
|
+
self.with_unbundled_env do
|
112
|
+
self._build_gem_exts(executor, build, gem_home)
|
113
|
+
end
|
111
114
|
end
|
112
115
|
|
113
116
|
def link_gem_exts(executor, ruby_root, gem_home, module_bytes)
|
@@ -178,6 +181,12 @@ class RubyWasm::Packager::Core
|
|
178
181
|
end
|
179
182
|
|
180
183
|
def _build_gem_exts(executor, build, gem_home)
|
184
|
+
build.toolchain.install
|
185
|
+
baseruby = build.baseruby
|
186
|
+
unless Dir.exist?(baseruby.install_dir)
|
187
|
+
baseruby.build(executor)
|
188
|
+
end
|
189
|
+
|
181
190
|
exts = specs_with_extensions.flat_map do |spec, exts|
|
182
191
|
exts.map do |ext|
|
183
192
|
ext_feature = File.dirname(ext) # e.g. "ext/cgi/escape"
|
@@ -269,21 +278,14 @@ class RubyWasm::Packager::Core
|
|
269
278
|
end
|
270
279
|
build.crossruby.clean(executor) if options[:clean]
|
271
280
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
end
|
281
|
+
self.with_unbundled_env do
|
282
|
+
build.crossruby.build(
|
283
|
+
executor,
|
284
|
+
remake: options[:remake],
|
285
|
+
reconfigure: options[:reconfigure]
|
286
|
+
)
|
287
|
+
end
|
280
288
|
|
281
|
-
__skip__ =
|
282
|
-
if defined?(Bundler)
|
283
|
-
Bundler.with_unbundled_env(&do_build)
|
284
|
-
else
|
285
|
-
do_build.call
|
286
|
-
end
|
287
289
|
build.crossruby.artifact
|
288
290
|
end
|
289
291
|
|
data/lib/ruby_wasm/packager.rb
CHANGED
@@ -44,7 +44,7 @@ class RubyWasm::Packager
|
|
44
44
|
fs.remove_stdlib(executor)
|
45
45
|
end
|
46
46
|
|
47
|
-
if full_build_options[:target] == "wasm32-unknown-wasip1" && !features.
|
47
|
+
if full_build_options[:target] == "wasm32-unknown-wasip1" && !features.support_component_model?
|
48
48
|
# wasi-vfs supports only WASI target
|
49
49
|
wasi_vfs = RubyWasmExt::WasiVfs.new
|
50
50
|
wasi_vfs.map_dir("/bundle", fs.bundle_dir)
|
@@ -54,6 +54,20 @@ class RubyWasm::Packager
|
|
54
54
|
end
|
55
55
|
wasm_bytes = ruby_core.link_gem_exts(executor, fs.ruby_root, fs.bundle_dir, wasm_bytes)
|
56
56
|
|
57
|
+
if features.support_component_model?
|
58
|
+
wasi_virt = RubyWasmExt::WasiVirt.new
|
59
|
+
wasi_virt.allow_all
|
60
|
+
[
|
61
|
+
{ guest: "/bundle", host: fs.bundle_dir },
|
62
|
+
{ guest: "/usr", host: File.dirname(fs.ruby_root) }
|
63
|
+
].each do |map|
|
64
|
+
map => { guest:, host: }
|
65
|
+
RubyWasm.logger.debug "Adding files into VFS: #{host} => #{guest}"
|
66
|
+
wasi_virt.map_dir(guest, host)
|
67
|
+
end
|
68
|
+
wasm_bytes = wasi_virt.compose(wasm_bytes)
|
69
|
+
end
|
70
|
+
|
57
71
|
wasm_bytes = RubyWasmExt.preinitialize(wasm_bytes) if options[:optimize]
|
58
72
|
wasm_bytes
|
59
73
|
end
|
@@ -68,8 +82,9 @@ class RubyWasm::Packager
|
|
68
82
|
# Retrieves the specs from the Bundler definition, excluding the excluded gems.
|
69
83
|
def specs
|
70
84
|
return [] unless @definition
|
71
|
-
@specs ||= @definition.resolve.materialize(@definition.requested_dependencies)
|
85
|
+
__skip__ = @specs ||= @definition.resolve.materialize(@definition.requested_dependencies)
|
72
86
|
.reject { |spec| EXCLUDED_GEMS.include?(spec.name) }
|
87
|
+
.reject { |spec| spec.is_a?(Bundler::LazySpecification) }
|
73
88
|
@specs
|
74
89
|
end
|
75
90
|
|
data/lib/ruby_wasm/version.rb
CHANGED