ruby_wasm 2.5.0 → 2.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +7 -7
- data/Cargo.lock +91 -6
- data/Gemfile +1 -1
- data/README.md +9 -9
- data/Rakefile +9 -7
- data/docs/cheat_sheet.md +8 -8
- data/ext/ruby_wasm/Cargo.toml +3 -1
- data/ext/ruby_wasm/src/lib.rs +198 -8
- data/lib/ruby_wasm/build/executor.rb +4 -0
- data/lib/ruby_wasm/build/product/crossruby.rb +59 -25
- data/lib/ruby_wasm/build/product/libyaml.rb +5 -3
- data/lib/ruby_wasm/build/product/openssl.rb +7 -2
- data/lib/ruby_wasm/build/product/product.rb +3 -3
- data/lib/ruby_wasm/build/product/ruby_source.rb +3 -3
- data/lib/ruby_wasm/build/product/wasi_vfs.rb +1 -1
- data/lib/ruby_wasm/build/product/zlib.rb +3 -1
- data/lib/ruby_wasm/build/target.rb +24 -0
- data/lib/ruby_wasm/build/toolchain/wit_bindgen.rb +2 -2
- data/lib/ruby_wasm/build/toolchain.rb +1 -1
- data/lib/ruby_wasm/build.rb +7 -3
- data/lib/ruby_wasm/cli.rb +147 -11
- data/lib/ruby_wasm/feature_set.rb +30 -0
- data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.command.wasm +0 -0
- data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.reactor.wasm +0 -0
- data/lib/ruby_wasm/packager/component_adapter.rb +14 -0
- data/lib/ruby_wasm/packager/core.rb +199 -5
- data/lib/ruby_wasm/packager/file_system.rb +5 -3
- data/lib/ruby_wasm/packager.rb +22 -82
- data/lib/ruby_wasm/rake_task.rb +1 -0
- data/lib/ruby_wasm/version.rb +1 -1
- data/lib/ruby_wasm.rb +2 -0
- data/package-lock.json +5571 -7015
- data/package.json +3 -3
- data/rakelib/check.rake +23 -10
- data/rakelib/ci.rake +3 -3
- data/rakelib/packaging.rake +44 -15
- data/sig/ruby_wasm/build.rbs +38 -28
- data/sig/ruby_wasm/cli.rbs +27 -3
- data/sig/ruby_wasm/ext.rbs +25 -2
- data/sig/ruby_wasm/feature_set.rbs +12 -0
- data/sig/ruby_wasm/packager.rbs +44 -7
- metadata +9 -10
- data/builders/wasm32-unknown-emscripten/Dockerfile +0 -43
- data/builders/wasm32-unknown-emscripten/entrypoint.sh +0 -7
- data/builders/wasm32-unknown-wasi/Dockerfile +0 -47
- data/builders/wasm32-unknown-wasi/entrypoint.sh +0 -7
@@ -5,11 +5,12 @@ module RubyWasm
|
|
5
5
|
class CrossRubyExtProduct < BuildProduct
|
6
6
|
attr_reader :name
|
7
7
|
|
8
|
-
def initialize(srcdir, toolchain, ext_relative_path: nil)
|
8
|
+
def initialize(srcdir, toolchain, features:, ext_relative_path: nil)
|
9
9
|
@srcdir, @toolchain = srcdir, toolchain
|
10
10
|
# ext_relative_path is relative path from build dir
|
11
11
|
# e.g. cgi-0.3.6/ext/cgi/escape
|
12
12
|
@ext_relative_path = ext_relative_path || File.basename(srcdir)
|
13
|
+
@features = features
|
13
14
|
@name = @ext_relative_path
|
14
15
|
end
|
15
16
|
|
@@ -37,7 +38,6 @@ module RubyWasm
|
|
37
38
|
make_args << "AR=#{@toolchain.ar}"
|
38
39
|
make_args << "RANLIB=#{@toolchain.ranlib}"
|
39
40
|
|
40
|
-
make_args << "DESTDIR=#{crossruby.dest_dir}"
|
41
41
|
make_args
|
42
42
|
end
|
43
43
|
|
@@ -45,12 +45,15 @@ module RubyWasm
|
|
45
45
|
objdir = product_build_dir crossruby
|
46
46
|
executor.mkdir_p objdir
|
47
47
|
do_extconf executor, crossruby
|
48
|
+
|
49
|
+
executor.system "make", "-C", objdir, *make_args(crossruby), "clean"
|
50
|
+
build_target = crossruby.target.pic? ? "install-so" : "static"
|
48
51
|
executor.system "make",
|
49
52
|
"-j#{executor.process_count}",
|
50
53
|
"-C",
|
51
54
|
"#{objdir}",
|
52
55
|
*make_args(crossruby),
|
53
|
-
|
56
|
+
build_target
|
54
57
|
# A ext can provide link args by link.filelist. It contains only built archive file by default.
|
55
58
|
unless File.exist?(linklist(crossruby))
|
56
59
|
executor.write(
|
@@ -61,9 +64,28 @@ module RubyWasm
|
|
61
64
|
end
|
62
65
|
|
63
66
|
def do_extconf(executor, crossruby)
|
67
|
+
unless crossruby.target.pic?
|
68
|
+
self.do_legacy_extconf(executor, crossruby)
|
69
|
+
return
|
70
|
+
end
|
71
|
+
objdir = product_build_dir crossruby
|
72
|
+
source = crossruby.source
|
73
|
+
rbconfig_rb = Dir.glob(File.join(crossruby.dest_dir, "usr/local/lib/ruby/*/wasm32-wasi/rbconfig.rb")).first
|
74
|
+
raise "rbconfig.rb not found" unless rbconfig_rb
|
75
|
+
extconf_args = [
|
76
|
+
"-C", objdir,
|
77
|
+
"#{@srcdir}/extconf.rb",
|
78
|
+
"--target-rbconfig=#{rbconfig_rb}",
|
79
|
+
]
|
80
|
+
extconf_args << "--enable-component-model" if @features.support_component_model?
|
81
|
+
executor.system Gem.ruby, *extconf_args
|
82
|
+
end
|
83
|
+
|
84
|
+
def do_legacy_extconf(executor, crossruby)
|
64
85
|
objdir = product_build_dir crossruby
|
65
86
|
source = crossruby.source
|
66
87
|
extconf_args = [
|
88
|
+
"-C", objdir,
|
67
89
|
"--disable=gems",
|
68
90
|
# HACK: top_srcdir is required to find ruby headers
|
69
91
|
"-e",
|
@@ -71,9 +93,6 @@ module RubyWasm
|
|
71
93
|
# HACK: extout is required to find config.h
|
72
94
|
"-e",
|
73
95
|
%Q($extout="#{crossruby.build_dir}/.ext"),
|
74
|
-
# HACK: skip have_devel check since ruby is not installed yet
|
75
|
-
"-e",
|
76
|
-
"$have_devel = true",
|
77
96
|
# HACK: force static ext build by imitating extmk
|
78
97
|
"-e",
|
79
98
|
"$static = true; trace_var(:$static) {|v| $static = true }",
|
@@ -88,12 +107,13 @@ module RubyWasm
|
|
88
107
|
# like "cgi/escape" instead of "escape"
|
89
108
|
"-e",
|
90
109
|
%Q(require "json"; File.write("#{metadata_json(crossruby)}", JSON.dump({target: $target}))),
|
91
|
-
"-I#{crossruby.build_dir}"
|
110
|
+
"-I#{crossruby.build_dir}",
|
111
|
+
"--",
|
92
112
|
]
|
113
|
+
extconf_args << "--enable-component-model" if @features.support_component_model?
|
93
114
|
# Clear RUBYOPT to avoid loading unrelated bundle setup
|
94
115
|
executor.system crossruby.baseruby_path,
|
95
116
|
*extconf_args,
|
96
|
-
chdir: objdir,
|
97
117
|
env: {
|
98
118
|
"RUBYOPT" => ""
|
99
119
|
}
|
@@ -118,7 +138,7 @@ module RubyWasm
|
|
118
138
|
end
|
119
139
|
|
120
140
|
class CrossRubyProduct < AutoconfProduct
|
121
|
-
attr_reader :source, :toolchain
|
141
|
+
attr_reader :target, :source, :toolchain
|
122
142
|
attr_accessor :user_exts,
|
123
143
|
:wasmoptflags,
|
124
144
|
:cppflags,
|
@@ -155,6 +175,14 @@ module RubyWasm
|
|
155
175
|
executor.system "make", "rbconfig.rb", chdir: build_dir
|
156
176
|
end
|
157
177
|
|
178
|
+
def need_exts_build?
|
179
|
+
@user_exts.any?
|
180
|
+
end
|
181
|
+
|
182
|
+
def need_extinit_obj?
|
183
|
+
need_exts_build? && !@target.pic?
|
184
|
+
end
|
185
|
+
|
158
186
|
def build_exts(executor)
|
159
187
|
@user_exts.each do |prod|
|
160
188
|
executor.begin_section prod.class, prod.name, "Building"
|
@@ -168,6 +196,7 @@ module RubyWasm
|
|
168
196
|
executor.mkdir_p build_dir
|
169
197
|
@toolchain.install
|
170
198
|
[@source, @baseruby, @libyaml, @zlib, @openssl, @wasi_vfs].each do |prod|
|
199
|
+
next unless prod
|
171
200
|
executor.begin_section prod.class, prod.name, "Building"
|
172
201
|
prod.build(executor)
|
173
202
|
executor.end_section prod.class, prod.name
|
@@ -176,17 +205,20 @@ module RubyWasm
|
|
176
205
|
configure(executor, reconfigure: reconfigure)
|
177
206
|
executor.end_section self.class, name
|
178
207
|
|
179
|
-
build_exts(executor)
|
208
|
+
build_exts(executor) if need_exts_build?
|
180
209
|
|
181
210
|
executor.begin_section self.class, name, "Building"
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
211
|
+
|
212
|
+
if need_extinit_obj?
|
213
|
+
executor.mkdir_p File.dirname(extinit_obj)
|
214
|
+
executor.system "ruby",
|
215
|
+
extinit_c_erb,
|
216
|
+
*@user_exts.map { |ext| ext.feature_name(self) },
|
217
|
+
"--cc",
|
218
|
+
toolchain.cc,
|
219
|
+
"--output",
|
220
|
+
extinit_obj
|
221
|
+
end
|
190
222
|
install_dir = File.join(build_dir, "install")
|
191
223
|
if !File.exist?(install_dir) || remake || reconfigure
|
192
224
|
executor.system "make",
|
@@ -216,7 +248,7 @@ module RubyWasm
|
|
216
248
|
end
|
217
249
|
|
218
250
|
def cache_key(digest)
|
219
|
-
|
251
|
+
@params.target.cache_key(digest)
|
220
252
|
digest << @params.default_exts
|
221
253
|
@wasmoptflags.each { |f| digest << f }
|
222
254
|
@cppflags.each { |f| digest << f }
|
@@ -229,11 +261,11 @@ module RubyWasm
|
|
229
261
|
end
|
230
262
|
|
231
263
|
def build_dir
|
232
|
-
File.join(@build_dir, @params.target, name)
|
264
|
+
File.join(@build_dir, @params.target.to_s, name)
|
233
265
|
end
|
234
266
|
|
235
267
|
def ext_build_dir
|
236
|
-
File.join(@build_dir, @params.target, name + "-ext")
|
268
|
+
File.join(@build_dir, @params.target.to_s, name + "-ext")
|
237
269
|
end
|
238
270
|
|
239
271
|
def with_libyaml(libyaml)
|
@@ -274,14 +306,14 @@ module RubyWasm
|
|
274
306
|
end
|
275
307
|
|
276
308
|
def configure_args(build_triple, toolchain)
|
277
|
-
target = @params.target
|
309
|
+
target = @params.target.triple
|
278
310
|
default_exts = @params.default_exts
|
279
311
|
|
280
312
|
ldflags = @ldflags.dup
|
281
313
|
xldflags = @xldflags.dup
|
282
314
|
|
283
315
|
args = self.system_triplet_args + ["--build", build_triple]
|
284
|
-
args << "--with-static-linked-ext"
|
316
|
+
args << "--with-static-linked-ext" unless @params.target.pic?
|
285
317
|
args << %Q(--with-ext=#{default_exts})
|
286
318
|
args << %Q(--with-libyaml-dir=#{@libyaml.install_root})
|
287
319
|
args << %Q(--with-zlib-dir=#{@zlib.install_root})
|
@@ -289,7 +321,7 @@ module RubyWasm
|
|
289
321
|
args << %Q(--with-baseruby=#{baseruby_path})
|
290
322
|
|
291
323
|
case target
|
292
|
-
when
|
324
|
+
when /^wasm32-unknown-wasi/
|
293
325
|
xldflags << @wasi_vfs.lib_wasi_vfs_a if @wasi_vfs
|
294
326
|
# TODO: Find a way to force cast or update API
|
295
327
|
# @type var wasi_sdk_path: untyped
|
@@ -308,8 +340,9 @@ module RubyWasm
|
|
308
340
|
|
309
341
|
args.concat(self.tools_args)
|
310
342
|
(@user_exts || []).each { |lib| xldflags << "@#{lib.linklist(self)}" }
|
311
|
-
xldflags << extinit_obj
|
343
|
+
xldflags << extinit_obj if need_extinit_obj?
|
312
344
|
|
345
|
+
cflags = @cflags.dup
|
313
346
|
xcflags = @xcflags.dup
|
314
347
|
xcflags << "-DWASM_SETJMP_STACK_BUFFER_SIZE=24576"
|
315
348
|
xcflags << "-DWASM_FIBER_STACK_BUFFER_SIZE=24576"
|
@@ -317,6 +350,7 @@ module RubyWasm
|
|
317
350
|
|
318
351
|
args << %Q(LDFLAGS=#{ldflags.join(" ")})
|
319
352
|
args << %Q(XLDFLAGS=#{xldflags.join(" ")})
|
353
|
+
args << %Q(CFLAGS=#{cflags.join(" ")})
|
320
354
|
args << %Q(XCFLAGS=#{xcflags.join(" ")})
|
321
355
|
args << %Q(debugflags=#{@debugflags.join(" ")})
|
322
356
|
args << %Q(cppflags=#{@cppflags.join(" ")})
|
@@ -13,7 +13,7 @@ module RubyWasm
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def product_build_dir
|
16
|
-
File.join(@build_dir, target, "yaml-#{LIBYAML_VERSION}")
|
16
|
+
File.join(@build_dir, target.to_s, "yaml-#{LIBYAML_VERSION}")
|
17
17
|
end
|
18
18
|
|
19
19
|
def destdir
|
@@ -52,12 +52,14 @@ module RubyWasm
|
|
52
52
|
executor.system "curl",
|
53
53
|
"-o",
|
54
54
|
"#{product_build_dir}/config/config.guess",
|
55
|
-
"https://
|
55
|
+
"https://cdn.jsdelivr.net/gh/gcc-mirror/gcc@master/config.guess"
|
56
56
|
executor.system "curl",
|
57
57
|
"-o",
|
58
58
|
"#{product_build_dir}/config/config.sub",
|
59
|
-
"https://
|
59
|
+
"https://cdn.jsdelivr.net/gh/gcc-mirror/gcc@master/config.sub"
|
60
60
|
|
61
|
+
configure_args = self.configure_args.dup
|
62
|
+
configure_args << "CFLAGS=-fPIC" if target.pic?
|
61
63
|
executor.system "./configure", *configure_args, chdir: product_build_dir
|
62
64
|
executor.system "make",
|
63
65
|
"install",
|
@@ -13,7 +13,7 @@ module RubyWasm
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def product_build_dir
|
16
|
-
File.join(@build_dir, target, "openssl-#{OPENSSL_VERSION}")
|
16
|
+
File.join(@build_dir, target.to_s, "openssl-#{OPENSSL_VERSION}")
|
17
17
|
end
|
18
18
|
|
19
19
|
def destdir
|
@@ -42,7 +42,7 @@ module RubyWasm
|
|
42
42
|
--libdir=lib
|
43
43
|
-Wl,--allow-undefined
|
44
44
|
]
|
45
|
-
if @target
|
45
|
+
if @target.triple.start_with?("wasm32-unknown-wasi")
|
46
46
|
args.concat %w[
|
47
47
|
-D_WASI_EMULATED_SIGNAL
|
48
48
|
-D_WASI_EMULATED_PROCESS_CLOCKS
|
@@ -52,6 +52,11 @@ module RubyWasm
|
|
52
52
|
-DHAVE_FORK=0
|
53
53
|
]
|
54
54
|
end
|
55
|
+
|
56
|
+
if @target.pic?
|
57
|
+
args << "-fPIC"
|
58
|
+
end
|
59
|
+
|
55
60
|
args + tools_args
|
56
61
|
end
|
57
62
|
|
@@ -12,13 +12,13 @@ module RubyWasm
|
|
12
12
|
end
|
13
13
|
def system_triplet_args
|
14
14
|
args = []
|
15
|
-
case @target
|
16
|
-
when
|
15
|
+
case @target.triple
|
16
|
+
when /^wasm32-unknown-wasi/
|
17
17
|
args.concat(%W[--host wasm32-wasi])
|
18
18
|
when "wasm32-unknown-emscripten"
|
19
19
|
args.concat(%W[--host wasm32-emscripten])
|
20
20
|
else
|
21
|
-
raise "unknown target: #{@target}"
|
21
|
+
raise "unknown target: #{@target.triple}"
|
22
22
|
end
|
23
23
|
args
|
24
24
|
end
|
@@ -19,7 +19,7 @@ module RubyWasm
|
|
19
19
|
when "tarball"
|
20
20
|
digest << @params[:url]
|
21
21
|
when "local"
|
22
|
-
digest << File.mtime(@params[:
|
22
|
+
digest << File.mtime(@params[:path]).to_i.to_s
|
23
23
|
else
|
24
24
|
raise "unknown source type: #{@params[:type]}"
|
25
25
|
end
|
@@ -75,12 +75,12 @@ module RubyWasm
|
|
75
75
|
)
|
76
76
|
when "local"
|
77
77
|
executor.mkdir_p File.dirname(src_dir)
|
78
|
-
executor.
|
78
|
+
executor.ln_s File.expand_path(@params[:path]), src_dir
|
79
79
|
else
|
80
80
|
raise "unknown source type: #{@params[:type]}"
|
81
81
|
end
|
82
82
|
(@params[:patches] || []).each do |patch_path|
|
83
|
-
executor.system "patch", "-p1", patch_path, chdir: src_dir
|
83
|
+
executor.system "patch", "-p1", "-i", patch_path, chdir: src_dir
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -13,7 +13,7 @@ module RubyWasm
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def product_build_dir
|
16
|
-
File.join(@build_dir, target, "zlib-#{ZLIB_VERSION}")
|
16
|
+
File.join(@build_dir, target.to_s, "zlib-#{ZLIB_VERSION}")
|
17
17
|
end
|
18
18
|
|
19
19
|
def destdir
|
@@ -54,6 +54,8 @@ module RubyWasm
|
|
54
54
|
product_build_dir,
|
55
55
|
"--strip-components=1"
|
56
56
|
|
57
|
+
configure_args = self.configure_args.dup
|
58
|
+
configure_args << "CFLAGS=-fPIC" if target.pic?
|
57
59
|
executor.system "env",
|
58
60
|
*configure_args,
|
59
61
|
"./configure",
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RubyWasm
|
2
|
+
# A build target representation
|
3
|
+
class Target
|
4
|
+
attr_reader :triple
|
5
|
+
|
6
|
+
def initialize(triple, pic: false)
|
7
|
+
@triple = triple
|
8
|
+
@pic = pic
|
9
|
+
end
|
10
|
+
|
11
|
+
def pic?
|
12
|
+
@pic
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{@triple}#{@pic ? "-pic" : ""}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def cache_key(digest)
|
20
|
+
digest << @triple
|
21
|
+
digest << "pic" if @pic
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -4,10 +4,10 @@ module RubyWasm
|
|
4
4
|
|
5
5
|
def initialize(
|
6
6
|
build_dir:,
|
7
|
-
revision: "
|
7
|
+
revision: "v0.24.0"
|
8
8
|
)
|
9
9
|
@build_dir = build_dir
|
10
|
-
@tool_dir = File.join(@build_dir, "toolchain", "wit-bindgen")
|
10
|
+
@tool_dir = File.join(@build_dir, "toolchain", "wit-bindgen-#{revision}")
|
11
11
|
@bin_path = File.join(@tool_dir, "bin", "wit-bindgen")
|
12
12
|
@revision = revision
|
13
13
|
end
|
@@ -18,7 +18,7 @@ module RubyWasm
|
|
18
18
|
|
19
19
|
def self.get(target, build_dir = nil)
|
20
20
|
case target
|
21
|
-
when
|
21
|
+
when /^wasm32-unknown-wasi/
|
22
22
|
return RubyWasm::WASISDK.new(build_dir: build_dir)
|
23
23
|
when "wasm32-unknown-emscripten"
|
24
24
|
return RubyWasm::Emscripten.new
|
data/lib/ruby_wasm/build.rb
CHANGED
@@ -2,6 +2,7 @@ require_relative "build/build_params"
|
|
2
2
|
require_relative "build/product"
|
3
3
|
require_relative "build/toolchain"
|
4
4
|
require_relative "build/executor"
|
5
|
+
require_relative "build/target"
|
5
6
|
|
6
7
|
class RubyWasm::Build
|
7
8
|
# Source to build from.
|
@@ -36,6 +37,7 @@ class RubyWasm::Build
|
|
36
37
|
toolchain:,
|
37
38
|
build_dir:,
|
38
39
|
rubies_dir:,
|
40
|
+
wasi_vfs: :default,
|
39
41
|
**options
|
40
42
|
)
|
41
43
|
@target = target
|
@@ -45,7 +47,7 @@ class RubyWasm::Build
|
|
45
47
|
|
46
48
|
@libyaml = RubyWasm::LibYAMLProduct.new(@build_dir, @target, @toolchain)
|
47
49
|
@zlib = RubyWasm::ZlibProduct.new(@build_dir, @target, @toolchain)
|
48
|
-
@wasi_vfs = RubyWasm::WasiVfsProduct.new(@build_dir)
|
50
|
+
@wasi_vfs = wasi_vfs == :default ? RubyWasm::WasiVfsProduct.new(@build_dir) : wasi_vfs
|
49
51
|
@source = RubyWasm::BuildSource.new(src, @build_dir)
|
50
52
|
@baseruby = RubyWasm::BaseRubyProduct.new(@build_dir, @source)
|
51
53
|
@openssl = RubyWasm::OpenSSLProduct.new(@build_dir, @target, @toolchain)
|
@@ -78,11 +80,13 @@ class RubyWasm::Build
|
|
78
80
|
@crossruby.cache_key(digest)
|
79
81
|
digest << @build_dir
|
80
82
|
digest << @rubies_dir
|
81
|
-
|
83
|
+
@target.cache_key(digest)
|
82
84
|
digest << @toolchain.name
|
83
85
|
digest << @libyaml.name
|
84
86
|
digest << @zlib.name
|
85
87
|
digest << @openssl.name
|
86
|
-
|
88
|
+
if wasi_vfs = @wasi_vfs
|
89
|
+
digest << wasi_vfs.name
|
90
|
+
end
|
87
91
|
end
|
88
92
|
end
|
data/lib/ruby_wasm/cli.rb
CHANGED
@@ -42,7 +42,7 @@ module RubyWasm
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def build(args)
|
45
|
-
# @type var options:
|
45
|
+
# @type var options: cli_options
|
46
46
|
options = {
|
47
47
|
save_temps: false,
|
48
48
|
optimize: false,
|
@@ -50,14 +50,16 @@ module RubyWasm
|
|
50
50
|
reconfigure: false,
|
51
51
|
clean: false,
|
52
52
|
ruby_version: "3.3",
|
53
|
-
target_triplet: "wasm32-unknown-
|
53
|
+
target_triplet: "wasm32-unknown-wasip1",
|
54
54
|
profile: "full",
|
55
55
|
stdlib: true,
|
56
|
-
disable_gems: false
|
56
|
+
disable_gems: false,
|
57
|
+
gemfile: nil,
|
58
|
+
patches: [],
|
57
59
|
}
|
58
60
|
OptionParser
|
59
61
|
.new do |opts|
|
60
|
-
opts.banner = "Usage: rbwasm
|
62
|
+
opts.banner = "Usage: rbwasm build [options]"
|
61
63
|
opts.on("-h", "--help", "Prints this help") do
|
62
64
|
@stdout.puts opts
|
63
65
|
exit
|
@@ -106,6 +108,10 @@ module RubyWasm
|
|
106
108
|
options[:disable_gems] = true
|
107
109
|
end
|
108
110
|
|
111
|
+
opts.on("-p", "--patch PATCH", "Apply a patch") do |patch|
|
112
|
+
options[:patches] << patch
|
113
|
+
end
|
114
|
+
|
109
115
|
opts.on("--format FORMAT", "Output format") do |format|
|
110
116
|
options[:format] = format
|
111
117
|
end
|
@@ -116,8 +122,19 @@ module RubyWasm
|
|
116
122
|
end
|
117
123
|
.parse!(args)
|
118
124
|
|
125
|
+
__skip__ = if defined?(Bundler)
|
126
|
+
Bundler.settings.temporary(force_ruby_platform: true) do
|
127
|
+
do_build_with_force_ruby_platform(options)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
do_build_with_force_ruby_platform(options)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def do_build_with_force_ruby_platform(options)
|
119
135
|
verbose = RubyWasm.logger.level == :debug
|
120
136
|
executor = RubyWasm::BuildExecutor.new(verbose: verbose)
|
137
|
+
|
121
138
|
packager = self.derive_packager(options)
|
122
139
|
|
123
140
|
if options[:print_ruby_cache_key]
|
@@ -152,10 +169,12 @@ module RubyWasm
|
|
152
169
|
private
|
153
170
|
|
154
171
|
def build_config(options)
|
155
|
-
|
172
|
+
build_source, all_default_exts = compute_build_source(options)
|
173
|
+
# @type var config: Packager::build_config
|
174
|
+
config = { target: options[:target_triplet], src: build_source }
|
156
175
|
case options[:profile]
|
157
176
|
when "full"
|
158
|
-
config[:default_exts] =
|
177
|
+
config[:default_exts] = all_default_exts || ""
|
159
178
|
env_additional_exts = ENV["RUBY_WASM_ADDITIONAL_EXTS"] || ""
|
160
179
|
unless env_additional_exts.empty?
|
161
180
|
config[:default_exts] += "," + env_additional_exts
|
@@ -170,12 +189,129 @@ module RubyWasm
|
|
170
189
|
config
|
171
190
|
end
|
172
191
|
|
173
|
-
def
|
192
|
+
def compute_build_source(options)
|
193
|
+
src_name = options[:ruby_version]
|
194
|
+
aliases = self.class.build_source_aliases(root)
|
195
|
+
source = aliases[src_name]
|
196
|
+
if source.nil?
|
197
|
+
if File.directory?(src_name)
|
198
|
+
# Treat as a local source if the given name is a source directory.
|
199
|
+
RubyWasm.logger.debug "Using local source: #{src_name}"
|
200
|
+
if options[:patches].any?
|
201
|
+
RubyWasm.logger.warn "Patches specified through --patch are ignored for local sources"
|
202
|
+
end
|
203
|
+
# @type var local_source: RubyWasm::Packager::build_source_local
|
204
|
+
local_source = { type: "local", path: src_name }
|
205
|
+
# @type var local_source: RubyWasm::Packager::build_source
|
206
|
+
local_source = local_source.merge(name: "local", patches: [])
|
207
|
+
return [local_source, nil]
|
208
|
+
end
|
209
|
+
# Otherwise, it's an unknown source.
|
210
|
+
raise(
|
211
|
+
"Unknown Ruby source: #{src_name} (available: #{aliases.keys.join(", ")} or a local directory)"
|
212
|
+
)
|
213
|
+
end
|
214
|
+
# Apply user-specified patches in addition to bundled patches.
|
215
|
+
source[:patches].concat(options[:patches])
|
216
|
+
# @type var all_default_exts: String
|
217
|
+
__skip__ = all_default_exts = source[:all_default_exts]
|
218
|
+
[source, all_default_exts]
|
219
|
+
end
|
220
|
+
|
221
|
+
# Retrieves the alias definitions for the Ruby sources.
|
222
|
+
def self.build_source_aliases(root)
|
223
|
+
# @type var sources: Hash[string, RubyWasm::Packager::build_source]
|
224
|
+
sources = {
|
225
|
+
"head" => {
|
226
|
+
type: "github",
|
227
|
+
repo: "ruby/ruby",
|
228
|
+
rev: "master",
|
229
|
+
all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS,
|
230
|
+
},
|
231
|
+
"3.3" => {
|
232
|
+
type: "tarball",
|
233
|
+
url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.1.tar.gz",
|
234
|
+
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",
|
235
|
+
},
|
236
|
+
"3.2" => {
|
237
|
+
type: "tarball",
|
238
|
+
url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.4.tar.gz",
|
239
|
+
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",
|
240
|
+
}
|
241
|
+
}
|
242
|
+
|
243
|
+
# Apply bundled and user-specified `<root>/patches` directories.
|
244
|
+
sources.each do |name, source|
|
245
|
+
source[:name] = name
|
246
|
+
patches_dirs = [bundled_patches_path, File.join(root, "patches")]
|
247
|
+
source[:patches] = patches_dirs.flat_map do |patches_dir|
|
248
|
+
Dir[File.join(patches_dir, name, "*.patch")]
|
249
|
+
.map { |p| File.expand_path(p) }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
build_manifest = File.join(root, "build_manifest.json")
|
254
|
+
if File.exist?(build_manifest)
|
255
|
+
begin
|
256
|
+
manifest = JSON.parse(File.read(build_manifest))
|
257
|
+
manifest["ruby_revisions"].each do |name, rev|
|
258
|
+
source = sources[name]
|
259
|
+
next unless source[:type] == "github"
|
260
|
+
# @type var source: RubyWasm::Packager::build_source_github
|
261
|
+
source[:rev] = rev
|
262
|
+
end
|
263
|
+
rescue StandardError => e
|
264
|
+
RubyWasm.logger.warn "Failed to load build_manifest.json: #{e}"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
sources
|
268
|
+
end
|
269
|
+
|
270
|
+
# Retrieves the root directory of the Ruby project.
|
271
|
+
def root
|
174
272
|
__skip__ =
|
175
|
-
|
273
|
+
@root ||=
|
274
|
+
begin
|
275
|
+
if explicit = ENV["RUBY_WASM_ROOT"]
|
276
|
+
File.expand_path(explicit)
|
277
|
+
elsif defined?(Bundler)
|
278
|
+
Bundler.root
|
279
|
+
else
|
280
|
+
Dir.pwd
|
281
|
+
end
|
282
|
+
rescue Bundler::GemfileNotFound
|
283
|
+
Dir.pwd
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Path to the directory containing the bundled patches, which is shipped
|
288
|
+
# as part of ruby_wasm gem to backport fixes or try experimental features
|
289
|
+
# before landing them to the ruby/ruby repository.
|
290
|
+
def self.bundled_patches_path
|
291
|
+
dir = __dir__
|
292
|
+
raise "Unexpected directory structure, no __dir__!??" unless dir
|
293
|
+
lib_source_root = File.join(dir, "..", "..")
|
294
|
+
File.join(lib_source_root, "patches")
|
295
|
+
end
|
296
|
+
|
297
|
+
def derive_packager(options)
|
298
|
+
__skip__ = definition = nil
|
299
|
+
__skip__ = if defined?(Bundler) && !options[:disable_gems]
|
300
|
+
begin
|
301
|
+
# Silence Bundler UI if --print-ruby-cache-key is specified not to bother the JSON output.
|
302
|
+
level = options[:print_ruby_cache_key] ? :silent : Bundler.ui.level
|
303
|
+
old_level = Bundler.ui.level
|
304
|
+
Bundler.ui.level = level
|
176
305
|
definition = Bundler.definition
|
306
|
+
ensure
|
307
|
+
Bundler.ui.level = old_level
|
177
308
|
end
|
178
|
-
|
309
|
+
end
|
310
|
+
RubyWasm.logger.info "Using Gemfile: #{definition.gemfiles}" if definition
|
311
|
+
RubyWasm::Packager.new(
|
312
|
+
root, build_config(options), definition,
|
313
|
+
features: RubyWasm::FeatureSet.derive_from_env
|
314
|
+
)
|
179
315
|
end
|
180
316
|
|
181
317
|
def do_print_ruby_cache_key(packager)
|
@@ -197,9 +333,9 @@ module RubyWasm
|
|
197
333
|
RubyWasm.logger.info "Size: #{SizeFormatter.format(wasm_bytes.size)}"
|
198
334
|
case options[:output]
|
199
335
|
when "-"
|
200
|
-
@stdout.write wasm_bytes
|
336
|
+
@stdout.write wasm_bytes
|
201
337
|
else
|
202
|
-
File.binwrite(options[:output], wasm_bytes
|
338
|
+
File.binwrite(options[:output], wasm_bytes)
|
203
339
|
RubyWasm.logger.debug "Wrote #{options[:output]}"
|
204
340
|
end
|
205
341
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
##
|
2
|
+
# A set of feature flags that can be used to enable or disable experimental features.
|
3
|
+
class RubyWasm::FeatureSet
|
4
|
+
def initialize(features)
|
5
|
+
@features = features
|
6
|
+
end
|
7
|
+
|
8
|
+
# Maps the feature to the environment variable.
|
9
|
+
FEATURES = {
|
10
|
+
dynamic_linking: "RUBY_WASM_EXPERIMENTAL_DYNAMIC_LINKING",
|
11
|
+
component_model: "RUBY_WASM_EXPERIMENTAL_COMPONENT_MODEL",
|
12
|
+
}.freeze
|
13
|
+
private_constant :FEATURES
|
14
|
+
|
15
|
+
# Derives the feature set from the environment variables. A feature
|
16
|
+
# is enabled if the corresponding environment variable is set to "1",
|
17
|
+
# otherwise it is disabled.
|
18
|
+
def self.derive_from_env
|
19
|
+
values = FEATURES.transform_values { |key| ENV[key] == "1" }
|
20
|
+
new(values)
|
21
|
+
end
|
22
|
+
|
23
|
+
def support_dynamic_linking?
|
24
|
+
@features[:dynamic_linking]
|
25
|
+
end
|
26
|
+
|
27
|
+
def support_component_model?
|
28
|
+
@features[:component_model] || @features[:dynamic_linking]
|
29
|
+
end
|
30
|
+
end
|