ruby_wasm 2.5.0-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.clang-format +8 -0
  3. data/CONTRIBUTING.md +126 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE +21 -0
  6. data/NOTICE +1293 -0
  7. data/README.md +153 -0
  8. data/Rakefile +163 -0
  9. data/Steepfile +24 -0
  10. data/benchmarks/vm_deep_call.rb +55 -0
  11. data/builders/wasm32-unknown-emscripten/Dockerfile +43 -0
  12. data/builders/wasm32-unknown-emscripten/entrypoint.sh +7 -0
  13. data/builders/wasm32-unknown-wasi/Dockerfile +47 -0
  14. data/builders/wasm32-unknown-wasi/entrypoint.sh +7 -0
  15. data/docs/api.md +2 -0
  16. data/docs/cheat_sheet.md +195 -0
  17. data/docs/faq.md +25 -0
  18. data/exe/rbwasm +7 -0
  19. data/ext/.gitignore +2 -0
  20. data/ext/README.md +11 -0
  21. data/ext/extinit.c.erb +32 -0
  22. data/lib/ruby_wasm/3.1/ruby_wasm.so +0 -0
  23. data/lib/ruby_wasm/3.2/ruby_wasm.so +0 -0
  24. data/lib/ruby_wasm/build/build_params.rb +3 -0
  25. data/lib/ruby_wasm/build/downloader.rb +18 -0
  26. data/lib/ruby_wasm/build/executor.rb +187 -0
  27. data/lib/ruby_wasm/build/product/baseruby.rb +37 -0
  28. data/lib/ruby_wasm/build/product/crossruby.rb +330 -0
  29. data/lib/ruby_wasm/build/product/libyaml.rb +68 -0
  30. data/lib/ruby_wasm/build/product/openssl.rb +88 -0
  31. data/lib/ruby_wasm/build/product/product.rb +39 -0
  32. data/lib/ruby_wasm/build/product/ruby_source.rb +103 -0
  33. data/lib/ruby_wasm/build/product/wasi_vfs.rb +45 -0
  34. data/lib/ruby_wasm/build/product/zlib.rb +68 -0
  35. data/lib/ruby_wasm/build/product.rb +8 -0
  36. data/lib/ruby_wasm/build/toolchain/wit_bindgen.rb +31 -0
  37. data/lib/ruby_wasm/build/toolchain.rb +193 -0
  38. data/lib/ruby_wasm/build.rb +88 -0
  39. data/lib/ruby_wasm/cli.rb +217 -0
  40. data/lib/ruby_wasm/packager/core.rb +156 -0
  41. data/lib/ruby_wasm/packager/file_system.rb +158 -0
  42. data/lib/ruby_wasm/packager.rb +159 -0
  43. data/lib/ruby_wasm/rake_task.rb +59 -0
  44. data/lib/ruby_wasm/util.rb +15 -0
  45. data/lib/ruby_wasm/version.rb +3 -0
  46. data/lib/ruby_wasm.rb +33 -0
  47. data/package-lock.json +9500 -0
  48. data/package.json +12 -0
  49. data/rakelib/check.rake +37 -0
  50. data/rakelib/ci.rake +152 -0
  51. data/rakelib/doc.rake +29 -0
  52. data/rakelib/format.rake +35 -0
  53. data/rakelib/gem.rake +22 -0
  54. data/rakelib/packaging.rake +151 -0
  55. data/rakelib/version.rake +40 -0
  56. data/sig/open_uri.rbs +4 -0
  57. data/sig/ruby_wasm/build.rbs +318 -0
  58. data/sig/ruby_wasm/cli.rbs +27 -0
  59. data/sig/ruby_wasm/ext.rbs +13 -0
  60. data/sig/ruby_wasm/packager.rbs +91 -0
  61. data/sig/ruby_wasm/util.rbs +5 -0
  62. data/tools/clang-format-diff.sh +18 -0
  63. data/tools/exe/rbminify +12 -0
  64. data/tools/lib/syntax_tree/minify_ruby.rb +63 -0
  65. metadata +113 -0
