ruby_wasm 2.5.0-x64-mingw-ucrt
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.clang-format +8 -0
- data/CONTRIBUTING.md +126 -0
- data/Gemfile +17 -0
- data/LICENSE +21 -0
- data/NOTICE +1293 -0
- data/README.md +153 -0
- data/Rakefile +163 -0
- data/Steepfile +24 -0
- data/benchmarks/vm_deep_call.rb +55 -0
- data/builders/wasm32-unknown-emscripten/Dockerfile +43 -0
- data/builders/wasm32-unknown-emscripten/entrypoint.sh +7 -0
- data/builders/wasm32-unknown-wasi/Dockerfile +47 -0
- data/builders/wasm32-unknown-wasi/entrypoint.sh +7 -0
- data/docs/api.md +2 -0
- data/docs/cheat_sheet.md +195 -0
- data/docs/faq.md +25 -0
- data/exe/rbwasm +7 -0
- data/ext/.gitignore +2 -0
- data/ext/README.md +11 -0
- data/ext/extinit.c.erb +32 -0
- data/lib/ruby_wasm/3.1/ruby_wasm.so +0 -0
- data/lib/ruby_wasm/3.2/ruby_wasm.so +0 -0
- data/lib/ruby_wasm/build/build_params.rb +3 -0
- data/lib/ruby_wasm/build/downloader.rb +18 -0
- data/lib/ruby_wasm/build/executor.rb +187 -0
- data/lib/ruby_wasm/build/product/baseruby.rb +37 -0
- data/lib/ruby_wasm/build/product/crossruby.rb +330 -0
- data/lib/ruby_wasm/build/product/libyaml.rb +68 -0
- data/lib/ruby_wasm/build/product/openssl.rb +88 -0
- data/lib/ruby_wasm/build/product/product.rb +39 -0
- data/lib/ruby_wasm/build/product/ruby_source.rb +103 -0
- data/lib/ruby_wasm/build/product/wasi_vfs.rb +45 -0
- data/lib/ruby_wasm/build/product/zlib.rb +68 -0
- data/lib/ruby_wasm/build/product.rb +8 -0
- data/lib/ruby_wasm/build/toolchain/wit_bindgen.rb +31 -0
- data/lib/ruby_wasm/build/toolchain.rb +193 -0
- data/lib/ruby_wasm/build.rb +88 -0
- data/lib/ruby_wasm/cli.rb +217 -0
- data/lib/ruby_wasm/packager/core.rb +156 -0
- data/lib/ruby_wasm/packager/file_system.rb +158 -0
- data/lib/ruby_wasm/packager.rb +159 -0
- data/lib/ruby_wasm/rake_task.rb +59 -0
- data/lib/ruby_wasm/util.rb +15 -0
- data/lib/ruby_wasm/version.rb +3 -0
- data/lib/ruby_wasm.rb +33 -0
- data/package-lock.json +9500 -0
- data/package.json +12 -0
- data/rakelib/check.rake +37 -0
- data/rakelib/ci.rake +152 -0
- data/rakelib/doc.rake +29 -0
- data/rakelib/format.rake +35 -0
- data/rakelib/gem.rake +22 -0
- data/rakelib/packaging.rake +151 -0
- data/rakelib/version.rake +40 -0
- data/sig/open_uri.rbs +4 -0
- data/sig/ruby_wasm/build.rbs +318 -0
- data/sig/ruby_wasm/cli.rbs +27 -0
- data/sig/ruby_wasm/ext.rbs +13 -0
- data/sig/ruby_wasm/packager.rbs +91 -0
- data/sig/ruby_wasm/util.rbs +5 -0
- data/tools/clang-format-diff.sh +18 -0
- data/tools/exe/rbminify +12 -0
- data/tools/lib/syntax_tree/minify_ruby.rb +63 -0
- metadata +113 -0
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            require_relative "./product"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RubyWasm
         | 
| 4 | 
            +
              class ZlibProduct < AutoconfProduct
         | 
| 5 | 
            +
                attr_reader :target
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                ZLIB_VERSION = "1.3.1"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(build_dir, target, toolchain)
         | 
| 10 | 
            +
                  @build_dir = build_dir
         | 
| 11 | 
            +
                  @target = target
         | 
| 12 | 
            +
                  super(target, toolchain)
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def product_build_dir
         | 
