Skip to content

Commit b7ff15d

Browse files
Allow configuring WASI SDK version per Ruby version
1 parent ef7abfb commit b7ff15d

File tree

10 files changed

+153
-79
lines changed

10 files changed

+153
-79
lines changed

Steepfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ target :lib do
1919
library "logger"
2020
library "pathname"
2121
library "forwardable"
22+
library "net/http"
2223

2324
configure_code_diagnostics(D::Ruby.default)
2425
end

lib/ruby_wasm/build.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def initialize(
4343
@target = target
4444
@build_dir = build_dir
4545
@rubies_dir = rubies_dir
46-
@toolchain = (toolchain || RubyWasm::Toolchain.get(target, @build_dir))
46+
@toolchain = toolchain || raise("toolchain is required")
4747

4848
@libyaml = RubyWasm::LibYAMLProduct.new(@build_dir, @target, @toolchain)
4949
@zlib = RubyWasm::ZlibProduct.new(@build_dir, @target, @toolchain)

lib/ruby_wasm/build/executor.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,19 @@ def _print_command(args, env)
147147
end
148148
end
149149

150+
class SilentExecutor
151+
def system(*args, chdir: nil, env: nil)
152+
kwargs = {}
153+
kwargs[:chdir] = chdir if chdir
154+
kwargs[:exception] = true
155+
if env
156+
Kernel.system(env, *args, **kwargs)
157+
else
158+
Kernel.system(*args, **kwargs)
159+
end
160+
end
161+
end
162+
150163
# Human readable status printer for the build.
151164
class StatusPrinter
152165
def initialize

lib/ruby_wasm/build/product/crossruby.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def build_exts(executor)
204204
def build(executor, remake: false, reconfigure: false)
205205
executor.mkdir_p dest_dir
206206
executor.mkdir_p build_dir
207-
@toolchain.install
207+
@toolchain.install(executor)
208208
[@source, @baseruby, @libyaml, @zlib, @openssl, @wasi_vfs].each do |prod|
209209
next unless prod
210210
executor.begin_section prod.class, prod.name, "Building"

lib/ruby_wasm/build/toolchain.rb

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ def check_envvar(name)
1616
raise "missing environment variable: #{name}" if ENV[name].nil?
1717
end
1818

19-
def self.get(target, build_dir = nil)
19+
def self.get(target, options, build_dir = nil)
2020
case target
2121
when /^wasm32-unknown-wasi/
22-
return RubyWasm::WASISDK.new(build_dir: build_dir)
22+
return RubyWasm::WASISDK.new(build_dir: build_dir, version: options[:wasi_sdk_version])
2323
when "wasm32-unknown-emscripten"
2424
return RubyWasm::Emscripten.new
2525
else
@@ -56,18 +56,19 @@ class WASISDK < Toolchain
5656
def initialize(
5757
wasi_sdk_path = ENV["WASI_SDK_PATH"],
5858
build_dir: nil,
59-
version_major: 22,
60-
version_minor: 0,
59+
version: "23.0",
6160
binaryen_version: 108
6261
)
6362
@need_fetch_wasi_sdk = wasi_sdk_path.nil?
6463
if @need_fetch_wasi_sdk
6564
if build_dir.nil?
6665
raise "build_dir is required when WASI_SDK_PATH is not set"
6766
end
68-
wasi_sdk_path = File.join(build_dir, "toolchain", "wasi-sdk")
69-
@version_major = version_major
70-
@version_minor = version_minor
67+
wasi_sdk_path = File.join(build_dir, "toolchain", "wasi-sdk-#{version}")
68+
if version.nil?
69+
raise "version is required when WASI_SDK_PATH is not set"
70+
end
71+
@version = version
7172
end
7273

7374
@binaryen = Binaryen.new(build_dir: build_dir, binaryen_version: binaryen_version)
@@ -98,36 +99,69 @@ def wasi_sdk_path
9899
@wasi_sdk_path
99100
end
100101

101-
def download_url(version_major, version_minor)
102-
version = "#{version_major}.#{version_minor}"
102+
def download_url
103+
major, _ = @version.split(".").map(&:to_i)
104+
# @type var assets: Array[[Regexp, Array[String]]]
103105
assets = [
104-
[/x86_64-linux/, "wasi-sdk-#{version}-linux.tar.gz"],
105-
[/(arm64e?|x86_64)-darwin/, "wasi-sdk-#{version}-macos.tar.gz"]
106+
[/x86_64-linux/, [
107+
"wasi-sdk-#{@version}-x86_64-linux.tar.gz",
108+
# For wasi-sdk version < 23.0
109+
"wasi-sdk-#{@version}-linux.tar.gz",
110+
]],
111+
[/arm64e?-darwin/, [
112+
"wasi-sdk-#{@version}-arm64-macos.tar.gz",
113+
# For wasi-sdk version < 23.0
114+
"wasi-sdk-#{@version}-macos.tar.gz",
115+
]],
116+
[/x86_64-darwin/, [
117+
"wasi-sdk-#{@version}-x86_64-macos.tar.gz",
118+
# For wasi-sdk version < 23.0
119+
"wasi-sdk-#{@version}-macos.tar.gz",
120+
]],
106121
]
107-
asset = assets.find { |os, _| os =~ RUBY_PLATFORM }&.at(1)
122+
asset = assets.find do |os, candidates|
123+
os =~ RUBY_PLATFORM
124+
end
108125
if asset.nil?
109126
raise "unsupported platform for fetching WASI SDK: #{RUBY_PLATFORM}"
110127
end
111-
"https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{version_major}/#{asset}"
128+
_, candidates = asset
129+
candidates_urls = candidates.map do |candidate|
130+
"https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{major}/#{candidate}"
131+
end
132+
require "net/http"
133+
# Find an asset that exists by checking HEAD response to see if the asset exists
134+
candidates_urls.each do |url_str|
135+
# @type var url: URI::HTTPS
136+
url = URI.parse(url_str)
137+
ok = Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
138+
response = http.head(url.request_uri)
139+
next response.code == "302"
140+
end
141+
if ok
142+
return url_str
143+
end
144+
end
145+
raise "WASI SDK asset not found: #{candidates_urls.join(", ")}"
112146
end
113147

114-
def install_wasi_sdk
148+
def install_wasi_sdk(executor)
115149
return unless @need_fetch_wasi_sdk
116150
wasi_sdk_tarball =
117-
File.join(File.dirname(@wasi_sdk_path), "wasi-sdk.tar.gz")
151+
File.join(File.dirname(@wasi_sdk_path), "wasi-sdk-#{@version}.tar.gz")
118152
unless File.exist? wasi_sdk_tarball
119153
FileUtils.mkdir_p File.dirname(wasi_sdk_tarball)
120-
system "curl -L -o #{wasi_sdk_tarball} #{self.download_url(@version_major, @version_minor)}"
154+
executor.system "curl", "-fsSL", "-o", wasi_sdk_tarball, self.download_url
121155
end
122156
unless File.exist? @wasi_sdk_path
123157
FileUtils.mkdir_p @wasi_sdk_path
124-
system "tar -C #{@wasi_sdk_path} --strip-component 1 -xzf #{wasi_sdk_tarball}"
158+
executor.system "tar", "-C", @wasi_sdk_path, "--strip-component", "1", "-xzf", wasi_sdk_tarball
125159
end
126160
end
127161

128-
def install
129-
install_wasi_sdk
130-
@binaryen.install
162+
def install(executor)
163+
install_wasi_sdk(executor)
164+
@binaryen.install(executor)
131165
end
132166
end
133167

@@ -179,17 +213,17 @@ def download_url(version)
179213
"https://github.com/WebAssembly/binaryen/releases/download/version_#{@binaryen_version}/#{asset}"
180214
end
181215

182-
def install
216+
def install(executor)
183217
return unless @need_fetch_binaryen
184218
binaryen_tarball = File.expand_path("../binaryen.tar.gz", @binaryen_path)
185219
unless File.exist? binaryen_tarball
186220
FileUtils.mkdir_p File.dirname(binaryen_tarball)
187-
system "curl -L -o #{binaryen_tarball} #{self.download_url(@binaryen_version)}"
221+
executor.system "curl", "-L", "-o", binaryen_tarball, self.download_url(@binaryen_version)
188222
end
189223

190224
unless File.exist? @binaryen_path
191225
FileUtils.mkdir_p @binaryen_path
192-
system "tar -C #{@binaryen_path} --strip-component 1 -xzf #{binaryen_tarball}"
226+
executor.system "tar", "-C", @binaryen_path, "--strip-component", "1", "-xzf", binaryen_tarball
193227
end
194228
end
195229

@@ -201,7 +235,7 @@ def initialize
201235
@name = "emscripten"
202236
end
203237

204-
def install
238+
def install(executor)
205239
end
206240

207241
def find_tool(name)

lib/ruby_wasm/cli.rb

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@ def pack(args)
181181
private
182182

183183
def build_config(options)
184-
build_source, all_default_exts = compute_build_source(options)
185184
# @type var config: Packager::build_config
186-
config = { target: options[:target_triplet], src: build_source }
185+
config = compute_build_alias(options)
186+
config[:target] = options[:target_triplet]
187187
case options[:profile]
188188
when "full"
189-
config[:default_exts] = all_default_exts || ""
189+
config[:default_exts] = config[:all_default_exts] || ""
190190
env_additional_exts = ENV["RUBY_WASM_ADDITIONAL_EXTS"] || ""
191191
unless env_additional_exts.empty?
192192
config[:default_exts] += "," + env_additional_exts
@@ -201,80 +201,103 @@ def build_config(options)
201201
config
202202
end
203203

204-
def compute_build_source(options)
204+
def compute_build_alias(options)
205205
src_name = options[:ruby_version]
206-
aliases = self.class.build_source_aliases(root)
207-
source = aliases[src_name]
208-
if source.nil?
206+
aliases = self.class.build_config_aliases(root)
207+
config = aliases[src_name]
208+
if config.nil?
209209
if File.directory?(src_name)
210210
# Treat as a local source if the given name is a source directory.
211211
RubyWasm.logger.debug "Using local source: #{src_name}"
212212
if options[:patches].any?
213213
RubyWasm.logger.warn "Patches specified through --patch are ignored for local sources"
214214
end
215-
# @type var local_source: RubyWasm::Packager::build_source_local
216-
local_source = { type: "local", path: src_name }
217-
# @type var local_source: RubyWasm::Packager::build_source
218-
local_source = local_source.merge(name: "local", patches: [])
219215
# FIXME: We should have a way to specify extensions to be included by users.
220216
# For now, assume all default extensions available in the head revision are available.
221-
return [local_source, RubyWasm::Packager::ALL_DEFAULT_EXTS]
217+
return {
218+
name: "local",
219+
src: {
220+
type: "local",
221+
path: src_name,
222+
patches: []
223+
},
224+
all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS,
225+
}
222226
end
223227
# Otherwise, it's an unknown source.
224228
raise(
225229
"Unknown Ruby source: #{src_name} (available: #{aliases.keys.join(", ")} or a local directory)"
226230
)
227231
end
228232
# Apply user-specified patches in addition to bundled patches.
229-
source[:patches].concat(options[:patches])
230-
# @type var all_default_exts: String
231-
__skip__ = all_default_exts = source[:all_default_exts]
232-
[source, all_default_exts]
233+
config[:src][:patches].concat(options[:patches])
234+
config
233235
end
234236

235237
# Retrieves the alias definitions for the Ruby sources.
236-
def self.build_source_aliases(root)
237-
# @type var sources: Hash[string, RubyWasm::Packager::build_source]
238-
sources = {
239-
"head" => {
240-
type: "github",
241-
repo: "ruby/ruby",
242-
rev: "master",
238+
def self.build_config_aliases(root)
239+
# @type var aliases: Array[RubyWasm::Packager::build_source]
240+
aliases = [
241+
{
242+
name: "head",
243+
src: {
244+
type: "github",
245+
repo: "ruby/ruby",
246+
rev: "master",
247+
},
243248
all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS,
249+
wasi_sdk_version: "23.0",
244250
},
245-
"3.4" => {
246-
type: "tarball",
247-
url: "https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.1.tar.gz",
251+
{
252+
name: "3.4",
253+
src: {
254+
type: "tarball",
255+
url: "https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.1.tar.gz",
256+
},
248257
all_default_exts: "cgi/escape,continuation,coverage,date,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,json,json/generator,json/parser,objspace,pathname,psych,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl",
258+
wasi_sdk_version: "22.0",
249259
},
250-
"3.3" => {
251-
type: "tarball",
252-
url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz",
260+
{
261+
name: "3.3",
262+
src: {
263+
type: "tarball",
264+
url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz",
265+
},
253266
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",
267+
wasi_sdk_version: "22.0",
254268
},
255-
"3.2" => {
256-
type: "tarball",
257-
url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.4.tar.gz",
269+
{
270+
name: "3.2",
271+
src: {
272+
type: "tarball",
273+
url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.4.tar.gz",
274+
},
258275
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",
276+
wasi_sdk_version: "22.0",
259277
}
260-
}
278+
]
279+
280+
# Set the name in the source config.
281+
aliases.each do |config|
282+
config[:src][:name] = config[:name]
283+
end
261284

262285
# Apply bundled and user-specified `<root>/patches` directories.
263-
sources.each do |name, source|
264-
source[:name] = name
286+
aliases.each do |config|
265287
patches_dirs = [bundled_patches_path, File.join(root, "patches")]
266-
source[:patches] = patches_dirs.flat_map do |patches_dir|
267-
Dir[File.join(patches_dir, name, "*.patch")]
288+
config[:src][:patches] = patches_dirs.flat_map do |patches_dir|
289+
Dir[File.join(patches_dir, config[:name], "*.patch")]
268290
.map { |p| File.expand_path(p) }
269291
end.uniq
270292
end
271293

294+
# Pin the revisions based on build_manifest.json if available.
272295
build_manifest = File.join(root, "build_manifest.json")
273296
if File.exist?(build_manifest)
274297
begin
275298
manifest = JSON.parse(File.read(build_manifest))
276299
manifest["ruby_revisions"].each do |name, rev|
277-
source = sources[name]
300+
source = aliases[name][:src]
278301
next unless source[:type] == "github"
279302
# @type var source: RubyWasm::Packager::build_source_github
280303
source[:rev] = rev
@@ -283,7 +306,7 @@ def self.build_source_aliases(root)
283306
RubyWasm.logger.warn "Failed to load build_manifest.json: #{e}"
284307
end
285308
end
286-
sources
309+
aliases.to_h { |config| [config[:name], config] }
287310
end
288311

289312
# Retrieves the root directory of the Ruby project.

lib/ruby_wasm/packager.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def full_build_options
111111
options = build_options
112112
build_dir = File.join(@root, "build")
113113
rubies_dir = File.join(@root, "rubies")
114-
toolchain = RubyWasm::Toolchain.get(options[:target], build_dir)
114+
toolchain = RubyWasm::Toolchain.get(options[:target], options, build_dir)
115115
options.merge(
116116
toolchain: toolchain,
117117
build_dir: build_dir,

lib/ruby_wasm/packager/core.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def _link_gem_exts(executor, build, ruby_root, gem_home, module_bytes)
179179
end
180180

181181
def _build_gem_exts(executor, build, gem_home)
182-
build.toolchain.install
182+
build.toolchain.install(executor)
183183
baseruby = build.baseruby
184184
unless Dir.exist?(baseruby.install_dir)
185185
baseruby.build(executor)

0 commit comments

Comments
 (0)