ruby_wasm 2.5.0-x64-mingw-ucrt → 2.5.2-x64-mingw-ucrt

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +9 -7
  3. data/Gemfile +1 -1
  4. data/README.md +12 -11
  5. data/Rakefile +9 -7
  6. data/docs/cheat_sheet.md +8 -8
  7. data/lib/ruby_wasm/3.1/ruby_wasm.so +0 -0
  8. data/lib/ruby_wasm/3.2/ruby_wasm.so +0 -0
  9. data/lib/ruby_wasm/3.3/ruby_wasm.so +0 -0
  10. data/lib/ruby_wasm/build/executor.rb +4 -0
  11. data/lib/ruby_wasm/build/product/crossruby.rb +59 -25
  12. data/lib/ruby_wasm/build/product/libyaml.rb +5 -3
  13. data/lib/ruby_wasm/build/product/openssl.rb +7 -2
  14. data/lib/ruby_wasm/build/product/product.rb +3 -3
  15. data/lib/ruby_wasm/build/product/ruby_source.rb +3 -3
  16. data/lib/ruby_wasm/build/product/wasi_vfs.rb +1 -1
  17. data/lib/ruby_wasm/build/product/zlib.rb +3 -1
  18. data/lib/ruby_wasm/build/target.rb +24 -0
  19. data/lib/ruby_wasm/build/toolchain/wit_bindgen.rb +2 -2
  20. data/lib/ruby_wasm/build/toolchain.rb +1 -1
  21. data/lib/ruby_wasm/build.rb +7 -3
  22. data/lib/ruby_wasm/cli.rb +147 -11
  23. data/lib/ruby_wasm/feature_set.rb +30 -0
  24. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.command.wasm +0 -0
  25. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.reactor.wasm +0 -0
  26. data/lib/ruby_wasm/packager/component_adapter.rb +14 -0
  27. data/lib/ruby_wasm/packager/core.rb +199 -5
  28. data/lib/ruby_wasm/packager/file_system.rb +5 -3
  29. data/lib/ruby_wasm/packager.rb +22 -82
  30. data/lib/ruby_wasm/rake_task.rb +1 -0
  31. data/lib/ruby_wasm/version.rb +1 -1
  32. data/lib/ruby_wasm.rb +2 -0
  33. data/package-lock.json +5571 -7015
  34. data/package.json +3 -3
  35. data/rakelib/check.rake +23 -10
  36. data/rakelib/ci.rake +3 -3
  37. data/rakelib/packaging.rake +44 -15
  38. data/sig/ruby_wasm/build.rbs +38 -28
  39. data/sig/ruby_wasm/cli.rbs +27 -3
  40. data/sig/ruby_wasm/ext.rbs +25 -2
  41. data/sig/ruby_wasm/feature_set.rbs +12 -0
  42. data/sig/ruby_wasm/packager.rbs +44 -7
  43. metadata +10 -7
  44. data/builders/wasm32-unknown-emscripten/Dockerfile +0 -43
  45. data/builders/wasm32-unknown-emscripten/entrypoint.sh +0 -7
  46. data/builders/wasm32-unknown-wasi/Dockerfile +0 -47
  47. data/builders/wasm32-unknown-wasi/entrypoint.sh +0 -7
@@ -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
- digest << @target
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
- digest << @wasi_vfs.name
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: Hash[Symbol, untyped]
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-wasi",
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 componentize [options]"
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
- config = { target: options[:target_triplet], src: options[:ruby_version] }
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] = RubyWasm::Packager::ALL_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 derive_packager(options)
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
- if defined?(Bundler) && !options[:disable_gems]
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
- RubyWasm::Packager.new(build_config(options), definition)
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.pack("C*")
336
+ @stdout.write wasm_bytes
201
337
  else