| 16 | 
            +
                  File.join(@build_dir, target, "zlib-#{ZLIB_VERSION}")
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def destdir
         | 
| 20 | 
            +
                  File.join(product_build_dir, "opt")
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def install_root
         | 
| 24 | 
            +
                  File.join(destdir, "usr", "local")
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def name
         | 
| 28 | 
            +
                  "zlib-#{ZLIB_VERSION}-#{target}"
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def configure_args
         | 
| 32 | 
            +
                  args = %w[CHOST=linux]
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  args + tools_args
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def build(executor)
         | 
| 38 | 
            +
                  return if Dir.exist?(install_root)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  executor.mkdir_p File.dirname(product_build_dir)
         | 
| 41 | 
            +
                  executor.rm_rf product_build_dir
         | 
| 42 | 
            +
                  executor.mkdir_p product_build_dir
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  tarball_path = File.join(product_build_dir, "zlib-#{ZLIB_VERSION}.tar.gz")
         | 
| 45 | 
            +
                  executor.system "curl",
         | 
| 46 | 
            +
                                  "-o",
         | 
| 47 | 
            +
                                  tarball_path,
         | 
| 48 | 
            +
                                  "-L",
         | 
| 49 | 
            +
                                  "https://github.com/madler/zlib/releases/download/v#{ZLIB_VERSION}/zlib-#{ZLIB_VERSION}.tar.gz"
         | 
| 50 | 
            +
                  executor.system "tar",
         | 
| 51 | 
            +
                                  "xzf",
         | 
| 52 | 
            +
                                  tarball_path,
         | 
| 53 | 
            +
                                  "-C",
         | 
| 54 | 
            +
                                  product_build_dir,
         | 
| 55 | 
            +
                                  "--strip-components=1"
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  executor.system "env",
         | 
| 58 | 
            +
                                  *configure_args,
         | 
| 59 | 
            +
                                  "./configure",
         | 
| 60 | 
            +
                                  "--static",
         | 
| 61 | 
            +
                                  chdir: product_build_dir
         | 
| 62 | 
            +
                  executor.system "make",
         | 
| 63 | 
            +
                                  "install",
         | 
| 64 | 
            +
                                  "DESTDIR=#{destdir}",
         | 
| 65 | 
            +
                                  chdir: product_build_dir
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
| @@ -0,0 +1,8 @@ | |
| 1 | 
            +
            require_relative "product/product"
         | 
| 2 | 
            +
            require_relative "product/ruby_source"
         | 
| 3 | 
            +
            require_relative "product/baseruby"
         | 
| 4 | 
            +
            require_relative "product/zlib"
         | 
| 5 | 
            +
            require_relative "product/libyaml"
         | 
| 6 | 
            +
            require_relative "product/openssl"
         | 
| 7 | 
            +
            require_relative "product/wasi_vfs"
         | 
| 8 | 
            +
            require_relative "product/crossruby"
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            module RubyWasm
         | 
| 2 | 
            +
              class WitBindgen
         | 
| 3 | 
            +
                attr_reader :bin_path
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(
         | 
| 6 | 
            +
                  build_dir:,
         | 
| 7 | 
            +
                  revision: "251e84b89121751f79ac268629e9285082b2596d"
         | 
| 8 | 
            +
                )
         | 
| 9 | 
            +
                  @build_dir = build_dir
         | 
| 10 | 
            +
                  @tool_dir = File.join(@build_dir, "toolchain", "wit-bindgen")
         | 
| 11 | 
            +
                  @bin_path = File.join(@tool_dir, "bin", "wit-bindgen")
         | 
| 12 | 
            +
                  @revision = revision
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def install
         | 
| 16 | 
            +
                  return if File.exist?(@bin_path)
         | 
| 17 | 
            +
                  RubyWasm::Toolchain.check_executable("cargo")
         | 
| 18 | 
            +
                  Kernel.system(
         | 
| 19 | 
            +
                    "cargo",
         | 
| 20 | 
            +
                    "install",
         | 
| 21 | 
            +
                    "--git",
         | 
| 22 | 
            +
                    "https://github.com/bytecodealliance/wit-bindgen",
         | 
| 23 | 
            +
                    "--rev",
         | 
| 24 | 
            +
                    @revision,
         | 
| 25 | 
            +
                    "--root",
         | 
| 26 | 
            +
                    @tool_dir,
         | 
| 27 | 
            +
                    "wit-bindgen-cli"
         | 
| 28 | 
            +
                  )
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,193 @@ | |
| 1 | 
            +
            require_relative "./toolchain/wit_bindgen"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RubyWasm
         | 
