Skip to content

Commit 25c1d3b

Browse files
Initial dynamic extension library support
1 parent 669e988 commit 25c1d3b

File tree

8 files changed

+83
-42
lines changed

8 files changed

+83
-42
lines changed

lib/ruby_wasm/build/product/crossruby.rb

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ def build(executor, crossruby)
4444
objdir = product_build_dir crossruby
4545
executor.mkdir_p objdir
4646
do_extconf executor, crossruby
47+
48+
build_target = crossruby.target.pic? ? "install-so" : "static"
4749
executor.system "make",
4850
"-j#{executor.process_count}",
4951
"-C",
5052
"#{objdir}",
5153
*make_args(crossruby),
52-
"static"
54+
build_target
5355
# A ext can provide link args by link.filelist. It contains only built archive file by default.
5456
unless File.exist?(linklist(crossruby))
5557
executor.write(
@@ -62,37 +64,48 @@ def build(executor, crossruby)
6264
def do_extconf(executor, crossruby)
6365
objdir = product_build_dir crossruby
6466
source = crossruby.source
65-
extconf_args = [
66-
"-C", objdir,
67-
"--disable=gems",
68-
# HACK: top_srcdir is required to find ruby headers
69-
"-e",
70-
%Q($top_srcdir="#{source.src_dir}"),
71-
# HACK: extout is required to find config.h
72-
"-e",
73-
%Q($extout="#{crossruby.build_dir}/.ext"),
74-
# HACK: force static ext build by imitating extmk
75-
"-e",
76-
"$static = true; trace_var(:$static) {|v| $static = true }",
77-
# HACK: $0 should be extconf.rb path due to mkmf source file detection
78-
# and we want to insert some hacks before it. But -e and $0 cannot be
79-
# used together, so we rewrite $0 in -e.
80-
"-e",
81-
%Q($0="#{@srcdir}/extconf.rb"),
82-
"-e",
83-
%Q(require_relative "#{@srcdir}/extconf.rb"),
84-
# HACK: extract "$target" from extconf.rb to get a full target name
85-
# like "cgi/escape" instead of "escape"
86-
"-e",
87-
%Q(require "json"; File.write("#{metadata_json(crossruby)}", JSON.dump({target: $target}))),
88-
"-I#{crossruby.build_dir}"
89-
]
90-
# Clear RUBYOPT to avoid loading unrelated bundle setup
91-
executor.system crossruby.baseruby_path,
67+
extconf_args = ["-C", objdir]
68+
# @type var extconf_env: Hash[String, String]
69+
extconf_env = {}
70+
71+
if crossruby.target.pic?
72+
extconf_args.concat [
73+
"#{@srcdir}/extconf.rb",
74+
]
75+
rbconfig_rb = Dir.glob(File.join(crossruby.dest_dir, "usr/local/lib/ruby/*/wasm32-wasi/rbconfig.rb")).first
76+
raise "rbconfig.rb not found" unless rbconfig_rb
77+
extconf_env["MKMF_TARGET_RBCONFIG"] = rbconfig_rb
78+
else
79+
extconf_args.concat [
80+
"--disable=gems",
81+
# HACK: top_srcdir is required to find ruby headers
82+
"-e",
83+
%Q($top_srcdir="#{source.src_dir}"),
84+
# HACK: extout is required to find config.h
85+
"-e",
86+
%Q($extout="#{crossruby.build_dir}/.ext"),
87+
# HACK: force static ext build by imitating extmk
88+
"-e",
89+
"$static = true; trace_var(:$static) {|v| $static = true }",
90+
# HACK: $0 should be extconf.rb path due to mkmf source file detection
91+
# and we want to insert some hacks before it. But -e and $0 cannot be
92+
# used together, so we rewrite $0 in -e.
93+
"-e",
94+
%Q($0="#{@srcdir}/extconf.rb"),
95+
"-e",
96+
%Q(require_relative "#{@srcdir}/extconf.rb"),
97+
# HACK: extract "$target" from extconf.rb to get a full target name
98+
# like "cgi/escape" instead of "escape"
99+
"-e",
100+
%Q(require "json"; File.write("#{metadata_json(crossruby)}", JSON.dump({target: $target}))),
101+
"-I#{crossruby.build_dir}"
102+
]
103+
# Clear RUBYOPT to avoid loading unrelated bundle setup
104+
extconf_env["RUBYOPT"] = ""
105+
end
106+
executor.system Gem.ruby,
92107
*extconf_args,
93-
env: {
94-
"RUBYOPT" => ""
95-
}
108+
env: extconf_env
96109
end
97110

98111
def do_install_rb(executor, crossruby)
@@ -114,7 +127,7 @@ def cache_key(digest)
114127
end
115128

116129
class CrossRubyProduct < AutoconfProduct
117-
attr_reader :source, :toolchain
130+
attr_reader :target, :source, :toolchain
118131
attr_accessor :user_exts,
119132
:wasmoptflags,
120133
:cppflags,
@@ -155,6 +168,10 @@ def need_exts_build?
155168
@user_exts.any?
156169
end
157170

171+
def need_extinit_obj?
172+
need_exts_build? && !@target.pic?
173+
end
174+
158175
def build_exts(executor)
159176
@user_exts.each do |prod|
160177
executor.begin_section prod.class, prod.name, "Building"
@@ -181,7 +198,7 @@ def build(executor, remake: false, reconfigure: false)
181198

182199
executor.begin_section self.class, name, "Building"
183200

184-
if need_exts_build?
201+
if need_extinit_obj?
185202
executor.mkdir_p File.dirname(extinit_obj)
186203
executor.system "ruby",
187204
extinit_c_erb,
@@ -312,7 +329,7 @@ def configure_args(build_triple, toolchain)
312329

313330
args.concat(self.tools_args)
314331
(@user_exts || []).each { |lib| xldflags << "@#{lib.linklist(self)}" }
315-
xldflags << extinit_obj if need_exts_build?
332+
xldflags << extinit_obj if need_extinit_obj?
316333

317334
cflags = @cflags.dup
318335
xcflags = @xcflags.dup

lib/ruby_wasm/cli.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,15 @@ def build(args)
122122
end
123123
.parse!(args)
124124

125+
__skip__ = Bundler.settings.temporary(force_ruby_platform: true) do
126+
do_build_with_force_ruby_platform(options)
127+
end
128+
end
129+
130+
def do_build_with_force_ruby_platform(options)
125131
verbose = RubyWasm.logger.level == :debug
126132
executor = RubyWasm::BuildExecutor.new(verbose: verbose)
133+
127134
packager = self.derive_packager(options)
128135

129136
if options[:print_ruby_cache_key]
@@ -281,7 +288,7 @@ def self.bundled_patches_path
281288
end
282289

283290
def derive_packager(options)
284-
definition = nil
291+
__skip__ = definition = nil
285292
__skip__ = if defined?(Bundler) && !options[:disable_gems]
286293
begin
287294
# Silence Bundler UI if --print-ruby-cache-key is specified not to bother the JSON output.
@@ -293,6 +300,7 @@ def derive_packager(options)
293300
Bundler.ui.level = old_level
294301
end
295302
end
303+
RubyWasm.logger.info "Using Gemfile: #{definition.gemfiles}" if definition
296304
RubyWasm::Packager.new(root, build_config(options), definition)
297305
end
298306

lib/ruby_wasm/packager.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ def ruby_core_build
5858
# Retrieves the specs from the Bundler definition, excluding the excluded gems.
5959
def specs
6060
return [] unless @definition
61-
@definition.specs.reject { |spec| EXCLUDED_GEMS.include?(spec.name) }
61+
@specs ||= @definition.resolve.materialize(@definition.requested_dependencies)
62+
.reject { |spec| EXCLUDED_GEMS.include?(spec.name) }
63+
@specs
6264
end
6365

6466
# Checks if dynamic linking is supported.

lib/ruby_wasm/packager/core.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ def build(executor, options)
6666
force_rebuild =
6767
options[:remake] || options[:clean] || options[:reconfigure]
6868
if File.exist?(build.crossruby.artifact) && !force_rebuild
69+
# Always build extensions because they are usually not expensive to build
70+
self.build_exts(executor, build)
6971
return build.crossruby.artifact
7072
end
7173
build.crossruby.clean(executor) if options[:clean]
@@ -85,11 +87,12 @@ def build(executor, options)
8587
else
8688
do_build.call
8789
end
90+
self.build_exts(executor, build)
8891
build.crossruby.artifact
8992
end
9093

91-
def build_exts(build)
92-
specs_with_extensions.flat_map do |spec, exts|
94+
def build_exts(executor, build)
95+
exts = specs_with_extensions.flat_map do |spec, exts|
9396
exts.map do |ext|
9497
ext_feature = File.dirname(ext) # e.g. "ext/cgi/escape"
9598
ext_srcdir = File.join(spec.full_gem_path, ext_feature)
@@ -101,6 +104,12 @@ def build_exts(build)
101104
)
102105
end
103106
end
107+
108+
exts.each do |prod|
109+
executor.begin_section prod.class, prod.name, "Building"
110+
prod.build(executor, build.crossruby)
111+
executor.end_section prod.class, prod.name
112+
end
104113
end
105114

