From ef7abfbbe72a4676b464b7592b418fec4c97d5ff Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 24 Dec 2025 14:21:29 +0900 Subject: [PATCH 1/3] build: Remove TOOLCHAINS constant and move Binaryen to its own class --- Rakefile | 10 ---- lib/ruby_wasm/build/toolchain.rb | 84 +++++++++++++++++++------------- rakelib/packaging.rake | 4 +- sig/ruby_wasm/build.rbs | 18 +++++-- 4 files changed, 68 insertions(+), 48 deletions(-) diff --git a/Rakefile b/Rakefile index a90a6aecd..858dacc19 100644 --- a/Rakefile +++ b/Rakefile @@ -66,16 +66,6 @@ STANDALONE_PACKAGES = [ LIB_ROOT = File.dirname(__FILE__) -TOOLCHAINS = {} -BUILDS - .map { |_, target, _| target } - .uniq - .each do |target| - build_dir = File.join(LIB_ROOT, "build") - toolchain = RubyWasm::Toolchain.get(target, build_dir) - TOOLCHAINS[toolchain.name] = toolchain - end - class BuildTask < Struct.new(:name, :target, :build_command) def ruby_cache_key return @key if @key diff --git a/lib/ruby_wasm/build/toolchain.rb b/lib/ruby_wasm/build/toolchain.rb index 5c1043223..6aac58e2b 100644 --- a/lib/ruby_wasm/build/toolchain.rb +++ b/lib/ruby_wasm/build/toolchain.rb @@ -60,10 +60,7 @@ def initialize( version_minor: 0, binaryen_version: 108 ) - @wasm_opt_path = Toolchain.find_path("wasm-opt") @need_fetch_wasi_sdk = wasi_sdk_path.nil? - @need_fetch_binaryen = @wasm_opt_path.nil? - if @need_fetch_wasi_sdk if build_dir.nil? raise "build_dir is required when WASI_SDK_PATH is not set" @@ -73,14 +70,7 @@ def initialize( @version_minor = version_minor end - if @need_fetch_binaryen - if build_dir.nil? - raise "build_dir is required when wasm-opt not installed in PATH" - end - @binaryen_path = File.join(build_dir, "toolchain", "binaryen") - @binaryen_version = binaryen_version - @wasm_opt_path = File.join(@binaryen_path, "bin", "wasm-opt") - end + @binaryen = Binaryen.new(build_dir: build_dir, binaryen_version: binaryen_version) @tools = { cc: "#{wasi_sdk_path}/bin/clang", @@ -101,7 +91,7 @@ def find_tool(name) end def wasm_opt - @wasm_opt_path + @binaryen.wasm_opt end def wasi_sdk_path @@ -121,7 +111,53 @@ def download_url(version_major, version_minor) "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{version_major}/#{asset}" end - def binaryen_download_url(version) + def install_wasi_sdk + return unless @need_fetch_wasi_sdk + wasi_sdk_tarball = + File.join(File.dirname(@wasi_sdk_path), "wasi-sdk.tar.gz") + unless File.exist? wasi_sdk_tarball + FileUtils.mkdir_p File.dirname(wasi_sdk_tarball) + system "curl -L -o #{wasi_sdk_tarball} #{self.download_url(@version_major, @version_minor)}" + end + unless File.exist? @wasi_sdk_path + FileUtils.mkdir_p @wasi_sdk_path + system "tar -C #{@wasi_sdk_path} --strip-component 1 -xzf #{wasi_sdk_tarball}" + end + end + + def install + install_wasi_sdk + @binaryen.install + end + end + + class Binaryen + def initialize(build_dir: nil, binaryen_version: 108) + @wasm_opt_path = Toolchain.find_path("wasm-opt") + @need_fetch_binaryen = @wasm_opt_path.nil? + if @need_fetch_binaryen + if build_dir.nil? + raise "build_dir is required when wasm-opt not installed in PATH" + end + @binaryen_path = File.join(build_dir, "toolchain", "binaryen") + @binaryen_version = binaryen_version + @wasm_opt_path = File.join(@binaryen_path, "bin", "wasm-opt") + end + end + + def wasm_opt + @wasm_opt_path + end + + def binaryen_path + @binaryen_path + end + + def binaryen_version + @binaryen_version + end + + def download_url(version) assets = [ [ /x86_64-linux/, @@ -143,26 +179,12 @@ def binaryen_download_url(version) "https://github.com/WebAssembly/binaryen/releases/download/version_#{@binaryen_version}/#{asset}" end - def install_wasi_sdk - return unless @need_fetch_wasi_sdk - wasi_sdk_tarball = - File.join(File.dirname(@wasi_sdk_path), "wasi-sdk.tar.gz") - unless File.exist? wasi_sdk_tarball - FileUtils.mkdir_p File.dirname(wasi_sdk_tarball) - system "curl -L -o #{wasi_sdk_tarball} #{self.download_url(@version_major, @version_minor)}" - end - unless File.exist? @wasi_sdk_path - FileUtils.mkdir_p @wasi_sdk_path - system "tar -C #{@wasi_sdk_path} --strip-component 1 -xzf #{wasi_sdk_tarball}" - end - end - - def install_binaryen + def install return unless @need_fetch_binaryen binaryen_tarball = File.expand_path("../binaryen.tar.gz", @binaryen_path) unless File.exist? binaryen_tarball FileUtils.mkdir_p File.dirname(binaryen_tarball) - system "curl -L -o #{binaryen_tarball} #{self.binaryen_download_url(@binaryen_version)}" + system "curl -L -o #{binaryen_tarball} #{self.download_url(@binaryen_version)}" end unless File.exist? @binaryen_path @@ -171,10 +193,6 @@ def install_binaryen end end - def install - install_wasi_sdk - install_binaryen - end end class Emscripten < Toolchain diff --git a/rakelib/packaging.rake b/rakelib/packaging.rake index 1f2da10d6..878fffb1d 100644 --- a/rakelib/packaging.rake +++ b/rakelib/packaging.rake @@ -1,10 +1,10 @@ wasi_vfs = RubyWasm::WasiVfsProduct.new(File.join(Dir.pwd, "build")) -wasi_sdk = TOOLCHAINS["wasi-sdk"] +binaryen = RubyWasm::Binaryen.new(build_dir: File.join(Dir.pwd, "build")) def exe_rbwasm = File.expand_path(File.join(__dir__, "..", "exe", "rbwasm")) tools = { "WASI_VFS_CLI" => exe_rbwasm, - "WASMOPT" => wasi_sdk.wasm_opt + "WASMOPT" => binaryen.wasm_opt } def npm_pkg_build_command(pkg) diff --git a/sig/ruby_wasm/build.rbs b/sig/ruby_wasm/build.rbs index 47a381c67..14e94a520 100644 --- a/sig/ruby_wasm/build.rbs +++ b/sig/ruby_wasm/build.rbs @@ -255,13 +255,11 @@ module RubyWasm class WASISDK < Toolchain @wasm_opt_path: String @need_fetch_wasi_sdk: bool - @need_fetch_binaryen: bool @tools: Hash[Symbol, String] @wasi_sdk_path: String - @binaryen_version: Integer @version_major: Integer @version_minor: Integer - @binaryen_path: String + @binaryen: Binaryen def initialize: (?String? wasi_sdk_path, ?build_dir: String?, ?version_major: Integer, ?version_minor: Integer, ?binaryen_version: Integer) -> void def find_tool: (Symbol name) -> String @@ -273,6 +271,20 @@ module RubyWasm def install_binaryen: -> void end + class Binaryen + @need_fetch_binaryen: bool + @binaryen_path: String + @binaryen_version: Integer + @wasm_opt_path: String + + def initialize: (?build_dir: String?, ?binaryen_version: Integer) -> void + def wasm_opt: -> String + def binaryen_path: -> String + def binaryen_version: -> Integer + def download_url: (Integer? version) -> String + def install: -> void + end + class Emscripten < Toolchain @tools: Hash[Symbol, String] From b7ff15d7ae43dc948ccb49d7d4609d3c72038483 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 27 Dec 2025 17:58:55 +0900 Subject: [PATCH 2/3] Allow configuring WASI SDK version per Ruby version --- Steepfile | 1 + lib/ruby_wasm/build.rb | 2 +- lib/ruby_wasm/build/executor.rb | 13 +++ lib/ruby_wasm/build/product/crossruby.rb | 2 +- lib/ruby_wasm/build/toolchain.rb | 82 ++++++++++++------ lib/ruby_wasm/cli.rb | 101 ++++++++++++++--------- lib/ruby_wasm/packager.rb | 2 +- lib/ruby_wasm/packager/core.rb | 2 +- rakelib/packaging.rake | 16 ++-- sig/ruby_wasm/build.rbs | 11 +-- 10 files changed, 153 insertions(+), 79 deletions(-) diff --git a/Steepfile b/Steepfile index 6bb7851cc..34eeb4bdd 100644 --- a/Steepfile +++ b/Steepfile @@ -19,6 +19,7 @@ target :lib do library "logger" library "pathname" library "forwardable" + library "net/http" configure_code_diagnostics(D::Ruby.default) end diff --git a/lib/ruby_wasm/build.rb b/lib/ruby_wasm/build.rb index 436ae2baf..37e095a98 100644 --- a/lib/ruby_wasm/build.rb +++ b/lib/ruby_wasm/build.rb @@ -43,7 +43,7 @@ def initialize( @target = target @build_dir = build_dir @rubies_dir = rubies_dir - @toolchain = (toolchain || RubyWasm::Toolchain.get(target, @build_dir)) + @toolchain = toolchain || raise("toolchain is required") @libyaml = RubyWasm::LibYAMLProduct.new(@build_dir, @target, @toolchain) @zlib = RubyWasm::ZlibProduct.new(@build_dir, @target, @toolchain) diff --git a/lib/ruby_wasm/build/executor.rb b/lib/ruby_wasm/build/executor.rb index ed1b1c02f..5df62cb40 100644 --- a/lib/ruby_wasm/build/executor.rb +++ b/lib/ruby_wasm/build/executor.rb @@ -147,6 +147,19 @@ def _print_command(args, env) end end + class SilentExecutor + def system(*args, chdir: nil, env: nil) + kwargs = {} + kwargs[:chdir] = chdir if chdir + kwargs[:exception] = true + if env + Kernel.system(env, *args, **kwargs) + else + Kernel.system(*args, **kwargs) + end + end + end + # Human readable status printer for the build. class StatusPrinter def initialize diff --git a/lib/ruby_wasm/build/product/crossruby.rb b/lib/ruby_wasm/build/product/crossruby.rb index a4c5d2d43..330cb8480 100644 --- a/lib/ruby_wasm/build/product/crossruby.rb +++ b/lib/ruby_wasm/build/product/crossruby.rb @@ -204,7 +204,7 @@ def build_exts(executor) def build(executor, remake: false, reconfigure: false) executor.mkdir_p dest_dir executor.mkdir_p build_dir - @toolchain.install + @toolchain.install(executor) [@source, @baseruby, @libyaml, @zlib, @openssl, @wasi_vfs].each do |prod| next unless prod executor.begin_section prod.class, prod.name, "Building" diff --git a/lib/ruby_wasm/build/toolchain.rb b/lib/ruby_wasm/build/toolchain.rb index 6aac58e2b..4daf833f4 100644 --- a/lib/ruby_wasm/build/toolchain.rb +++ b/lib/ruby_wasm/build/toolchain.rb @@ -16,10 +16,10 @@ def check_envvar(name) raise "missing environment variable: #{name}" if ENV[name].nil? end - def self.get(target, build_dir = nil) + def self.get(target, options, build_dir = nil) case target when /^wasm32-unknown-wasi/ - return RubyWasm::WASISDK.new(build_dir: build_dir) + return RubyWasm::WASISDK.new(build_dir: build_dir, version: options[:wasi_sdk_version]) when "wasm32-unknown-emscripten" return RubyWasm::Emscripten.new else @@ -56,8 +56,7 @@ class WASISDK < Toolchain def initialize( wasi_sdk_path = ENV["WASI_SDK_PATH"], build_dir: nil, - version_major: 22, - version_minor: 0, + version: "23.0", binaryen_version: 108 ) @need_fetch_wasi_sdk = wasi_sdk_path.nil? @@ -65,9 +64,11 @@ def initialize( if build_dir.nil? raise "build_dir is required when WASI_SDK_PATH is not set" end - wasi_sdk_path = File.join(build_dir, "toolchain", "wasi-sdk") - @version_major = version_major - @version_minor = version_minor + wasi_sdk_path = File.join(build_dir, "toolchain", "wasi-sdk-#{version}") + if version.nil? + raise "version is required when WASI_SDK_PATH is not set" + end + @version = version end @binaryen = Binaryen.new(build_dir: build_dir, binaryen_version: binaryen_version) @@ -98,36 +99,69 @@ def wasi_sdk_path @wasi_sdk_path end - def download_url(version_major, version_minor) - version = "#{version_major}.#{version_minor}" + def download_url + major, _ = @version.split(".").map(&:to_i) + # @type var assets: Array[[Regexp, Array[String]]] assets = [ - [/x86_64-linux/, "wasi-sdk-#{version}-linux.tar.gz"], - [/(arm64e?|x86_64)-darwin/, "wasi-sdk-#{version}-macos.tar.gz"] + [/x86_64-linux/, [ + "wasi-sdk-#{@version}-x86_64-linux.tar.gz", + # For wasi-sdk version < 23.0 + "wasi-sdk-#{@version}-linux.tar.gz", + ]], + [/arm64e?-darwin/, [ + "wasi-sdk-#{@version}-arm64-macos.tar.gz", + # For wasi-sdk version < 23.0 + "wasi-sdk-#{@version}-macos.tar.gz", + ]], + [/x86_64-darwin/, [ + "wasi-sdk-#{@version}-x86_64-macos.tar.gz", + # For wasi-sdk version < 23.0 + "wasi-sdk-#{@version}-macos.tar.gz", + ]], ] - asset = assets.find { |os, _| os =~ RUBY_PLATFORM }&.at(1) + asset = assets.find do |os, candidates| + os =~ RUBY_PLATFORM + end if asset.nil? raise "unsupported platform for fetching WASI SDK: #{RUBY_PLATFORM}" end - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{version_major}/#{asset}" + _, candidates = asset + candidates_urls = candidates.map do |candidate| + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{major}/#{candidate}" + end + require "net/http" + # Find an asset that exists by checking HEAD response to see if the asset exists + candidates_urls.each do |url_str| + # @type var url: URI::HTTPS + url = URI.parse(url_str) + ok = Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http| + response = http.head(url.request_uri) + next response.code == "302" + end + if ok + return url_str + end + end + raise "WASI SDK asset not found: #{candidates_urls.join(", ")}" end - def install_wasi_sdk + def install_wasi_sdk(executor) return unless @need_fetch_wasi_sdk wasi_sdk_tarball = - File.join(File.dirname(@wasi_sdk_path), "wasi-sdk.tar.gz") + File.join(File.dirname(@wasi_sdk_path), "wasi-sdk-#{@version}.tar.gz") unless File.exist? wasi_sdk_tarball FileUtils.mkdir_p File.dirname(wasi_sdk_tarball) - system "curl -L -o #{wasi_sdk_tarball} #{self.download_url(@version_major, @version_minor)}" + executor.system "curl", "-fsSL", "-o", wasi_sdk_tarball, self.download_url end unless File.exist? @wasi_sdk_path FileUtils.mkdir_p @wasi_sdk_path - system "tar -C #{@wasi_sdk_path} --strip-component 1 -xzf #{wasi_sdk_tarball}" + executor.system "tar", "-C", @wasi_sdk_path, "--strip-component", "1", "-xzf", wasi_sdk_tarball end end - def install - install_wasi_sdk - @binaryen.install + def install(executor) + install_wasi_sdk(executor) + @binaryen.install(executor) end end @@ -179,17 +213,17 @@ def download_url(version) "https://github.com/WebAssembly/binaryen/releases/download/version_#{@binaryen_version}/#{asset}" end - def install + def install(executor) return unless @need_fetch_binaryen binaryen_tarball = File.expand_path("../binaryen.tar.gz", @binaryen_path) unless File.exist? binaryen_tarball FileUtils.mkdir_p File.dirname(binaryen_tarball) - system "curl -L -o #{binaryen_tarball} #{self.download_url(@binaryen_version)}" + executor.system "curl", "-L", "-o", binaryen_tarball, self.download_url(@binaryen_version) end unless File.exist? @binaryen_path FileUtils.mkdir_p @binaryen_path - system "tar -C #{@binaryen_path} --strip-component 1 -xzf #{binaryen_tarball}" + executor.system "tar", "-C", @binaryen_path, "--strip-component", "1", "-xzf", binaryen_tarball end end @@ -201,7 +235,7 @@ def initialize @name = "emscripten" end - def install + def install(executor) end def find_tool(name) diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index e950a5144..6fb577d69 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -181,12 +181,12 @@ def pack(args) private def build_config(options) - build_source, all_default_exts = compute_build_source(options) # @type var config: Packager::build_config - config = { target: options[:target_triplet], src: build_source } + config = compute_build_alias(options) + config[:target] = options[:target_triplet] case options[:profile] when "full" - config[:default_exts] = all_default_exts || "" + config[:default_exts] = config[:all_default_exts] || "" env_additional_exts = ENV["RUBY_WASM_ADDITIONAL_EXTS"] || "" unless env_additional_exts.empty? config[:default_exts] += "," + env_additional_exts @@ -201,24 +201,28 @@ def build_config(options) config end - def compute_build_source(options) + def compute_build_alias(options) src_name = options[:ruby_version] - aliases = self.class.build_source_aliases(root) - source = aliases[src_name] - if source.nil? + aliases = self.class.build_config_aliases(root) + config = aliases[src_name] + if config.nil? if File.directory?(src_name) # Treat as a local source if the given name is a source directory. RubyWasm.logger.debug "Using local source: #{src_name}" if options[:patches].any? RubyWasm.logger.warn "Patches specified through --patch are ignored for local sources" end - # @type var local_source: RubyWasm::Packager::build_source_local - local_source = { type: "local", path: src_name } - # @type var local_source: RubyWasm::Packager::build_source - local_source = local_source.merge(name: "local", patches: []) # FIXME: We should have a way to specify extensions to be included by users. # For now, assume all default extensions available in the head revision are available. - return [local_source, RubyWasm::Packager::ALL_DEFAULT_EXTS] + return { + name: "local", + src: { + type: "local", + path: src_name, + patches: [] + }, + all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS, + } end # Otherwise, it's an unknown source. raise( @@ -226,55 +230,74 @@ def compute_build_source(options) ) end # Apply user-specified patches in addition to bundled patches. - source[:patches].concat(options[:patches]) - # @type var all_default_exts: String - __skip__ = all_default_exts = source[:all_default_exts] - [source, all_default_exts] + config[:src][:patches].concat(options[:patches]) + config end # Retrieves the alias definitions for the Ruby sources. - def self.build_source_aliases(root) - # @type var sources: Hash[string, RubyWasm::Packager::build_source] - sources = { - "head" => { - type: "github", - repo: "ruby/ruby", - rev: "master", + def self.build_config_aliases(root) + # @type var aliases: Array[RubyWasm::Packager::build_source] + aliases = [ + { + name: "head", + src: { + type: "github", + repo: "ruby/ruby", + rev: "master", + }, all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS, + wasi_sdk_version: "23.0", }, - "3.4" => { - type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.1.tar.gz", + { + name: "3.4", + src: { + type: "tarball", + url: "https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.1.tar.gz", + }, 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", + wasi_sdk_version: "22.0", }, - "3.3" => { - type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz", + { + name: "3.3", + src: { + type: "tarball", + url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz", + }, 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", + wasi_sdk_version: "22.0", }, - "3.2" => { - type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.4.tar.gz", + { + name: "3.2", + src: { + type: "tarball", + url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.4.tar.gz", + }, 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", + wasi_sdk_version: "22.0", } - } + ] + + # Set the name in the source config. + aliases.each do |config| + config[:src][:name] = config[:name] + end # Apply bundled and user-specified `/patches` directories. - sources.each do |name, source| - source[:name] = name + aliases.each do |config| patches_dirs = [bundled_patches_path, File.join(root, "patches")] - source[:patches] = patches_dirs.flat_map do |patches_dir| - Dir[File.join(patches_dir, name, "*.patch")] + config[:src][:patches] = patches_dirs.flat_map do |patches_dir| + Dir[File.join(patches_dir, config[:name], "*.patch")] .map { |p| File.expand_path(p) } end.uniq end + # Pin the revisions based on build_manifest.json if available. build_manifest = File.join(root, "build_manifest.json") if File.exist?(build_manifest) begin manifest = JSON.parse(File.read(build_manifest)) manifest["ruby_revisions"].each do |name, rev| - source = sources[name] + source = aliases[name][:src] next unless source[:type] == "github" # @type var source: RubyWasm::Packager::build_source_github source[:rev] = rev @@ -283,7 +306,7 @@ def self.build_source_aliases(root) RubyWasm.logger.warn "Failed to load build_manifest.json: #{e}" end end - sources + aliases.to_h { |config| [config[:name], config] } end # Retrieves the root directory of the Ruby project. diff --git a/lib/ruby_wasm/packager.rb b/lib/ruby_wasm/packager.rb index f1a395303..cc43ef1db 100644 --- a/lib/ruby_wasm/packager.rb +++ b/lib/ruby_wasm/packager.rb @@ -111,7 +111,7 @@ def full_build_options options = build_options build_dir = File.join(@root, "build") rubies_dir = File.join(@root, "rubies") - toolchain = RubyWasm::Toolchain.get(options[:target], build_dir) + toolchain = RubyWasm::Toolchain.get(options[:target], options, build_dir) options.merge( toolchain: toolchain, build_dir: build_dir, diff --git a/lib/ruby_wasm/packager/core.rb b/lib/ruby_wasm/packager/core.rb index 0054633de..d4005532f 100644 --- a/lib/ruby_wasm/packager/core.rb +++ b/lib/ruby_wasm/packager/core.rb @@ -179,7 +179,7 @@ def _link_gem_exts(executor, build, ruby_root, gem_home, module_bytes) end def _build_gem_exts(executor, build, gem_home) - build.toolchain.install + build.toolchain.install(executor) baseruby = build.baseruby unless Dir.exist?(baseruby.install_dir) baseruby.build(executor) diff --git a/rakelib/packaging.rake b/rakelib/packaging.rake index 878fffb1d..e5259017c 100644 --- a/rakelib/packaging.rake +++ b/rakelib/packaging.rake @@ -51,7 +51,7 @@ def vendor_gem_cache(pkg) JS::VERSION end -def build_ruby(pkg, base_dir, pkg_dir, wasi_sdk, clean: false) +def build_ruby(pkg, base_dir, pkg_dir, binaryen, clean: false) build_command = npm_pkg_build_command(pkg) # Skip if the package does not require building ruby return unless build_command @@ -111,12 +111,12 @@ def build_ruby(pkg, base_dir, pkg_dir, wasi_sdk, clean: false) File.join(dist_dir, "ruby.debug+stdlib.wasm") end - sh wasi_sdk.wasm_opt, + sh binaryen.wasm_opt, "--strip-debug", File.join(dist_dir, "ruby.wasm"), "-o", File.join(dist_dir, "ruby.wasm") - sh wasi_sdk.wasm_opt, + sh binaryen.wasm_opt, "--strip-debug", File.join(dist_dir, "ruby.debug+stdlib.wasm"), "-o", @@ -137,7 +137,7 @@ namespace :npm do namespace pkg[:name] do desc "Build ruby for npm package #{pkg[:name]}" task "ruby" do - build_ruby(pkg, base_dir, pkg_dir, wasi_sdk) + build_ruby(pkg, base_dir, pkg_dir, binaryen) end desc "Build npm package #{pkg[:name]}" @@ -147,7 +147,7 @@ namespace :npm do desc "Clean and build npm package #{pkg[:name]}" task "clean-build" do - build_ruby(pkg, base_dir, pkg_dir, wasi_sdk, clean: true) + build_ruby(pkg, base_dir, pkg_dir, binaryen, clean: true) end desc "Check npm package #{pkg[:name]}" @@ -158,7 +158,8 @@ namespace :npm do desc "Make tarball for npm package #{pkg[:name]}" task pkg[:name] do - wasi_sdk.install_binaryen + executor = RubyWasm::BuildExecutor.new + binaryen.install(executor) Rake::Task["npm:#{pkg[:name]}:build"].invoke sh "npm pack", chdir: pkg_dir end @@ -201,7 +202,8 @@ namespace :standalone do desc "Build standalone package #{pkg[:name]}" task "#{pkg[:name]}" => ["build:#{pkg[:build]}"] do - wasi_sdk.install_binaryen + executor = RubyWasm::SilentExecutor.new + binaryen.install(executor) base_dir = Dir.pwd sh tools, "./build-package.sh #{base_dir}/rubies/ruby-#{pkg[:build]}", diff --git a/sig/ruby_wasm/build.rbs b/sig/ruby_wasm/build.rbs index 14e94a520..442b97f76 100644 --- a/sig/ruby_wasm/build.rbs +++ b/sig/ruby_wasm/build.rbs @@ -240,7 +240,7 @@ module RubyWasm def initialize: -> void def find_tool: (Symbol name) -> String def check_envvar: (untyped name) -> void - def self.get: (Target target, ?String? build_dir) -> (Toolchain) + def self.get: (Target target, Hash[untyped, untyped] config, ?String? build_dir) -> (Toolchain) def self.find_path: (String command) -> String? def self.check_executable: (String command) -> String def cc: -> String @@ -257,15 +257,14 @@ module RubyWasm @need_fetch_wasi_sdk: bool @tools: Hash[Symbol, String] @wasi_sdk_path: String - @version_major: Integer - @version_minor: Integer + @version: String @binaryen: Binaryen - def initialize: (?String? wasi_sdk_path, ?build_dir: String?, ?version_major: Integer, ?version_minor: Integer, ?binaryen_version: Integer) -> void + def initialize: (?String? wasi_sdk_path, ?build_dir: String?, ?version: String, ?binaryen_version: Integer) -> void def find_tool: (Symbol name) -> String def wasm_opt: -> String def wasi_sdk_path: -> String - def download_url: (Integer? version_major, Integer? version_minor) -> String + def download_url: () -> String def binaryen_download_url: (Integer? version) -> String def install_wasi_sdk: -> void def install_binaryen: -> void @@ -331,6 +330,8 @@ module RubyWasm def format_size: (Integer size) -> String def download: (String url, String dest, String message) -> void + def head: (String url) -> bool + private def _head: (URI::HTTPS uri, Integer limit) -> bool end class BuildTask From f45d018b59b224e2c124b0ffcbb92c346a947a70 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 27 Dec 2025 18:00:41 +0900 Subject: [PATCH 3/3] rake format --- Rakefile | 4 +- lib/ruby_wasm/build/executor.rb | 6 +- lib/ruby_wasm/build/toolchain.rb | 114 +++++++++++++++++++++---------- lib/ruby_wasm/cli.rb | 60 ++++++++-------- 4 files changed, 114 insertions(+), 70 deletions(-) diff --git a/Rakefile b/Rakefile index 858dacc19..4d7fe9bca 100644 --- a/Rakefile +++ b/Rakefile @@ -29,14 +29,14 @@ NPM_PACKAGES = [ name: "ruby-head-wasm-wasi", ruby_version: "head", gemfile: "packages/npm-packages/ruby-head-wasm-wasi/Gemfile", - target: "wasm32-unknown-wasip1", + target: "wasm32-unknown-wasip1" }, { name: "ruby-head-wasm-wasip2", ruby_version: "head", gemfile: "packages/npm-packages/ruby-head-wasm-wasip2/Gemfile", target: "wasm32-unknown-wasip2", - enable_component_model: true, + enable_component_model: true }, { name: "ruby-3.4-wasm-wasi", diff --git a/lib/ruby_wasm/build/executor.rb b/lib/ruby_wasm/build/executor.rb index 5df62cb40..97676ded5 100644 --- a/lib/ruby_wasm/build/executor.rb +++ b/lib/ruby_wasm/build/executor.rb @@ -152,11 +152,7 @@ def system(*args, chdir: nil, env: nil) kwargs = {} kwargs[:chdir] = chdir if chdir kwargs[:exception] = true - if env - Kernel.system(env, *args, **kwargs) - else - Kernel.system(*args, **kwargs) - end + env ? Kernel.system(env, *args, **kwargs) : Kernel.system(*args, **kwargs) end end diff --git a/lib/ruby_wasm/build/toolchain.rb b/lib/ruby_wasm/build/toolchain.rb index 4daf833f4..0f786e995 100644 --- a/lib/ruby_wasm/build/toolchain.rb +++ b/lib/ruby_wasm/build/toolchain.rb @@ -19,7 +19,12 @@ def check_envvar(name) def self.get(target, options, build_dir = nil) case target when /^wasm32-unknown-wasi/ - return RubyWasm::WASISDK.new(build_dir: build_dir, version: options[:wasi_sdk_version]) + return( + RubyWasm::WASISDK.new( + build_dir: build_dir, + version: options[:wasi_sdk_version] + ) + ) when "wasm32-unknown-emscripten" return RubyWasm::Emscripten.new else @@ -71,7 +76,8 @@ def initialize( @version = version end - @binaryen = Binaryen.new(build_dir: build_dir, binaryen_version: binaryen_version) + @binaryen = + Binaryen.new(build_dir: build_dir, binaryen_version: binaryen_version) @tools = { cc: "#{wasi_sdk_path}/bin/clang", @@ -103,44 +109,55 @@ def download_url major, _ = @version.split(".").map(&:to_i) # @type var assets: Array[[Regexp, Array[String]]] assets = [ - [/x86_64-linux/, [ - "wasi-sdk-#{@version}-x86_64-linux.tar.gz", - # For wasi-sdk version < 23.0 - "wasi-sdk-#{@version}-linux.tar.gz", - ]], - [/arm64e?-darwin/, [ - "wasi-sdk-#{@version}-arm64-macos.tar.gz", - # For wasi-sdk version < 23.0 - "wasi-sdk-#{@version}-macos.tar.gz", - ]], - [/x86_64-darwin/, [ - "wasi-sdk-#{@version}-x86_64-macos.tar.gz", - # For wasi-sdk version < 23.0 - "wasi-sdk-#{@version}-macos.tar.gz", - ]], + [ + /x86_64-linux/, + [ + "wasi-sdk-#{@version}-x86_64-linux.tar.gz", + # For wasi-sdk version < 23.0 + "wasi-sdk-#{@version}-linux.tar.gz" + ] + ], + [ + /arm64e?-darwin/, + [ + "wasi-sdk-#{@version}-arm64-macos.tar.gz", + # For wasi-sdk version < 23.0 + "wasi-sdk-#{@version}-macos.tar.gz" + ] + ], + [ + /x86_64-darwin/, + [ + "wasi-sdk-#{@version}-x86_64-macos.tar.gz", + # For wasi-sdk version < 23.0 + "wasi-sdk-#{@version}-macos.tar.gz" + ] + ] ] - asset = assets.find do |os, candidates| - os =~ RUBY_PLATFORM - end + asset = assets.find { |os, candidates| os =~ RUBY_PLATFORM } if asset.nil? raise "unsupported platform for fetching WASI SDK: #{RUBY_PLATFORM}" end _, candidates = asset - candidates_urls = candidates.map do |candidate| - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{major}/#{candidate}" - end + candidates_urls = + candidates.map do |candidate| + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{major}/#{candidate}" + end require "net/http" # Find an asset that exists by checking HEAD response to see if the asset exists candidates_urls.each do |url_str| # @type var url: URI::HTTPS url = URI.parse(url_str) - ok = Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http| - response = http.head(url.request_uri) - next response.code == "302" - end - if ok - return url_str - end + ok = + Net::HTTP.start( + url.host, + url.port, + use_ssl: url.scheme == "https" + ) do |http| + response = http.head(url.request_uri) + next response.code == "302" + end + return url_str if ok end raise "WASI SDK asset not found: #{candidates_urls.join(", ")}" end @@ -151,11 +168,21 @@ def install_wasi_sdk(executor) File.join(File.dirname(@wasi_sdk_path), "wasi-sdk-#{@version}.tar.gz") unless File.exist? wasi_sdk_tarball FileUtils.mkdir_p File.dirname(wasi_sdk_tarball) - executor.system "curl", "-fsSL", "-o", wasi_sdk_tarball, self.download_url + executor.system "curl", + "-fsSL", + "-o", + wasi_sdk_tarball, + self.download_url end unless File.exist? @wasi_sdk_path FileUtils.mkdir_p @wasi_sdk_path - executor.system "tar", "-C", @wasi_sdk_path, "--strip-component", "1", "-xzf", wasi_sdk_tarball + executor.system "tar", + "-C", + @wasi_sdk_path, + "--strip-component", + "1", + "-xzf", + wasi_sdk_tarball end end @@ -218,20 +245,35 @@ def install(executor) binaryen_tarball = File.expand_path("../binaryen.tar.gz", @binaryen_path) unless File.exist? binaryen_tarball FileUtils.mkdir_p File.dirname(binaryen_tarball) - executor.system "curl", "-L", "-o", binaryen_tarball, self.download_url(@binaryen_version) + executor.system "curl", + "-L", + "-o", + binaryen_tarball, + self.download_url(@binaryen_version) end unless File.exist? @binaryen_path FileUtils.mkdir_p @binaryen_path - executor.system "tar", "-C", @binaryen_path, "--strip-component", "1", "-xzf", binaryen_tarball + executor.system "tar", + "-C", + @binaryen_path, + "--strip-component", + "1", + "-xzf", + binaryen_tarball end end - end class Emscripten < Toolchain def initialize - @tools = { cc: "emcc", cxx: "em++", ld: "emcc", ar: "emar", ranlib: "emranlib" } + @tools = { + cc: "emcc", + cxx: "em++", + ld: "emcc", + ar: "emar", + ranlib: "emranlib" + } @name = "emscripten" end diff --git a/lib/ruby_wasm/cli.rb b/lib/ruby_wasm/cli.rb index 6fb577d69..790c281d5 100644 --- a/lib/ruby_wasm/cli.rb +++ b/lib/ruby_wasm/cli.rb @@ -214,15 +214,17 @@ def compute_build_alias(options) end # FIXME: We should have a way to specify extensions to be included by users. # For now, assume all default extensions available in the head revision are available. - return { - name: "local", - src: { - type: "local", - path: src_name, - patches: [] - }, - all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS, - } + return( + { + name: "local", + src: { + type: "local", + path: src_name, + patches: [] + }, + all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS + } + ) end # Otherwise, it's an unknown source. raise( @@ -243,52 +245,56 @@ def self.build_config_aliases(root) src: { type: "github", repo: "ruby/ruby", - rev: "master", + rev: "master" }, all_default_exts: RubyWasm::Packager::ALL_DEFAULT_EXTS, - wasi_sdk_version: "23.0", + wasi_sdk_version: "23.0" }, { name: "3.4", src: { type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.1.tar.gz", + url: "https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.1.tar.gz" }, - 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", - wasi_sdk_version: "22.0", + 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", + wasi_sdk_version: "22.0" }, { name: "3.3", src: { type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz", + url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz" }, - 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", - wasi_sdk_version: "22.0", + 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", + wasi_sdk_version: "22.0" }, { name: "3.2", src: { type: "tarball", - url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.4.tar.gz", + url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.4.tar.gz" }, - 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", - wasi_sdk_version: "22.0", + 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", + wasi_sdk_version: "22.0" } ] # Set the name in the source config. - aliases.each do |config| - config[:src][:name] = config[:name] - end + aliases.each { |config| config[:src][:name] = config[:name] } # Apply bundled and user-specified `/patches` directories. aliases.each do |config| patches_dirs = [bundled_patches_path, File.join(root, "patches")] - config[:src][:patches] = patches_dirs.flat_map do |patches_dir| - Dir[File.join(patches_dir, config[:name], "*.patch")] - .map { |p| File.expand_path(p) } - end.uniq + config[:src][:patches] = patches_dirs + .flat_map do |patches_dir| + Dir[File.join(patches_dir, config[:name], "*.patch")].map do |p| + File.expand_path(p) + end + end + .uniq end # Pin the revisions based on build_manifest.json if available.