| 4 | 
            +
              class Toolchain
         | 
| 5 | 
            +
                attr_reader :name
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize
         | 
| 8 | 
            +
                  @tools = {}
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def find_tool(name)
         | 
| 12 | 
            +
                  raise "not implemented"
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def check_envvar(name)
         | 
| 16 | 
            +
                  raise "missing environment variable: #{name}" if ENV[name].nil?
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def self.get(target, build_dir = nil)
         | 
| 20 | 
            +
                  case target
         | 
| 21 | 
            +
                  when "wasm32-unknown-wasi"
         | 
| 22 | 
            +
                    return RubyWasm::WASISDK.new(build_dir: build_dir)
         | 
| 23 | 
            +
                  when "wasm32-unknown-emscripten"
         | 
| 24 | 
            +
                    return RubyWasm::Emscripten.new
         | 
| 25 | 
            +
                  else
         | 
| 26 | 
            +
                    raise "unknown target: #{target}"
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.find_path(command)
         | 
| 31 | 
            +
                  (ENV["PATH"] || "")
         | 
| 32 | 
            +
                    .split(File::PATH_SEPARATOR)
         | 
| 33 | 
            +
                    .each do |path_dir|
         | 
| 34 | 
            +
                      bin_path = File.join(path_dir, command)
         | 
| 35 | 
            +
                      return bin_path if File.executable?(bin_path)
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  nil
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def self.check_executable(command)
         | 
| 41 | 
            +
                  tool = find_path(command)
         | 
| 42 | 
            +
                  raise "missing executable: #{command}" unless tool
         | 
| 43 | 
            +
                  tool
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                %i[cc ranlib ld ar].each do |name|
         | 
| 47 | 
            +
                  define_method(name) do
         | 
| 48 | 
            +
                    @tools_cache ||= {}
         | 
| 49 | 
            +
                    @tools_cache[name] ||= find_tool(name)
         | 
| 50 | 
            +
                    @tools_cache[name]
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              class WASISDK < Toolchain
         | 
| 56 | 
            +
                def initialize(
         | 
| 57 | 
            +
                  wasi_sdk_path = ENV["WASI_SDK_PATH"],
         | 
| 58 | 
            +
                  build_dir: nil,
         | 
| 59 | 
            +
                  version_major: 20,
         | 
| 60 | 
            +
                  version_minor: 0,
         | 
| 61 | 
            +
                  binaryen_version: 108
         | 
| 62 | 
            +
                )
         | 
| 63 | 
            +
                  @wasm_opt_path = Toolchain.find_path("wasm-opt")
         | 
| 64 | 
            +
                  @need_fetch_wasi_sdk = wasi_sdk_path.nil?
         | 
| 65 | 
            +
                  @need_fetch_binaryen = @wasm_opt_path.nil?
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  if @need_fetch_wasi_sdk
         | 
| 68 | 
            +
                    if build_dir.nil?
         | 
| 69 | 
            +
                      raise "build_dir is required when WASI_SDK_PATH is not set"
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
                    wasi_sdk_path = File.join(build_dir, "toolchain", "wasi-sdk")
         | 
| 72 | 
            +
                    @version_major = version_major
         | 
| 73 | 
            +
                    @version_minor = version_minor
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  if @need_fetch_binaryen
         | 
| 77 | 
            +
                    if build_dir.nil?
         | 
| 78 | 
            +
                      raise "build_dir is required when wasm-opt not installed in PATH"
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                    @binaryen_path = File.join(build_dir, "toolchain", "binaryen")
         | 
| 81 | 
            +
                    @binaryen_version = binaryen_version
         | 
| 82 | 
            +
                    @wasm_opt_path = File.join(@binaryen_path, "bin", "wasm-opt")
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  @tools = {
         | 
| 86 | 
            +
                    cc: "#{wasi_sdk_path}/bin/clang",
         | 
| 87 | 
            +
                    ld: "#{wasi_sdk_path}/bin/clang",
         | 
| 88 | 
            +
                    ar: "#{wasi_sdk_path}/bin/llvm-ar",
         | 
| 89 | 
            +
                    ranlib: "#{wasi_sdk_path}/bin/llvm-ranlib"
         | 
| 90 | 
            +
                  }
         | 
