ruby_wasm 2.5.0.pre.1 → 2.5.1
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 +13 -9
- data/Cargo.lock +705 -451
- data/Gemfile +1 -1
- data/README.md +12 -19
- data/Rakefile +9 -9
- data/benchmarks/vm_deep_call.rb +2 -2
- data/docs/cheat_sheet.md +8 -8
- data/ext/ruby_wasm/Cargo.toml +6 -5
- data/ext/ruby_wasm/src/lib.rs +208 -7
- data/lib/ruby_wasm/build/executor.rb +4 -0
- data/lib/ruby_wasm/build/product/crossruby.rb +57 -23
- 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 -39
- data/lib/ruby_wasm/build/product/zlib.rb +5 -3
- data/lib/ruby_wasm/build/target.rb +24 -0
- data/lib/ruby_wasm/build/toolchain.rb +1 -1
- data/lib/ruby_wasm/build.rb +7 -3
- data/lib/ruby_wasm/cli.rb +171 -13
- 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 +192 -4
- data/lib/ruby_wasm/packager/file_system.rb +20 -17
- data/lib/ruby_wasm/packager.rb +21 -83
- data/lib/ruby_wasm/rake_task.rb +2 -0
- data/lib/ruby_wasm/version.rb +1 -1
- data/lib/ruby_wasm.rb +2 -0
- data/package-lock.json +410 -133
- data/package.json +3 -3
- data/{tasks → rakelib}/ci.rake +3 -3
- data/{tasks → rakelib}/doc.rake +6 -1
- data/{tasks → rakelib}/format.rake +3 -2
- data/{tasks → rakelib}/gem.rake +4 -1
- data/{tasks → rakelib}/packaging.rake +34 -17
- data/{tasks → rakelib}/version.rake +2 -0
- data/sig/ruby_wasm/build.rbs +36 -31
- data/sig/ruby_wasm/cli.rbs +30 -3
- data/sig/ruby_wasm/ext.rbs +28 -3
- data/sig/ruby_wasm/feature_set.rbs +12 -0
- data/sig/ruby_wasm/packager.rbs +44 -7
- metadata +16 -15
- 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
- data/ruby_wasm.gemspec +0 -32
- /data/{tasks → rakelib}/check.rake +0 -0
data/lib/ruby_wasm/cli.rb
CHANGED
@@ -9,7 +9,7 @@ module RubyWasm
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def run(args)
|
12
|
-
available_commands = %w[build]
|
12
|
+
available_commands = %w[build pack]
|
13
13
|
parser =
|
14
14
|
OptionParser.new do |opts|
|
15
15
|
opts.banner = <<~USAGE
|
@@ -32,14 +32,17 @@ module RubyWasm
|
|
32
32
|
case command
|
33
33
|
when "build"
|
34
34
|
build(args)
|
35
|
+
when "pack"
|
36
|
+
pack(args)
|
35
37
|
else
|
38
|
+
@stderr.puts "Unknown command: #{command}"
|
36
39
|
@stderr.puts parser
|
37
40
|
exit
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
41
44
|
def build(args)
|
42
|
-
# @type var options:
|
45
|
+
# @type var options: cli_options
|
43
46
|
options = {
|
44
47
|
save_temps: false,
|
45
48
|
optimize: false,
|
@@ -47,14 +50,16 @@ module RubyWasm
|
|
47
50
|
reconfigure: false,
|
48
51
|
clean: false,
|
49
52
|
ruby_version: "3.3",
|
50
|
-
target_triplet: "wasm32-unknown-
|
53
|
+
target_triplet: "wasm32-unknown-wasip1",
|
51
54
|
profile: "full",
|
52
55
|
stdlib: true,
|
53
|
-
disable_gems: false
|
56
|
+
disable_gems: false,
|
57
|
+
gemfile: nil,
|
58
|
+
patches: [],
|
54
59
|
}
|
55
60
|
OptionParser
|
56
61
|
.new do |opts|
|
57
|
-
opts.banner = "Usage: rbwasm
|
62
|
+
opts.banner = "Usage: rbwasm build [options]"
|
58
63
|
opts.on("-h", "--help", "Prints this help") do
|
59
64
|
@stdout.puts opts
|
60
65
|
exit
|
@@ -103,6 +108,10 @@ module RubyWasm
|
|
103
108
|
options[:disable_gems] = true
|
104
109
|
end
|
105
110
|
|
111
|
+
opts.on("-p", "--patch PATCH", "Apply a patch") do |patch|
|
112
|
+
options[:patches] << patch
|
113
|
+
end
|
114
|
+
|
106
115
|
opts.on("--format FORMAT", "Output format") do |format|
|
107
116
|
options[:format] = format
|
108
117
|
end
|
@@ -113,8 +122,19 @@ module RubyWasm
|
|
113
122
|
end
|
114
123
|
.parse!(args)
|
115
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)
|
116
135
|
verbose = RubyWasm.logger.level == :debug
|
117
136
|
executor = RubyWasm::BuildExecutor.new(verbose: verbose)
|
137
|
+
|
118
138
|
packager = self.derive_packager(options)
|
119
139
|
|
120
140
|
if options[:print_ruby_cache_key]
|
@@ -141,13 +161,24 @@ module RubyWasm
|
|
141
161
|
end
|
142
162
|
end
|
143
163
|
|
164
|
+
def pack(args)
|
165
|
+
self.require_extension
|
166
|
+
RubyWasmExt::WasiVfs.run_cli([$0, "pack", *args])
|
167
|
+
end
|
168
|
+
|
144
169
|
private
|
145
170
|
|
146
171
|
def build_config(options)
|
147
|
-
|
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 }
|
148
175
|
case options[:profile]
|
149
176
|
when "full"
|
150
|
-
config[:default_exts] =
|
177
|
+
config[:default_exts] = all_default_exts || ""
|
178
|
+
env_additional_exts = ENV["RUBY_WASM_ADDITIONAL_EXTS"] || ""
|
179
|
+
unless env_additional_exts.empty?
|
180
|
+
config[:default_exts] += "," + env_additional_exts
|
181
|
+
end
|
151
182
|
when "minimal"
|
152
183
|
config[:default_exts] = ""
|
153
184
|
else
|
@@ -158,12 +189,129 @@ module RubyWasm
|
|
158
189
|
config
|
159
190
|
end
|
160
191
|
|
161
|
-
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.0.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.3.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
|
162
272
|
__skip__ =
|
163
|
-
|
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
|
164
305
|
definition = Bundler.definition
|
306
|
+
ensure
|
307
|
+
Bundler.ui.level = old_level
|
165
308
|
end
|
166
|
-
|
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
|
+
)
|
167
315
|
end
|
168
316
|
|
169
317
|
def do_print_ruby_cache_key(packager)
|
@@ -180,16 +328,26 @@ module RubyWasm
|
|
180
328
|
end
|
181
329
|
|
182
330
|
def do_build(executor, tmpdir, packager, options)
|
183
|
-
|
331
|
+
self.require_extension
|
184
332
|
wasm_bytes = packager.package(executor, tmpdir, options)
|
185
333
|
RubyWasm.logger.info "Size: #{SizeFormatter.format(wasm_bytes.size)}"
|
186
334
|
case options[:output]
|
187
335
|
when "-"
|
188
|
-
@stdout.write wasm_bytes
|
336
|
+
@stdout.write wasm_bytes
|
189
337
|
else
|
190
|
-
File.binwrite(options[:output], wasm_bytes
|
338
|
+
File.binwrite(options[:output], wasm_bytes)
|
191
339
|
RubyWasm.logger.debug "Wrote #{options[:output]}"
|
192
340
|
end
|
193
341
|
end
|
342
|
+
|
343
|
+
def require_extension
|
344
|
+
# Tries to require the extension for the given Ruby version first
|
345
|
+
begin
|
346
|
+
RUBY_VERSION =~ /(\d+\.\d+)/
|
347
|
+
require_relative "#{Regexp.last_match(1)}/ruby_wasm.so"
|
348
|
+
rescue LoadError
|
349
|
+
require_relative "ruby_wasm.so"
|
350
|
+
end
|
351
|
+
end
|
194
352
|
end
|
195
353
|
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
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RubyWasm::Packager::ComponentAdapter
|
2
|
+
module_function
|
3
|
+
|
4
|
+
# The path to the component adapter for the given WASI execution model.
|
5
|
+
#
|
6
|
+
# @param exec_model [String] "command" or "reactor"
|
7
|
+
def wasi_snapshot_preview1(exec_model)
|
8
|
+
File.join(
|
9
|
+
File.dirname(__FILE__),
|
10
|
+
"component_adapter",
|
11
|
+
"wasi_snapshot_preview1.#{exec_model}.wasm"
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
@@ -12,7 +12,7 @@ class RubyWasm::Packager::Core
|
|
12
12
|
|
13
13
|
extend Forwardable
|
14
14
|
|
15
|
-
def_delegators :build_strategy, :cache_key, :artifact
|
15
|
+
def_delegators :build_strategy, :cache_key, :artifact, :build_and_link_exts
|
16
16
|
|
17
17
|
private
|
18
18
|
|
@@ -20,7 +20,7 @@ class RubyWasm::Packager::Core
|
|
20
20
|
@build_strategy ||=
|
21
21
|
begin
|
22
22
|
has_exts = @packager.specs.any? { |spec| spec.extensions.any? }
|
23
|
-
if @packager.support_dynamic_linking?
|
23
|
+
if @packager.features.support_dynamic_linking?
|
24
24
|
DynamicLinking.new(@packager)
|
25
25
|
else
|
26
26
|
StaticLinking.new(@packager)
|
@@ -37,6 +37,10 @@ class RubyWasm::Packager::Core
|
|
37
37
|
raise NotImplementedError
|
38
38
|
end
|
39
39
|
|
40
|
+
def build_and_link_exts(executor)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
40
44
|
# Array of paths to extconf.rb files.
|
41
45
|
def specs_with_extensions
|
42
46
|
@packager.specs.filter_map do |spec|
|
@@ -61,6 +65,167 @@ class RubyWasm::Packager::Core
|
|
61
65
|
end
|
62
66
|
|
63
67
|
class DynamicLinking < BuildStrategy
|
68
|
+
def build(executor, options)
|
69
|
+
build = derive_build
|
70
|
+
force_rebuild =
|
71
|
+
options[:remake] || options[:clean] || options[:reconfigure]
|
72
|
+
if File.exist?(build.crossruby.artifact) && !force_rebuild
|
73
|
+
# Always build extensions because they are usually not expensive to build
|
74
|
+
return build.crossruby.artifact
|
75
|
+
end
|
76
|
+
build.crossruby.clean(executor) if options[:clean]
|
77
|
+
|
78
|
+
do_build =
|
79
|
+
proc do
|
80
|
+
build.crossruby.build(
|
81
|
+
executor,
|
82
|
+
remake: options[:remake],
|
83
|
+
reconfigure: options[:reconfigure]
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
__skip__ =
|
88
|
+
if defined?(Bundler)
|
89
|
+
Bundler.with_unbundled_env(&do_build)
|
90
|
+
else
|
91
|
+
do_build.call
|
92
|
+
end
|
93
|
+
build.crossruby.artifact
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_and_link_exts(executor)
|
97
|
+
build = derive_build
|
98
|
+
self.build_exts(executor, build)
|
99
|
+
self.link_exts(executor, build)
|
100
|
+
end
|
101
|
+
|
102
|
+
def link_exts(executor, build)
|
103
|
+
ruby_root = build.crossruby.dest_dir
|
104
|
+
|
105
|
+
libraries = [File.join(ruby_root, "usr", "local", "bin", "ruby")]
|
106
|
+
|
107
|
+
# TODO: Should be computed from dyinfo of ruby binary
|
108
|
+
wasi_libc_shared_libs = [
|
109
|
+
"libc.so",
|
110
|
+
"libwasi-emulated-getpid.so",
|
111
|
+
"libwasi-emulated-mman.so",
|
112
|
+
"libwasi-emulated-process-clocks.so",
|
113
|
+
"libwasi-emulated-signal.so",
|
114
|
+
]
|
115
|
+
|
116
|
+
wasi_libc_shared_libs.each do |lib|
|
117
|
+
# @type var toolchain: RubyWasm::WASISDK
|
118
|
+
toolchain = build.toolchain
|
119
|
+
wasi_sdk_path = toolchain.wasi_sdk_path
|
120
|
+
libraries << File.join(wasi_sdk_path, "share/wasi-sysroot/lib/wasm32-wasi", lib)
|
121
|
+
end
|
122
|
+
wasi_adapter = RubyWasm::Packager::ComponentAdapter.wasi_snapshot_preview1("command")
|
123
|
+
adapters = [wasi_adapter]
|
124
|
+
dl_openable_libs = Dir.glob(File.join(ruby_root, "usr", "local", "lib", "ruby", "**", "*.so"))
|
125
|
+
linker = RubyWasmExt::ComponentLink.new
|
126
|
+
linker.use_built_in_libdl(true)
|
127
|
+
linker.stub_missing_functions(false)
|
128
|
+
linker.validate(true)
|
129
|
+
|
130
|
+
libraries.each do |lib|
|
131
|
+
# Non-DL openable libraries should be referenced as base name
|
132
|
+
lib_name = File.basename(lib)
|
133
|
+
module_bytes = File.binread(lib)
|
134
|
+
RubyWasm.logger.info "Linking #{lib_name} (#{module_bytes.size} bytes)"
|
135
|
+
linker.library(lib_name, module_bytes, false)
|
136
|
+
end
|
137
|
+
|
138
|
+
dl_openable_libs.each do |lib|
|
139
|
+
# DL openable lib_name should be a relative path from ruby_root
|
140
|
+
lib_name = "/" + Pathname.new(lib).relative_path_from(Pathname.new(ruby_root)).to_s
|
141
|
+
module_bytes = File.binread(lib)
|
142
|
+
RubyWasm.logger.info "Linking #{lib_name} (#{module_bytes.size} bytes)"
|
143
|
+
linker.library(lib_name, module_bytes, true)
|
144
|
+
end
|
145
|
+
|
146
|
+
adapters.each do |adapter|
|
147
|
+
adapter_name = File.basename(adapter)
|
148
|
+
# e.g. wasi_snapshot_preview1.command.wasm -> wasi_snapshot_preview1
|
149
|
+
adapter_name = adapter_name.split(".")[0]
|
150
|
+
module_bytes = File.binread(adapter)
|
151
|
+
linker.adapter(adapter_name, module_bytes)
|
152
|
+
end
|
153
|
+
return linker.encode()
|
154
|
+
end
|
155
|
+
|
156
|
+
def build_exts(executor, build)
|
157
|
+
exts = specs_with_extensions.flat_map do |spec, exts|
|
158
|
+
exts.map do |ext|
|
159
|
+
ext_feature = File.dirname(ext) # e.g. "ext/cgi/escape"
|
160
|
+
ext_srcdir = File.join(spec.full_gem_path, ext_feature)
|
161
|
+
ext_relative_path = File.join(spec.full_name, ext_feature)
|
162
|
+
RubyWasm::CrossRubyExtProduct.new(
|
163
|
+
ext_srcdir,
|
164
|
+
build.toolchain,
|
165
|
+
ext_relative_path: ext_relative_path
|
166
|
+
)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
exts.each do |prod|
|
171
|
+
executor.begin_section prod.class, prod.name, "Building"
|
172
|
+
prod.build(executor, build.crossruby)
|
173
|
+
executor.end_section prod.class, prod.name
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def cache_key(digest)
|
178
|
+
derive_build.cache_key(digest)
|
179
|
+
end
|
180
|
+
|
181
|
+
def artifact
|
182
|
+
derive_build.crossruby.artifact
|
183
|
+
end
|
184
|
+
|
185
|
+
def target
|
186
|
+
RubyWasm::Target.new(@packager.full_build_options[:target], pic: true)
|
187
|
+
end
|
188
|
+
|
189
|
+
def derive_build
|
190
|
+
return @build if @build
|
191
|
+
__skip__ =
|
192
|
+
build ||= RubyWasm::Build.new(
|
193
|
+
name, **@packager.full_build_options,
|
194
|
+
target: target,
|
195
|
+
# NOTE: We don't need linking libwasi_vfs because we use wasi-virt instead.
|
196
|
+
wasi_vfs: nil
|
197
|
+
)
|
198
|
+
build.crossruby.cflags = %w[-fPIC -fvisibility=default]
|
199
|
+
if @packager.full_build_options[:target] != "wasm32-unknown-emscripten"
|
200
|
+
build.crossruby.debugflags = %w[-g]
|
201
|
+
build.crossruby.wasmoptflags = %w[-O3 -g --pass-arg=asyncify-relocatable]
|
202
|
+
build.crossruby.ldflags = %w[
|
203
|
+
-Xlinker
|
204
|
+
--stack-first
|
205
|
+
-Xlinker
|
206
|
+
-z
|
207
|
+
-Xlinker
|
208
|
+
stack-size=16777216
|
209
|
+
]
|
210
|
+
build.crossruby.xldflags = %w[
|
211
|
+
-Xlinker -shared
|
212
|
+
-Xlinker --export-dynamic
|
213
|
+
-Xlinker --export-all
|
214
|
+
-Xlinker --experimental-pic
|
215
|
+
-Xlinker -export-if-defined=__main_argc_argv
|
216
|
+
]
|
217
|
+
end
|
218
|
+
@build = build
|
219
|
+
build
|
220
|
+
end
|
221
|
+
|
222
|
+
def name
|
223
|
+
require "digest"
|
224
|
+
options = @packager.full_build_options
|
225
|
+
src_channel = options[:src][:name]
|
226
|
+
target_triplet = options[:target]
|
227
|
+
"ruby-#{src_channel}-#{target_triplet}-pic#{options[:suffix]}"
|
228
|
+
end
|
64
229
|
end
|
65
230
|
|
66
231
|
class StaticLinking < BuildStrategy
|
@@ -99,10 +264,14 @@ class RubyWasm::Packager::Core
|
|
99
264
|
derive_build.crossruby.artifact
|
100
265
|
end
|
101
266
|
|
267
|
+
def target
|
268
|
+
RubyWasm::Target.new(@packager.full_build_options[:target])
|
269
|
+
end
|
270
|
+
|
102
271
|
def derive_build
|
103
272
|
return @build if @build
|
104
273
|
__skip__ =
|
105
|
-
build ||= RubyWasm::Build.new(name, **@packager.full_build_options)
|
274
|
+
build ||= RubyWasm::Build.new(name, **@packager.full_build_options, target: target)
|
106
275
|
build.crossruby.user_exts = user_exts(build)
|
107
276
|
# Emscripten uses --global-base=1024 by default, but it conflicts with
|
108
277
|
# --stack-first and -z stack-size since global-base 1024 is smaller than
|
@@ -111,7 +280,9 @@ class RubyWasm::Packager::Core
|
|
111
280
|
# script of Ruby.
|
112
281
|
if @packager.full_build_options[:target] != "wasm32-unknown-emscripten"
|
113
282
|
build.crossruby.debugflags = %w[-g]
|
114
|
-
|
283
|
+
# We assume that imported functions provided through WASI will not change
|
284
|
+
# asyncify state, so we ignore them.
|
285
|
+
build.crossruby.wasmoptflags = %w[-O3 -g --pass-arg=asyncify-ignore-imports]
|
115
286
|
build.crossruby.ldflags = %w[
|
116
287
|
-Xlinker
|
117
288
|
--stack-first
|
@@ -125,6 +296,23 @@ class RubyWasm::Packager::Core
|
|
125
296
|
build
|
126
297
|
end
|
127
298
|
|
299
|
+
def build_and_link_exts(executor)
|
300
|
+
build = derive_build
|
301
|
+
ruby_root = build.crossruby.dest_dir
|
302
|
+
module_bytes = File.binread(File.join(ruby_root, "usr", "local", "bin", "ruby"))
|
303
|
+
return module_bytes unless @packager.features.support_component_model?
|
304
|
+
|
305
|
+
linker = RubyWasmExt::ComponentEncode.new
|
306
|
+
linker.validate(true)
|
307
|
+
linker.module(module_bytes)
|
308
|
+
linker.adapter(
|
309
|
+
"wasi_snapshot_preview1",
|
310
|
+
File.binread(RubyWasm::Packager::ComponentAdapter.wasi_snapshot_preview1("reactor"))
|
311
|
+
)
|
312
|
+
|
313
|
+
linker.encode()
|
314
|
+
end
|
315
|
+
|
128
316
|
def user_exts(build)
|
129
317
|
@user_exts ||=
|
130
318
|
specs_with_extensions.flat_map do |spec, exts|
|
@@ -65,12 +65,14 @@ class RubyWasm::Packager::FileSystem
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def remove_non_runtime_files(executor)
|
68
|
-
%w[
|
69
|
-
**/*.so
|
68
|
+
patterns = %w[
|
70
69
|
usr/local/lib/libruby-static.a
|
71
70
|
usr/local/bin/ruby
|
72
71
|
usr/local/include
|
73
|
-
]
|
72
|
+
]
|
73
|
+
|
74
|
+
patterns << "**/*.so" unless @packager.features.support_dynamic_linking?
|
75
|
+
patterns.each do |pattern|
|
74
76
|
Dir
|
75
77
|
.glob(File.join(@dest_dir, pattern))
|
76
78
|
.each do |entry|
|
@@ -98,6 +100,7 @@ class RubyWasm::Packager::FileSystem
|
|
98
100
|
def each_gem_require_path(&block)
|
99
101
|
each_gem_extension_path(&block)
|
100
102
|
@packager.specs.each do |spec|
|
103
|
+
# Use raw_require_paths to exclude extensions
|
101
104
|
spec.raw_require_paths.each do |require_path|
|
102
105
|
source = File.expand_path(require_path, spec.full_gem_path)
|
103
106
|
next unless File.exist?(source)
|
@@ -114,20 +117,20 @@ class RubyWasm::Packager::FileSystem
|
|
114
117
|
@packager.specs.each do |spec|
|
115
118
|
next unless File.exist?(spec.full_gem_path)
|
116
119
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
120
|
+
# spec.files is only valid before the gem is packaged.
|
121
|
+
if spec.source.path?
|
122
|
+
relative_paths = spec.files
|
123
|
+
else
|
124
|
+
# All files in .gem are required.
|
125
|
+
relative_paths = Dir.children(spec.full_gem_path)
|
126
|
+
end
|
127
|
+
relative_paths.each do |require_path|
|
128
|
+
source = File.expand_path(require_path, spec.full_gem_path)
|
129
|
+
next unless File.exist?(source)
|
130
|
+
relative =
|
131
|
+
File.join(bundle_relative_path, "gems", spec.full_name, require_path)
|
132
|
+
yield relative, source
|
133
|
+
end
|
131
134
|
end
|
132
135
|
end
|
133
136
|
|