diff --git a/Rakefile b/Rakefile index a90a6aecd..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", @@ -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/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..97676ded5 100644 --- a/lib/ruby_wasm/build/executor.rb +++ b/lib/ruby_wasm/build/executor.rb @@ -147,6 +147,15 @@ 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 + env ? Kernel.system(env, *args, **kwargs) : Kernel.system(*args, **kwargs) + 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 5c1043223..0f786e995 100644 --- a/lib/ruby_wasm/build/toolchain.rb +++ b/lib/ruby_wasm/build/toolchain.rb @@ -16,10 +16,15 @@ 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,32 +61,24 @@ 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 ) - @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" end - wasi_sdk_path = File.join(build_dir, "toolchain", "wasi-sdk") - @version_major = version_major - @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" + 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 - @binaryen_path = File.join(build_dir, "toolchain", "binaryen") - @binaryen_version = binaryen_version - @wasm_opt_path = File.join(@binaryen_path, "bin", "wasm-opt") + @version = version end + @binaryen = + Binaryen.new(build_dir: build_dir, binaryen_version: binaryen_version) + @tools = { cc: "#{wasi_sdk_path}/bin/clang", cxx: "#{wasi_sdk_path}/bin/clang++", @@ -101,27 +98,127 @@ def find_tool(name) end def wasm_opt - @wasm_opt_path + @binaryen.wasm_opt end 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 { |os, candidates| os =~ RUBY_PLATFORM } 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 + return url_str if ok + end + raise "WASI SDK asset not found: #{candidates_urls.join(", ")}" + end + + def install_wasi_sdk(executor) + return unless @need_fetch_wasi_sdk + wasi_sdk_tarball = + 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 + 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 + end + end + + def install(executor) + install_wasi_sdk(executor) + @binaryen.install(executor) + 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_download_url(version) + def binaryen_path + @binaryen_path + end + + def binaryen_version + @binaryen_version + end + + def download_url(version) assets = [ [ /x86_64-linux/, @@ -143,47 +240,44 @@ 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(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.binaryen_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 - - def install - install_wasi_sdk - install_binaryen - 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 - 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..790c281d5 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,30 @@ 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 +232,78 @@ 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", - 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", + { + 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", - 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", + { + 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", - 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", + { + 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 { |config| config[:src][:name] = config[:name] } # 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")] - .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. 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 +312,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 1f2da10d6..e5259017c 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) @@ -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 47a381c67..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 @@ -255,24 +255,35 @@ 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 + @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 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] @@ -319,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