| 91 | 
            +
                  @wasi_sdk_path = wasi_sdk_path
         | 
| 92 | 
            +
                  @name = "wasi-sdk"
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def find_tool(name)
         | 
| 96 | 
            +
                  if !File.exist?(@tools[name]) && !ENV["WASI_SDK_PATH"].nil?
         | 
| 97 | 
            +
                    raise "missing tool '#{name}' at #{@tools[name]}"
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                  @tools[name]
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                def wasm_opt
         | 
| 103 | 
            +
                  @wasm_opt_path
         | 
| 104 | 
            +
                end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                def wasi_sdk_path
         | 
| 107 | 
            +
                  @wasi_sdk_path
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                def download_url(version_major, version_minor)
         | 
| 111 | 
            +
                  version = "#{version_major}.#{version_minor}"
         | 
| 112 | 
            +
                  assets = [
         | 
| 113 | 
            +
                    [/x86_64-linux/, "wasi-sdk-#{version}-linux.tar.gz"],
         | 
| 114 | 
            +
                    [/(arm64e?|x86_64)-darwin/, "wasi-sdk-#{version}-macos.tar.gz"]
         | 
| 115 | 
            +
                  ]
         | 
| 116 | 
            +
                  asset = assets.find { |os, _| os =~ RUBY_PLATFORM }&.at(1)
         | 
| 117 | 
            +
                  if asset.nil?
         | 
| 118 | 
            +
                    raise "unsupported platform for fetching WASI SDK: #{RUBY_PLATFORM}"
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
                  "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-#{version_major}/#{asset}"
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                def binaryen_download_url(version)
         | 
| 124 | 
            +
                  assets = [
         | 
| 125 | 
            +
                    [
         | 
| 126 | 
            +
                      /x86_64-linux/,
         | 
| 127 | 
            +
                      "binaryen-version_#{@binaryen_version}-x86_64-linux.tar.gz"
         | 
| 128 | 
            +
                    ],
         | 
| 129 | 
            +
                    [
         | 
| 130 | 
            +
                      /x86_64-darwin/,
         | 
| 131 | 
            +
                      "binaryen-version_#{@binaryen_version}-x86_64-macos.tar.gz"
         | 
| 132 | 
            +
                    ],
         | 
| 133 | 
            +
                    [
         | 
| 134 | 
            +
                      /arm64e?-darwin/,
         | 
| 135 | 
            +
                      "binaryen-version_#{@binaryen_version}-arm64-macos.tar.gz"
         | 
| 136 | 
            +
                    ]
         | 
| 137 | 
            +
                  ]
         | 
| 138 | 
            +
                  asset = assets.find { |os, _| os =~ RUBY_PLATFORM }&.at(1)
         | 
| 139 | 
            +
                  if asset.nil?
         | 
| 140 | 
            +
                    raise "unsupported platform for fetching Binaryen: #{RUBY_PLATFORM}"
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
                  "https://github.com/WebAssembly/binaryen/releases/download/version_#{@binaryen_version}/#{asset}"
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                def install_wasi_sdk
         | 
| 146 | 
            +
                  return unless @need_fetch_wasi_sdk
         | 
| 147 | 
            +
                  wasi_sdk_tarball =
         | 
| 148 | 
            +
                    File.join(File.dirname(@wasi_sdk_path), "wasi-sdk.tar.gz")
         | 
| 149 | 
            +
                  unless File.exist? wasi_sdk_tarball
         | 
| 150 | 
            +
                    FileUtils.mkdir_p File.dirname(wasi_sdk_tarball)
         | 
| 151 | 
            +
                    system "curl -L -o #{wasi_sdk_tarball} #{self.download_url(@version_major, @version_minor)}"
         | 
| 152 | 
            +
                  end
         | 
| 153 | 
            +
                  unless File.exist? @wasi_sdk_path
         | 
| 154 | 
            +
                    FileUtils.mkdir_p @wasi_sdk_path
         | 
| 155 | 
            +
                    system "tar -C #{@wasi_sdk_path} --strip-component 1 -xzf #{wasi_sdk_tarball}"
         | 
| 156 | 
            +
                  end
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                def install_binaryen
         | 
