Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ target :lib do
library "logger"
library "pathname"
library "forwardable"
library "net/http"

configure_code_diagnostics(D::Ruby.default)
end
2 changes: 1 addition & 1 deletion lib/ruby_wasm/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions lib/ruby_wasm/build/executor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby_wasm/build/product/crossruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
194 changes: 144 additions & 50 deletions lib/ruby_wasm/build/toolchain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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++",
Expand All @@ -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/,
Expand All @@ -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)
Expand Down
Loading
Loading