202
- File.binwrite(options[:output], wasm_bytes.pack("C*"))
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
@@ -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, module_bytes)
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,168 @@ 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, module_bytes)
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
+ features: @packager.features,
166
+ ext_relative_path: ext_relative_path
167
+ )
168
+ end
169
+ end
170
+
171
+ exts.each do |prod|
172
+ executor.begin_section prod.class, prod.name, "Building"
173
+ prod.build(executor, build.crossruby)
174
+ executor.end_section prod.class, prod.name
175
+ end
176
+ end
177
+
178
+ def cache_key(digest)
179
+ derive_build.cache_key(digest)
180
+ end
181
+
182
+ def artifact
183
+ derive_build.crossruby.artifact
184
+ end
185
+
186
+ def target
187
+ RubyWasm::Target.new(@packager.full_build_options[:target], pic: true)
188
+ end
189
+
190
+ def derive_build
191
+ return @build if @build
192
+ __skip__ =
193
+ build ||= RubyWasm::Build.new(
194
+ name, **@packager.full_build_options,
195
+ target: target,
196
+ # NOTE: We don't need linking libwasi_vfs because we use wasi-virt instead.
197
+ wasi_vfs: nil
198
+ )
199
+ build.crossruby.cflags = %w[-fPIC -fvisibility=default]
200
+ if @packager.full_build_options[:target] != "wasm32-unknown-emscripten"
201
+ build.crossruby.debugflags = %w[-g]
202
+ build.crossruby.wasmoptflags = %w[-O3 -g --pass-arg=asyncify-relocatable]
203
+ build.crossruby.ldflags = %w[
204
+ -Xlinker
205
+ --stack-first
206
+ -Xlinker
207
+ -z
208
+ -Xlinker
209
+ stack-size=16777216
210
+ ]
211
+ build.crossruby.xldflags = %w[
212
+ -Xlinker -shared
213
+ -Xlinker --export-dynamic
214
+ -Xlinker --export-all
215
+ -Xlinker --experimental-pic
216
+ -Xlinker -export-if-defined=__main_argc_argv
217
+ ]
218
+ end
219
+ @build = build
220
+ build
221
+ end
222
+
223
+ def name
224
+ require "digest"
225
+ options = @packager.full_build_options
226
+ src_channel = options[:src][:name]
227
+ target_triplet = options[:target]
228
+ "ruby-#{src_channel}-#{target_triplet}-pic#{options[:suffix]}"
229
+ end
64
230
  end
65
231
 
66
232
  class StaticLinking < BuildStrategy
@@ -93,16 +259,24 @@ class RubyWasm::Packager::Core
93
259
 
94
260
  def cache_key(digest)
95
261
  derive_build.cache_key(digest)
262
+ if enabled = @packager.features.support_component_model?
263
+ digest << enabled.to_s
264
+ end
96
265
  end
97
266
 
98
267
  def artifact
99
268
  derive_build.crossruby.artifact
100
269
  end
101
270
 
271
+ def target
272
+ RubyWasm::Target.new(@packager.full_build_options[:target])
273
+ end
274
+
102
275
  def derive_build
103
276
  return @build if @build
104
- __skip__ =
105
- build ||= RubyWasm::Build.new(name, **@packager.full_build_options)
277
+ __skip__ = build ||= RubyWasm::Build.new(
278
+ name, **@packager.full_build_options, target: target,
279
+ )
106
280
  build.crossruby.user_exts = user_exts(build)
107
281
  # Emscripten uses --global-base=1024 by default, but it conflicts with
108
282
  # --stack-first and -z stack-size since global-base 1024 is smaller than
@@ -111,7 +285,9 @@ class RubyWasm::Packager::Core
111
285
  # script of Ruby.
112
286
  if @packager.full_build_options[:target] != "wasm32-unknown-emscripten"
113
287
  build.crossruby.debugflags = %w[-g]
114
- build.crossruby.wasmoptflags = %w[-O3 -g]
288
+ # We assume that imported functions provided through WASI will not change
289
+ # asyncify state, so we ignore them.
290
+ build.crossruby.wasmoptflags = %w[-O3 -g --pass-arg=asyncify-ignore-imports]
115
291
  build.crossruby.ldflags = %w[
116
292
  -Xlinker
117
293
  --stack-first
@@ -125,6 +301,20 @@ class RubyWasm::Packager::Core
125
301
  build
126
302
  end
127
303
 
304
+ def build_and_link_exts(executor, module_bytes)
305
+ return module_bytes unless @packager.features.support_component_model?
306
+
307
+ linker = RubyWasmExt::ComponentEncode.new
308
+ linker.validate(true)
309
+ linker.module(module_bytes)
310
+ linker.adapter(
311
+ "wasi_snapshot_preview1",
312
+ File.binread(RubyWasm::Packager::ComponentAdapter.wasi_snapshot_preview1("reactor"))
313
+ )
314
+
315
+ linker.encode()
316
+ end
317
+
128
318
  def user_exts(build)
129
319
  @user_exts ||=
130
320
  specs_with_extensions.flat_map do |spec, exts|
@@ -135,6 +325,7 @@ class RubyWasm::Packager::Core
135
325
  RubyWasm::CrossRubyExtProduct.new(
136
326
  ext_srcdir,
137
327
  build.toolchain,
328
+ features: @packager.features,
138
329
  ext_relative_path: ext_relative_path
139
330
  )
140
331
  end
@@ -150,6 +341,9 @@ class RubyWasm::Packager::Core
150
341
  exts = specs_with_extensions.sort
151
342
  hash = ::Digest::MD5.new
152
343
  specs_with_extensions.each { |spec, _| hash << spec.full_name }
344
+ if enabled = @packager.features.support_component_model?
345
+ hash << enabled.to_s
346
+ end
153
347
  exts.empty? ? base : "#{base}-#{hash.hexdigest}"
154
348
  end
155
349
  end
@@ -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
- ].each do |pattern|
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|