| 160 | 
            +
                  return unless @need_fetch_binaryen
         | 
| 161 | 
            +
                  binaryen_tarball = File.expand_path("../binaryen.tar.gz", @binaryen_path)
         | 
| 162 | 
            +
                  unless File.exist? binaryen_tarball
         | 
| 163 | 
            +
                    FileUtils.mkdir_p File.dirname(binaryen_tarball)
         | 
| 164 | 
            +
                    system "curl -L -o #{binaryen_tarball} #{self.binaryen_download_url(@binaryen_version)}"
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  unless File.exist? @binaryen_path
         | 
| 168 | 
            +
                    FileUtils.mkdir_p @binaryen_path
         | 
| 169 | 
            +
                    system "tar -C #{@binaryen_path} --strip-component 1 -xzf #{binaryen_tarball}"
         | 
| 170 | 
            +
                  end
         | 
| 171 | 
            +
                end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                def install
         | 
| 174 | 
            +
                  install_wasi_sdk
         | 
| 175 | 
            +
                  install_binaryen
         | 
| 176 | 
            +
                end
         | 
| 177 | 
            +
              end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
              class Emscripten < Toolchain
         | 
| 180 | 
            +
                def initialize
         | 
| 181 | 
            +
                  @tools = { cc: "emcc", ld: "emcc", ar: "emar", ranlib: "emranlib" }
         | 
| 182 | 
            +
                  @name = "emscripten"
         | 
| 183 | 
            +
                end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                def install
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                def find_tool(name)
         | 
| 189 | 
            +
                  Toolchain.check_executable(@tools[name])
         | 
| 190 | 
            +
                  @tools[name]
         | 
| 191 | 
            +
                end
         | 
| 192 | 
            +
              end
         | 
| 193 | 
            +
            end
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            require_relative "build/build_params"
         | 
| 2 | 
            +
            require_relative "build/product"
         | 
| 3 | 
            +
            require_relative "build/toolchain"
         | 
| 4 | 
            +
            require_relative "build/executor"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            class RubyWasm::Build
         | 
| 7 | 
            +
              # Source to build from.
         | 
| 8 | 
            +
              attr_reader :source
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              # Target to build for.
         | 
| 11 | 
            +
              attr_reader :target
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              # Toolchain for the build.
         | 
| 14 | 
            +
              # Defaults to the Toolchain.get for the target.
         | 
| 15 | 
            +
              attr_reader :toolchain
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              # LibYAML product to build.
         | 
| 18 | 
            +
              attr_reader :libyaml
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              # zlib product to build.
         | 
| 21 | 
            +
              attr_reader :zlib
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              # wasi-vfs product used by the crossruby.
         | 
| 24 | 
            +
              attr_reader :wasi_vfs
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              # BaseRuby product to build.
         | 
| 27 | 
            +
              attr_reader :baseruby
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              # CrossRuby product to build.
         | 
| 30 | 
            +
              attr_reader :crossruby
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def initialize(
         | 
| 33 | 
            +
                name,
         | 
| 34 | 
            +
                target:,
         | 
| 35 | 
            +
                src:,
         | 
| 36 | 
            +
                toolchain:,
         | 
| 37 | 
            +
                build_dir:,
         | 
| 38 | 
            +
                rubies_dir:,
         | 
| 39 | 
            +
                **options
         | 
| 40 | 
            +
              )
         | 
| 41 | 
            +
                @target = target
         | 
| 42 | 
            +
                @build_dir = build_dir
         | 
| 43 | 
            +
                @rubies_dir = rubies_dir
         | 
| 44 | 
            +
                @toolchain = (toolchain || RubyWasm::Toolchain.get(target, @build_dir))
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                @libyaml = RubyWasm::LibYAMLProduct.new(@build_dir, @target, @toolchain)
         | 
| 47 | 
            +
                @zlib = RubyWasm::ZlibProduct.new(@build_dir, @target, @toolchain)
         | 
| 48 | 
            +
                @wasi_vfs = RubyWasm::WasiVfsProduct.new(@build_dir)
         | 
| 49 | 
            +
                @source = RubyWasm::BuildSource.new(src, @build_dir)
         | 
| 50 | 
            +
                @baseruby = RubyWasm::BaseRubyProduct.new(@build_dir, @source)
         | 
| 51 | 
            +
                @openssl = RubyWasm::OpenSSLProduct.new(@build_dir, @target, @toolchain)
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                build_params =
         | 