106115
def cache_key(digest)

lib/ruby_wasm/packager/file_system.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,14 @@ def setup_rb_content
6565
end
6666

6767
def remove_non_runtime_files(executor)
68-
%w[
69-
**/*.so
68+
patterns = %w[
7069
usr/local/lib/libruby-static.a
7170
usr/local/bin/ruby
7271
usr/local/include
73-
].each do |pattern|
72+
]
73+
74+
patterns << "**/*.so" unless @packager.support_dynamic_linking?
75+
patterns.each do |pattern|
7476
Dir
7577
.glob(File.join(@dest_dir, pattern))
7678
.each do |entry|

packages/gems/js/ext/witapi/depend

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ witapi-core.o: $(srcdir)/bindgen/rb-abi-guest.h
88

99
bindgen/%.o: $(srcdir)/bindgen/%.c
1010
@mkdir -p "$(@D)"
11-
$(CC) -c -I$(srcdir)/bindgen -o $@ $<
11+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$< -I$(srcdir)/bindgen

sig/ruby_wasm/build.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ module RubyWasm
184184
@openssl: OpenSSLProduct
185185
@wasi_vfs: WasiVfsProduct
186186

187+
attr_reader target: Target
187188
attr_reader source: BuildSource
188189
attr_reader toolchain: Toolchain
189190
attr_accessor user_exts: Array[CrossRubyExtProduct]
@@ -197,6 +198,7 @@ module RubyWasm
197198
def initialize: (BuildParams params, String build_dir, String rubies_dir, BaseRubyProduct baseruby, BuildSource source, Toolchain toolchain) -> void
198199
def configure: (BuildExecutor executor, ?reconfigure: bool) -> void
199200
def need_exts_build?: -> bool
201+
def need_extinit_obj?: -> bool
200202
def build_exts: (BuildExecutor executor) -> void
201203
def build: (BuildExecutor executor, ?remake: bool, ?reconfigure: bool) -> void
202204
def clean: (BuildExecutor executor) -> void

sig/ruby_wasm/packager.rbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class RubyWasm::Packager
6868
class DynamicLinking < RubyWasm::Packager::Core::BuildStrategy
6969
@build: RubyWasm::Build
7070
def derive_build: () -> RubyWasm::Build
71+
def build_exts: (RubyWasm::BuildExecutor, RubyWasm::Build) -> void
7172
def name: () -> string
7273
end
7374

0 commit comments

Comments
 (0)