data/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "ruby.wasm",
3
+ "private": true,
4
+ "workspaces": [
5
+ "packages/npm-packages/*"
6
+ ],
7
+ "devDependencies": {
8
+ "@playwright/test": "^1.40.1",
9
+ "@rollup/plugin-json": "^6.0.1",
10
+ "rollup": "^4.6.1"
11
+ }
12
+ }
@@ -0,0 +1,37 @@
1
+ namespace :check do
2
+ wit_bindgen = RubyWasm::WitBindgen.new(build_dir: "build")
3
+ task :install_wit_bindgen do
4
+ wit_bindgen.install
5
+ end
6
+ task bindgen_c: :install_wit_bindgen do
7
+ wits = [
8
+ %w[packages/gems/js/ext/witapi/bindgen/rb-abi-guest.wit --export],
9
+ %w[packages/gems/js/ext/js/bindgen/rb-js-abi-host.wit --import]
10
+ ]
11
+ wits.each do |wit|
12
+ path, mode = wit
13
+ sh "#{wit_bindgen.bin_path} guest c #{mode} #{path} --out-dir #{File.dirname(path)}"
14
+ end
15
+ end
16
+
17
+ task bindgen_js: :install_wit_bindgen do
18
+ sh *[
19
+ wit_bindgen.bin_path,
20
+ "host",
21
+ "js",
22
+ "--import",
23
+ "packages/gems/js/ext/witapi/bindgen/rb-abi-guest.wit",
24
+ "--export",
25
+ "packages/gems/js/ext/js/bindgen/rb-js-abi-host.wit",
26
+ "--out-dir",
27
+ "packages/npm-packages/ruby-wasm-wasi/src/bindgen"
28
+ ]
29
+ end
30
+
31
+ desc "Check wit-bindgen'ed sources are up-to-date"
32
+ task bindgen: %i[bindgen_c bindgen_js]
33
+
34
+ task :type do
35
+ sh "bundle exec steep check"
36
+ end
37
+ end
data/rakelib/ci.rake ADDED
@@ -0,0 +1,152 @@
1
+ def latest_build_sources
2
+ BUILD_SOURCES
3
+ .filter_map do |name|
4
+ src = RubyWasm::Packager.build_source_aliases(LIB_ROOT)[name]
5
+ case src[:type]
6
+ when "github"
7
+ url = "repos/#{src[:repo]}/commits/#{src[:rev]}"
8
+ revision = JSON.parse(`gh api #{url}`)
9
+ [name, revision["sha"]]
10
+ when "tarball"
11
+ nil
12
+ else
13
+ raise "#{src[:type]} is not supported to pin source revision"
14
+ end
15
+ end
16
+ .to_h
17
+ end
18
+
19
+ def release_note
20
+ output = <<EOS
21
+ | channel | source |
22
+ |:-------:|:------:|
23
+ EOS
24
+
25
+ BUILD_SOURCES.each do |name|
26
+ source = RubyWasm::Packager.build_source_aliases(LIB_ROOT)[name]
27
+ case source[:type]
28
+ when "github"
29
+ output +=
30
+ "| #{name} | [`#{source[:repo]}@#{source[:rev]}`](https://github.com/ruby/ruby/tree/#{source[:rev]}) |\n"
31
+ when "tarball"
32
+ output += "| #{name} | #{source[:url]} |\n"
33
+ else
34
+ raise "unknown source type: #{source[:type]}"
35
+ end
36
+ end
37
+ output
38
+ end
39
+
40
+ def sh_or_warn(*cmd)
41
+ sh *cmd do |ok, status|
42
+ unless ok
43
+ warn "Command failed with status (#{status.exitstatus}): #{cmd.join ""}"
44
+ end
45
+ end
46
+ end
47
+
48
+ def rake_task_matrix
49
+ require "pathname"
50
+ ruby_cache_keys = {}
51
+ BUILD_TASKS.each { |build| ruby_cache_keys[build.name] = build.hexdigest }
52
+ build_entries =
53
+ BUILD_TASKS.map do |build|
54
+ {
55
+ task: "build:#{build.name}",
56
+ artifact:
57
+ Pathname.new(build.artifact).relative_path_from(LIB_ROOT).to_s,
58
+ artifact_name: File.basename(build.artifact, ".tar.gz"),
59
+ builder: build.target,
60
+ rubies_cache_key: ruby_cache_keys[build.name]
61
+ }
62
+ end
63
+ npm_entries =
64
+ NPM_PACKAGES.map do |pkg|
65
+ entry = {
66
+ task: "npm:#{pkg[:name]}",
67
+ prerelease: "npm:configure_prerelease",
68
+ artifact: "packages/npm-packages/#{pkg[:name]}/#{pkg[:name]}-*.tgz",
69
+ artifact_name: "npm-#{pkg[:name]}",
70
+ builder: pkg[:target],
71
+ rubies_cache_key: npm_pkg_rubies_cache_key(pkg)
72
+ }
73
+ # Run tests only if the package has 'test' script
74
+ package_json =
75
+ JSON.parse(
76
+ File.read("packages/npm-packages/#{pkg[:name]}/package.json")
77
+ )
78
+ if package_json["scripts"] && package_json["scripts"]["test"]
79
+ entry[:test] = "npm:#{pkg[:name]}:check"
80
+ end
81
+ entry
82
+ end
83
+ standalone_entries =
84
+ STANDALONE_PACKAGES.map do |pkg|
85
+ {
86
+ task: "standalone:#{pkg[:name]}",
87
+ artifact: "packages/standalone/#{pkg[:name]}/dist",
88
+ artifact_name: "standalone-#{pkg[:name]}",
89
+ builder: "wasm32-unknown-wasi",
90
+ rubies_cache_key: ruby_cache_keys[pkg[:build]]
91
+ }
92
+ end
93
+ { build: build_entries, npm: npm_entries, standalone: standalone_entries }
94
+ end
95
+
96
+ namespace :ci do
97
+ task :rake_task_matrix do
98
+ print JSON.generate(rake_task_matrix.flat_map { |_, entries| entries })
99
+ end
100
+
101
+ task :pin_build_manifest do
102
+ content = JSON.generate({ ruby_revisions: latest_build_sources })
103
+ File.write("build_manifest.json", content)
104
+ end
105
+
106
+ desc "Fetch artifacts of a run of GitHub Actions"
107
+ task :fetch_artifacts, [:run_id] do |t, args|
108
+ RubyWasm::Toolchain.check_executable("gh")
109
+
110
+ artifacts =
111
+ JSON.load(
112
+ `gh api repos/{owner}/{repo}/actions/runs/#{args[:run_id]}/artifacts`
113
+ )
114
+ matrix = rake_task_matrix.flat_map { |_, entries| entries }
115
+ release_artifacts = matrix.map { |entry| entry[:artifact_name] }
116
+ artifacts =
117
+ artifacts["artifacts"].filter { release_artifacts.include?(_1["name"]) }
118
+ mkdir_p "release"
119
+ Dir.chdir("release") do
120
+ artifacts.each do |artifact|
121
+ url = artifact["archive_download_url"]
122
+ sh "gh api #{url} > #{artifact["name"]}.zip"
123
+ mkdir_p artifact["name"]
124
+ sh "unzip #{artifact["name"]}.zip -d #{artifact["name"]}"
125
+ rm "#{artifact["name"]}.zip"
126
+ end
127
+ end
128
+ end
129
+
130
+ desc "Publish artifacts as a GitHub Release"
131
+ task :publish, [:tag] do |t, args|
132
+ RubyWasm::Toolchain.check_executable("gh")
133
+
134
+ nightly = /^\d{4}-\d{2}-\d{2}-.$/.match?(args[:tag])
135
+ matrix = rake_task_matrix
136
+ files =
137
+ matrix
138
+ .flat_map { |_, entries| entries }
139
+ .map { |entry| "release/#{entry[:artifact_name]}/*" }
140
+ File.open("release/note.md", "w") { |f| f.print release_note }
141
+ matrix[:npm].each do |task|
142
+ artifact = task[:artifact_name]
143
+ tarball = Dir.glob("release/#{artifact}/*")
144
+ next if tarball.empty?
145
+ tarball = tarball[0]
146
+ # tolerate failure as a case that has already been released
147
+ npm_tag = nightly ? "next" : "latest"
148
+ sh_or_warn %Q(npm publish --tag #{npm_tag} #{tarball})
149
+ end
150
+ sh %Q(gh release create #{args[:tag]} --title #{args[:tag]} --notes-file release/note.md #{nightly ? "--prerelease" : ""} #{files.join(" ")})
151
+ end
152
+ end
data/rakelib/doc.rake ADDED
@@ -0,0 +1,29 @@
1
+ require "rdoc/task"
2
+ require "ruby_wasm/version"
3
+
4
+ RDoc::Task.new do |doc|
5
+ doc.main = "README.md"
6
+ doc.title = "ruby.wasm Documentation"
7
+ doc.rdoc_files =
8
+ FileList.new %w[
9
+ *.md
10
+ packages/gems/js/ext/**/*.c
11
+ packages/gems/js/lib/**/*.rb
12
+ ]
13
+ end
14
+
15
+ namespace :doc do
16
+ desc "Update docs/api/javascript.md"
17
+ task :api_js do
18
+ sh "npx",
19
+ "documentation",
20
+ "readme",
21
+ "--readme-file",
22
+ "./packages/npm-packages/ruby-wasm-wasi/README.md",
23
+ "--section",
24
+ "API",
25
+ "--markdown-toc",
26
+ "false",
27
+ "./packages/npm-packages/ruby-wasm-wasi/dist/esm/index.js"
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ namespace :format do
2
+ begin
3
+ require "syntax_tree/rake_tasks"
4
+ SyntaxTree::Rake::WriteTask.new(
5
+ :ruby,
6
+ Rake::FileList[
7
+ "Rakefile",
8
+ "lib/**/*.rb",
9
+ "ext/**/*.rb",
10
+ "test/**/*.rb",
11
+ "rakelib/**/*.rake",
12
+ "packages/**/*.rb"
13
+ ]
14
+ )
15
+ rescue LoadError
16
+ end
17
+
18
+ task :js do
19
+ sh "npm run format", chdir: "packages/npm-packages/ruby-wasm-wasi"
20
+ end
21
+
22
+ task :c do
23
+ sh "find packages/gems/ ext/ -iname *.h -o -iname *.c | xargs clang-format -i"
24
+ end
25
+ end
26
+
27
+ task :format do
28
+ if Rake::Task.task_defined?("format:ruby")
29
+ Rake::Task["format:ruby"].invoke
30
+ else
31
+ puts "\e[33mSyntaxTree not installed, skipping format:ruby\e[0m"
32
+ end
33
+ Rake::Task["format:js"].invoke
34
+ Rake::Task["format:c"].invoke
35
+ end
data/rakelib/gem.rake ADDED
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/test_*.rb"]
8
+ end
9
+
10
+ begin
11
+ require "rb_sys/extensiontask"
12
+
13
+ gemspec = Gem::Specification.load("ruby_wasm.gemspec")
14
+ RbSys::ExtensionTask.new("ruby_wasm", gemspec) do |ext|
15
+ ext.lib_dir = "lib/ruby_wasm"
16
+ end
17
+ rescue LoadError => e
18
+ task :compile do
19
+ $stderr.puts "Skipping compilation of ruby_wasm extension: #{e.message}"
20
+ exit 1
21
+ end
22
+ end
@@ -0,0 +1,151 @@
1
+ wasi_vfs = RubyWasm::WasiVfsProduct.new(File.join(Dir.pwd, "build"))
2
+ wasi_sdk = TOOLCHAINS["wasi-sdk"]
3
+ tools = {
4
+ "WASI_VFS_CLI" => File.expand_path(File.join(__dir__, "..", "exe", "rbwasm")),
5
+ "WASMOPT" => wasi_sdk.wasm_opt
6
+ }
7
+
8
+ def npm_pkg_build_command(pkg)
9
+ # Skip if the package does not require building ruby
10
+ return nil unless pkg[:ruby_version] && pkg[:target]
11
+ [
12
+ "bundle",
13
+ "exec",
14
+ "rbwasm",
15
+ "build",
16
+ "--ruby-version",
17
+ pkg[:ruby_version],
18
+ "--target",
19
+ pkg[:target],
20
+ "--build-profile",
21
+ "full"
22
+ ]
23
+ end
24
+
25
+ def npm_pkg_rubies_cache_key(pkg)
26
+ build_command = npm_pkg_build_command(pkg)
27
+ return nil unless build_command
28
+ require "open3"
29
+ cmd = build_command + ["--print-ruby-cache-key"]
30
+ stdout, status = Open3.capture2(*cmd)
31
+ unless status.success?
32
+ raise "Command failed with status (#{status.exitstatus}): #{cmd.join ""}"
33
+ end
34
+ require "json"
35
+ JSON.parse(stdout)["hexdigest"]
36
+ end
37
+
38
+ namespace :npm do
39
+ NPM_PACKAGES.each do |pkg|
40
+ base_dir = Dir.pwd
41
+ pkg_dir = "#{Dir.pwd}/packages/npm-packages/#{pkg[:name]}"
42
+
43
+ namespace pkg[:name] do
44
+ desc "Build ruby for npm package #{pkg[:name]}"
45
+ task "ruby" do
46
+ build_command = npm_pkg_build_command(pkg)
47
+ # Skip if the package does not require building ruby
48
+ next unless build_command
49
+
50
+ env = {
51
+ # Share ./build and ./rubies in the same workspace
52
+ "RUBY_WASM_ROOT" => base_dir
53
+ }
54
+ if gemfile_path = pkg[:gemfile]
55
+ env["BUNDLE_GEMFILE"] = File.join(base_dir, gemfile_path)
56
+ else
57
+ # Explicitly disable rubygems integration since Bundler finds
58
+ # Gemfile in the repo root directory.
59
+ build_command.push "--disable-gems"
60
+ end
61
+ dist_dir = File.join(pkg_dir, "dist")
62
+ mkdir_p dist_dir
63
+ if pkg[:target] == "wasm32-unknown-wasi"
64
+ sh env,
65
+ *build_command,
66
+ "--no-stdlib",
67
+ "-o",
68
+ File.join(dist_dir, "ruby.wasm")
69
+ sh env,
70
+ *build_command,
71
+ "-o",
72
+ File.join(dist_dir, "ruby.debug+stdlib.wasm")
73
+ sh wasi_sdk.wasm_opt,
74
+ "--strip-debug",
75
+ File.join(dist_dir, "ruby.wasm"),
76
+ "-o",
77
+ File.join(dist_dir, "ruby.wasm")
78
+ sh wasi_sdk.wasm_opt,
79
+ "--strip-debug",
80
+ File.join(dist_dir, "ruby.debug+stdlib.wasm"),
81
+ "-o",
82
+ File.join(dist_dir, "ruby+stdlib.wasm")
83
+ elsif pkg[:target] == "wasm32-unknown-emscripten"
84
+ sh env, *build_command, "-o", "/dev/null"
85
+ end
86
+ end
87
+
88
+ desc "Build npm package #{pkg[:name]}"
89
+ task "build" => ["ruby"] do
90
+ sh tools, "npm run build", chdir: pkg_dir
91
+ end
92
+
93
+ desc "Check npm package #{pkg[:name]}"
94
+ task "check" do
95
+ sh "npm test", chdir: pkg_dir
96
+ end
97
+ end
98
+
99
+ desc "Make tarball for npm package #{pkg[:name]}"
100
+ task pkg[:name] do
101
+ wasi_sdk.install_binaryen
102
+ Rake::Task["npm:#{pkg[:name]}:build"].invoke
103
+ sh "npm pack", chdir: pkg_dir
104
+ end
105
+ end
106
+
107
+ desc "Configure for pre-release"
108
+ task :configure_prerelease, [:prerel] do |t, args|
109
+ require "json"
110
+ prerel = args[:prerel]
111
+ new_pkgs = {}
112
+ NPM_PACKAGES.each do |pkg|
113
+ pkg_dir = "#{Dir.pwd}/packages/npm-packages/#{pkg[:name]}"
114
+ pkg_json = "#{pkg_dir}/package.json"
115
+ package = JSON.parse(File.read(pkg_json))
116
+
117
+ version = package["version"] + "-#{prerel}"
118
+ new_pkgs[package["name"]] = version
119
+ sh *["npm", "pkg", "set", "version=#{version}"], chdir: pkg_dir
120
+ end
121
+
122
+ NPM_PACKAGES.each do |pkg|
123
+ pkg_dir = "#{Dir.pwd}/packages/npm-packages/#{pkg[:name]}"
124
+ pkg_json = "#{pkg_dir}/package.json"
125
+ package = JSON.parse(File.read(pkg_json))
126
+ (package["dependencies"] || []).each do |dep, _|
127
+ next unless new_pkgs[dep]
128
+ sh *["npm", "pkg", "set", "dependencies.#{dep}=#{new_pkgs[dep]}"],
129
+ chdir: pkg_dir
130
+ end
131
+ end
132
+ end
133
+
134
+ desc "Build all npm packages"
135
+ multitask all: NPM_PACKAGES.map { |pkg| pkg[:name] }
136
+ end
137
+
138
+ namespace :standalone do
139
+ STANDALONE_PACKAGES.each do |pkg|
140
+ pkg_dir = "#{Dir.pwd}/packages/standalone/#{pkg[:name]}"
141
+
142
+ desc "Build standalone package #{pkg[:name]}"
143
+ task "#{pkg[:name]}" => ["build:#{pkg[:build]}"] do
144
+ wasi_sdk.install_binaryen
145
+ base_dir = Dir.pwd
146
+ sh tools,
147
+ "./build-package.sh #{base_dir}/rubies/ruby-#{pkg[:build]}",
148
+ chdir: pkg_dir
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,40 @@
1
+ def bump_version_npm_package(package, version)
2
+ require "json"
3
+ pkg_dir = "#{Dir.pwd}/packages/npm-packages/#{package}"
4
+ pkg_json = "#{pkg_dir}/package.json"
5
+ package = JSON.parse(File.read(pkg_json))
6
+ old_version = package["version"]
7
+ pkg_name = package["name"]
8
+ package["version"] = version
9
+ File.write(pkg_json, JSON.pretty_generate(package) + "\n")
10
+
11
+ # Update package-lock.json
12
+ # Update README.md and other docs
13
+ `git grep -l #{pkg_name}@#{old_version}`.split.each do |file|
14
+ content = File.read(file)
15
+ next_nightly = Date.today.strftime("%Y-%m-%d")
16
+ content.gsub!(
17
+ /#{pkg_name}@#{old_version}-\d{4}-\d{2}-\d{2}-a/,
18
+ "#{pkg_name}@#{version}-#{next_nightly}-a"
19
+ )
20
+ content.gsub!(/#{pkg_name}@#{old_version}/, "#{pkg_name}@#{version}")
21
+ File.write(file, content)
22
+ end
23
+ end
24
+
25
+ def bump_version_rb(version_rb, version)
26
+ version_rb_content = File.read(version_rb)
27
+ version_rb_content.gsub!(/VERSION = ".+"/, "VERSION = \"#{version}\"")
28
+ File.write(version_rb, version_rb_content)
29
+ end
30
+
31
+ task :bump_version, %i[version] do |t, args|
32
+ version = args[:version] or raise "version is required"
33
+ bump_version_rb("lib/ruby_wasm/version.rb", version)
34
+ bump_version_rb("packages/gems/js/lib/js/version.rb", version)
35
+ NPM_PACKAGES.each { |pkg| bump_version_npm_package(pkg[:name], version) }
36
+ # Update ./package-lock.json
37
+ sh "npm install"
38
+ # Update Gemfile.lock
39
+ sh "BUNDLE_GEMFILE=packages/npm-packages/ruby-wasm-wasi/Gemfile bundle install"
40
+ end
data/sig/open_uri.rbs ADDED
@@ -0,0 +1,4 @@
1
+ # TODO: Upstream OpenURI sigs to rbs/stdlib
2
+ module OpenURI
3
+ def self.open_uri: [T] (*untyped, **untyped) { (untyped) -> T } -> T
4
+ end