| 54 | 
            +
                  RubyWasm::BuildParams.new(
         | 
| 55 | 
            +
                    name: name,
         | 
| 56 | 
            +
                    target: target,
         | 
| 57 | 
            +
                    default_exts: options[:default_exts]
         | 
| 58 | 
            +
                  )
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                @crossruby =
         | 
| 61 | 
            +
                  RubyWasm::CrossRubyProduct.new(
         | 
| 62 | 
            +
                    build_params,
         | 
| 63 | 
            +
                    @build_dir,
         | 
| 64 | 
            +
                    @rubies_dir,
         | 
| 65 | 
            +
                    @baseruby,
         | 
| 66 | 
            +
                    @source,
         | 
| 67 | 
            +
                    @toolchain
         | 
| 68 | 
            +
                  )
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                @crossruby.with_libyaml @libyaml
         | 
| 71 | 
            +
                @crossruby.with_zlib @zlib
         | 
| 72 | 
            +
                @crossruby.with_wasi_vfs @wasi_vfs
         | 
| 73 | 
            +
                @crossruby.with_openssl @openssl
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def cache_key(digest)
         | 
| 77 | 
            +
                @source.cache_key(digest)
         | 
| 78 | 
            +
                @crossruby.cache_key(digest)
         | 
| 79 | 
            +
                digest << @build_dir
         | 
| 80 | 
            +
                digest << @rubies_dir
         | 
| 81 | 
            +
                digest << @target
         | 
| 82 | 
            +
                digest << @toolchain.name
         | 
| 83 | 
            +
                digest << @libyaml.name
         | 
| 84 | 
            +
                digest << @zlib.name
         | 
| 85 | 
            +
                digest << @openssl.name
         | 
| 86 | 
            +
                digest << @wasi_vfs.name
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
            end
         | 
| @@ -0,0 +1,217 @@ | |
| 1 | 
            +
            require "optparse"
         | 
| 2 | 
            +
            require "rbconfig"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module RubyWasm
         | 
| 5 | 
            +
              class CLI
         | 
| 6 | 
            +
                def initialize(stdout:, stderr:)
         | 
| 7 | 
            +
                  @stdout = stdout
         | 
| 8 | 
            +
                  @stderr = stderr
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def run(args)
         | 
| 12 | 
            +
                  available_commands = %w[build pack]
         | 
| 13 | 
            +
                  parser =
         | 
| 14 | 
            +
                    OptionParser.new do |opts|
         | 
| 15 | 
            +
                      opts.banner = <<~USAGE
         | 
| 16 | 
            +
                      Usage: rbwasm [options...] [command]
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      Available commands: #{available_commands.join(", ")}
         | 
| 19 | 
            +
                    USAGE
         | 
| 20 | 
            +
                      opts.version = RubyWasm::VERSION
         | 
| 21 | 
            +
                      opts.on("-h", "--help", "Prints this help") do
         | 
| 22 | 
            +
                        @stderr.puts opts
         | 
| 23 | 
            +
                        exit
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
                      opts.on("--log-level LEVEL", "Log level") do |level|
         | 
| 26 | 
            +
                        RubyWasm.log_level = level.to_sym
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  parser.order!(args)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  command = args.shift
         | 
| 32 | 
            +
                  case command
         | 
| 33 | 
            +
                  when "build"
         | 
| 34 | 
            +
                    build(args)
         | 
| 35 | 
            +
                  when "pack"
         | 
| 36 | 
            +
                    pack(args)
         | 
| 37 | 
            +
                  else
         | 
| 38 | 
            +
                    @stderr.puts "Unknown command: #{command}"
         | 
| 39 | 
            +
                    @stderr.puts parser
         | 
| 40 | 
            +
                    exit
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def build(args)
         | 
| 45 | 
            +
                  # @type var options: Hash[Symbol, untyped]
         | 
| 46 | 
            +
                  options = {
         | 
| 47 | 
            +
                    save_temps: false,
         | 
| 48 | 
            +
                    optimize: false,
         | 
| 49 | 
            +
                    remake: false,
         | 
| 50 | 
            +
                    reconfigure: false,
         | 
| 51 | 
            +
                    clean: false,
         | 
| 52 | 
            +
                    ruby_version: "3.3",
         | 
| 53 | 
            +
                    target_triplet: "wasm32-unknown-wasi",
         | 
| 54 | 
            +
                    profile: "full",
         | 
| 55 | 
            +
                    stdlib: true,
         | 
| 56 | 
            +
                    disable_gems: false
         | 
| 57 | 
            +
                  }
         | 
| 58 | 
            +
                  OptionParser
         | 
| 59 | 
            +
                    .new do |opts|
         | 
| 60 | 
            +
                      opts.banner = "Usage: rbwasm componentize [options]"
         | 
| 61 | 
            +
                      opts.on("-h", "--help", "Prints this help") do
         | 
| 62 | 
            +
                        @stdout.puts opts
         | 
| 63 | 
            +
                        exit
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      opts.on("--save-temps", "Save temporary files") do
         | 
| 67 | 
            +
                        options[:save_temps] = true
         | 
| 68 | 
            +
                      end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      opts.on("--ruby-version VERSION", "Ruby version") do |version|
         | 
| 71 | 
            +
                        options[:ruby_version] = version
         | 
| 72 | 
            +
                      end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                      opts.on("--target TRIPLET", "Target triplet") do |triplet|
         | 
| 75 | 
            +
                        options[:target_triplet] = triplet
         | 
| 76 | 
            +
                      end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                      opts.on(
         | 
| 79 | 
            +
                        "--build-profile PROFILE",
         | 
| 80 | 
            +
                        "Build profile. full or minimal"
         | 
| 81 | 
            +
                      ) { |profile| options[:profile] = profile }
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                      opts.on("--optimize", "Optimize the output") do
         | 
| 84 | 
            +
                        options[:optimize] = true
         | 
| 85 | 
            +
                      end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                      opts.on("--remake", "Re-execute make for Ruby") do
         | 
| 88 | 
            +
                        options[:remake] = true
         | 
| 89 | 
            +
                      end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                      opts.on("--reconfigure", "Re-execute configure for Ruby") do
         | 
| 92 | 
            +
                        options[:reconfigure] = true
         | 
| 93 | 
            +
                      end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                      opts.on("--clean", "Clean build artifacts") { options[:clean] = true }
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                      opts.on("-o", "--output FILE", "Output file") do |file|
         | 
| 98 | 
            +
                        options[:output] = file
         | 
| 99 | 
            +
                      end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                      opts.on("--[no-]stdlib", "Include stdlib") do |stdlib|
         | 
| 102 | 
            +
                        options[:stdlib] = stdlib
         | 
| 103 | 
            +
                      end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                      opts.on("--disable-gems", "Disable gems") do
         | 
| 106 | 
            +
                        options[:disable_gems] = true
         | 
| 107 | 
            +
                      end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                      opts.on("--format FORMAT", "Output format") do |format|
         | 
| 110 | 
            +
                        options[:format] = format
         | 
| 111 | 
            +
                      end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                      opts.on("--print-ruby-cache-key", "Print Ruby cache key") do
         | 
| 114 | 
            +
                        options[:print_ruby_cache_key] = true
         | 
| 115 | 
            +
                      end
         | 
| 116 | 
            +
                    end
         | 
| 117 | 
            +
                    .parse!(args)
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  verbose = RubyWasm.logger.level == :debug
         | 
| 120 | 
            +
                  executor = RubyWasm::BuildExecutor.new(verbose: verbose)
         | 
| 121 | 
            +
                  packager = self.derive_packager(options)
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  if options[:print_ruby_cache_key]
         | 
| 124 | 
            +
                    self.do_print_ruby_cache_key(packager)
         | 
| 125 | 
            +
                    exit
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  unless options[:output]
         | 
| 129 | 
            +
                    @stderr.puts "Output file is not specified"
         | 
| 130 | 
            +
                    exit 1
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  require "tmpdir"
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                  if options[:save_temps]
         | 
| 136 | 
            +
                    tmpdir = Dir.mktmpdir
         | 
| 137 | 
            +
                    self.do_build(executor, tmpdir, packager, options)
         | 
| 138 | 
            +
                    @stderr.puts "Temporary files are saved to #{tmpdir}"
         | 
| 139 | 
            +
                    exit
         | 
| 140 | 
            +
                  else
         | 
| 141 | 
            +
                    Dir.mktmpdir do |tmpdir|
         | 
| 142 | 
            +
                      self.do_build(executor, tmpdir, packager, options)
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
                  end
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                def pack(args)
         | 
| 148 | 
            +
                  self.require_extension
         | 
| 149 | 
            +
                  RubyWasmExt::WasiVfs.run_cli([$0, "pack", *args])
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                private
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def build_config(options)
         | 
| 155 | 
            +
                  config = { target: options[:target_triplet], src: options[:ruby_version] }
         | 
| 156 | 
            +
                  case options[:profile]
         | 
| 157 | 
            +
                  when "full"
         | 
| 158 | 
            +
                    config[:default_exts] = RubyWasm::Packager::ALL_DEFAULT_EXTS
         | 
| 159 | 
            +
                    env_additional_exts = ENV["RUBY_WASM_ADDITIONAL_EXTS"] || ""
         | 
| 160 | 
            +
                    unless env_additional_exts.empty?
         | 
| 161 | 
            +
                      config[:default_exts] += "," + env_additional_exts
         | 
| 162 | 
            +
                    end
         | 
| 163 | 
            +
                  when "minimal"
         | 
| 164 | 
            +
                    config[:default_exts] = ""
         | 
| 165 | 
            +
                  else
         | 
| 166 | 
            +
                    RubyWasm.logger.error "Unknown profile: #{options[:profile]} (available: full, minimal)"
         | 
| 167 | 
            +
                    exit 1
         | 
| 168 | 
            +
                  end
         | 
| 169 | 
            +
                  config[:suffix] = "-#{options[:profile]}"
         | 
| 170 | 
            +
                  config
         | 
| 171 | 
            +
                end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                def derive_packager(options)
         | 
| 174 | 
            +
                  __skip__ =
         | 
| 175 | 
            +
                    if defined?(Bundler) && !options[:disable_gems]
         | 
| 176 | 
            +
                      definition = Bundler.definition
         | 
| 177 | 
            +
                    end
         | 
| 178 | 
            +
                  RubyWasm::Packager.new(build_config(options), definition)
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                def do_print_ruby_cache_key(packager)
         | 
| 182 | 
            +
                  ruby_core_build = packager.ruby_core_build
         | 
| 183 | 
            +
                  require "digest"
         | 
| 184 | 
            +
                  digest = Digest::SHA256.new
         | 
| 185 | 
            +
                  ruby_core_build.cache_key(digest)
         | 
| 186 | 
            +
                  hexdigest = digest.hexdigest
         | 
| 187 | 
            +
                  require "json"
         | 
| 188 | 
            +
                  @stdout.puts JSON.generate(
         | 
| 189 | 
            +
                                 hexdigest: hexdigest,
         | 
| 190 | 
            +
                                 artifact: ruby_core_build.artifact
         | 
| 191 | 
            +
                               )
         | 
| 192 | 
            +
                end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                def do_build(executor, tmpdir, packager, options)
         | 
| 195 | 
            +
                  self.require_extension
         | 
| 196 | 
            +
                  wasm_bytes = packager.package(executor, tmpdir, options)
         | 
| 197 | 
            +
                  RubyWasm.logger.info "Size: #{SizeFormatter.format(wasm_bytes.size)}"
         | 
| 198 | 
            +
                  case options[:output]
         | 
| 199 | 
            +
                  when "-"
         | 
| 200 | 
            +
                    @stdout.write wasm_bytes.pack("C*")
         | 
| 201 | 
            +
                  else
         | 
| 202 | 
            +
                    File.binwrite(options[:output], wasm_bytes.pack("C*"))
         | 
| 203 | 
            +
                    RubyWasm.logger.debug "Wrote #{options[:output]}"
         | 
| 204 | 
            +
                  end
         | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                def require_extension
         | 
| 208 | 
            +
                  # Tries to require the extension for the given Ruby version first
         | 
| 209 | 
            +
                  begin
         | 
| 210 | 
            +
                    RUBY_VERSION =~ /(\d+\.\d+)/
         | 
| 211 | 
            +
                    require_relative "#{Regexp.last_match(1)}/ruby_wasm.so"
         | 
| 212 | 
            +
                  rescue LoadError
         | 
| 213 | 
            +
                    require_relative "ruby_wasm.so"
         | 
| 214 | 
            +
                  end
         | 
| 215 | 
            +
                end
         | 
| 216 | 
            +
              end
         | 
| 217 | 
            +
            end
         |