ruby_wasm 2.5.1-aarch64-linux-musl
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.clang-format +8 -0
- data/CONTRIBUTING.md +128 -0
- data/Gemfile +17 -0
- data/LICENSE +21 -0
- data/NOTICE +1293 -0
- data/README.md +154 -0
- data/Rakefile +164 -0
- data/Steepfile +24 -0
- data/benchmarks/vm_deep_call.rb +55 -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/3.3/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 +191 -0
- data/lib/ruby_wasm/build/product/baseruby.rb +37 -0
- data/lib/ruby_wasm/build/product/crossruby.rb +360 -0
- data/lib/ruby_wasm/build/product/libyaml.rb +70 -0
- data/lib/ruby_wasm/build/product/openssl.rb +93 -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 +70 -0
- data/lib/ruby_wasm/build/product.rb +8 -0
- data/lib/ruby_wasm/build/target.rb +24 -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 +92 -0
- data/lib/ruby_wasm/cli.rb +347 -0
- data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.command.wasm +0 -0
- data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.reactor.wasm +0 -0
- data/lib/ruby_wasm/packager/component_adapter.rb +14 -0
- data/lib/ruby_wasm/packager/core.rb +333 -0
- data/lib/ruby_wasm/packager/file_system.rb +160 -0
- data/lib/ruby_wasm/packager.rb +96 -0
- data/lib/ruby_wasm/rake_task.rb +60 -0
- data/lib/ruby_wasm/util.rb +15 -0
- data/lib/ruby_wasm/version.rb +3 -0
- data/lib/ruby_wasm.rb +34 -0
- data/package-lock.json +9777 -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 +165 -0
- data/rakelib/version.rake +40 -0
- data/sig/open_uri.rbs +4 -0
- data/sig/ruby_wasm/build.rbs +327 -0
- data/sig/ruby_wasm/cli.rbs +51 -0
- data/sig/ruby_wasm/ext.rbs +26 -0
- data/sig/ruby_wasm/packager.rbs +122 -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 +114 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
# Package Ruby code into a mountable directory.
|
2
|
+
class RubyWasm::Packager::FileSystem
|
3
|
+
def initialize(dest_dir, packager)
|
4
|
+
@dest_dir = dest_dir
|
5
|
+
@packager = packager
|
6
|
+
@ruby_root = File.join(@dest_dir, "usr", "local")
|
7
|
+
end
|
8
|
+
|
9
|
+
def package_ruby_root(tarball, executor)
|
10
|
+
usr_dir = File.dirname(@ruby_root)
|
11
|
+
executor.mkdir_p usr_dir
|
12
|
+
executor.system(
|
13
|
+
"tar",
|
14
|
+
"-C",
|
15
|
+
usr_dir,
|
16
|
+
"-xzf",
|
17
|
+
tarball,
|
18
|
+
"--strip-components=2"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove_stdlib(executor)
|
23
|
+
# Include only rbconfig.rb
|
24
|
+
rbconfig =
|
25
|
+
File.join(
|
26
|
+
@ruby_root,
|
27
|
+
"lib",
|
28
|
+
"ruby",
|
29
|
+
ruby_version,
|
30
|
+
"wasm32-wasi",
|
31
|
+
"rbconfig.rb"
|
32
|
+
)
|
33
|
+
# Remove all files except rbconfig.rb
|
34
|
+
RubyWasm.logger.info "Removing stdlib (except rbconfig.rb: #{rbconfig})"
|
35
|
+
rbconfig_contents = File.read(rbconfig)
|
36
|
+
executor.rm_rf @ruby_root
|
37
|
+
executor.mkdir_p File.dirname(rbconfig)
|
38
|
+
File.write(rbconfig, rbconfig_contents)
|
39
|
+
end
|
40
|
+
|
41
|
+
def package_gems
|
42
|
+
@packager.specs.each do |spec|
|
43
|
+
RubyWasm.logger.info "Packaging gem: #{spec.full_name}"
|
44
|
+
end
|
45
|
+
self.each_gem_content_path do |relative, source|
|
46
|
+
RubyWasm.logger.debug "Packaging gem file: #{relative}"
|
47
|
+
dest = File.join(@dest_dir, relative)
|
48
|
+
FileUtils.mkdir_p File.dirname(dest)
|
49
|
+
FileUtils.cp_r source, dest
|
50
|
+
end
|
51
|
+
|
52
|
+
setup_rb_path = File.join(bundle_relative_path, "setup.rb")
|
53
|
+
RubyWasm.logger.info "Packaging setup.rb: #{setup_rb_path}"
|
54
|
+
full_setup_rb_path = File.join(@dest_dir, setup_rb_path)
|
55
|
+
FileUtils.mkdir_p File.dirname(full_setup_rb_path)
|
56
|
+
File.write(full_setup_rb_path, setup_rb_content)
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup_rb_content
|
60
|
+
content = ""
|
61
|
+
self.each_gem_require_path do |relative, _|
|
62
|
+
content << %Q[$:.unshift File.expand_path("#{File.join("/", relative)}")\n]
|
63
|
+
end
|
64
|
+
content
|
65
|
+
end
|
66
|
+
|
67
|
+
def remove_non_runtime_files(executor)
|
68
|
+
patterns = %w[
|
69
|
+
usr/local/lib/libruby-static.a
|
70
|
+
usr/local/bin/ruby
|
71
|
+
usr/local/include
|
72
|
+
]
|
73
|
+
|
74
|
+
patterns << "**/*.so" unless @packager.support_dynamic_linking?
|
75
|
+
patterns.each do |pattern|
|
76
|
+
Dir
|
77
|
+
.glob(File.join(@dest_dir, pattern))
|
78
|
+
.each do |entry|
|
79
|
+
RubyWasm.logger.debug do
|
80
|
+
relative_entry = Pathname.new(entry).relative_path_from(@dest_dir)
|
81
|
+
"Removing non-runtime file: #{relative_entry}"
|
82
|
+
end
|
83
|
+
executor.rm_rf entry
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def bundle_dir
|
89
|
+
File.join(@dest_dir, bundle_relative_path)
|
90
|
+
end
|
91
|
+
|
92
|
+
def ruby_root
|
93
|
+
@ruby_root
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Iterates over each gem's require path and extension path.
|
99
|
+
# Yields the installation relative path and the source path.
|
100
|
+
def each_gem_require_path(&block)
|
101
|
+
each_gem_extension_path(&block)
|
102
|
+
@packager.specs.each do |spec|
|
103
|
+
# Use raw_require_paths to exclude extensions
|
104
|
+
spec.raw_require_paths.each do |require_path|
|
105
|
+
source = File.expand_path(require_path, spec.full_gem_path)
|
106
|
+
next unless File.exist?(source)
|
107
|
+
relative =
|
108
|
+
File.join(bundle_relative_path, "gems", spec.full_name, require_path)
|
109
|
+
yield relative, source
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def each_gem_content_path(&block)
|
115
|
+
each_gem_extension_path(&block)
|
116
|
+
|
117
|
+
@packager.specs.each do |spec|
|
118
|
+
next unless File.exist?(spec.full_gem_path)
|
119
|
+
|
120
|
+
# spec.files is only valid before the gem is packaged.
|
121
|
+
if spec.source.path?
|
122
|
+
relative_paths = spec.files
|
123
|
+
else
|
124
|
+
# All files in .gem are required.
|
125
|
+
relative_paths = Dir.children(spec.full_gem_path)
|
126
|
+
end
|
127
|
+
relative_paths.each do |require_path|
|
128
|
+
source = File.expand_path(require_path, spec.full_gem_path)
|
129
|
+
next unless File.exist?(source)
|
130
|
+
relative =
|
131
|
+
File.join(bundle_relative_path, "gems", spec.full_name, require_path)
|
132
|
+
yield relative, source
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def each_gem_extension_path
|
138
|
+
@packager.specs.each do |spec|
|
139
|
+
if !spec.extensions.empty? && File.exist?(spec.extension_dir)
|
140
|
+
relative = File.join(bundle_relative_path, "extensions", spec.full_name)
|
141
|
+
yield relative, spec.extension_dir
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def bundle_relative_path
|
147
|
+
"bundle"
|
148
|
+
end
|
149
|
+
|
150
|
+
def ruby_version
|
151
|
+
rubyarchdir = self.rubyarchdir
|
152
|
+
File.basename(File.dirname(rubyarchdir))
|
153
|
+
end
|
154
|
+
|
155
|
+
def rubyarchdir
|
156
|
+
maybe =
|
157
|
+
Dir.glob(File.join(@ruby_root, "lib", "ruby", "*", "wasm32-wasi")).first
|
158
|
+
maybe || raise("Cannot find rubyarchdir")
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# A class responsible for packaging whole Ruby project
|
2
|
+
class RubyWasm::Packager
|
3
|
+
# Initializes a new instance of the RubyWasm::Packager class.
|
4
|
+
#
|
5
|
+
# @param root [String] The root directory of the Ruby project.
|
6
|
+
# The root directory (will) contain the following files:
|
7
|
+
# * build_manifest.json
|
8
|
+
# * rubies
|
9
|
+
# * build
|
10
|
+
# @param config [Hash] The build config used for building Ruby.
|
11
|
+
# @param definition [Bundler::Definition] The Bundler definition.
|
12
|
+
def initialize(root, config = nil, definition = nil)
|
13
|
+
@root = root
|
14
|
+
@definition = definition
|
15
|
+
@config = config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Packages the Ruby code into a Wasm binary. (including extensions)
|
19
|
+
#
|
20
|
+
# @param executor [RubyWasm::BuildExecutor] The executor for building the Wasm binary.
|
21
|
+
# @param dest_dir [String] The destination used to construct the filesystem.
|
22
|
+
# @param options [Hash] The packaging options.
|
23
|
+
# @return [Array<Integer>] The bytes of the packaged Wasm binary.
|
24
|
+
def package(executor, dest_dir, options)
|
25
|
+
ruby_core = self.ruby_core_build()
|
26
|
+
tarball = ruby_core.build(executor, options)
|
27
|
+
|
28
|
+
fs = RubyWasm::Packager::FileSystem.new(dest_dir, self)
|
29
|
+
fs.package_ruby_root(tarball, executor)
|
30
|
+
wasm_bytes = ruby_core.build_and_link_exts(executor)
|
31
|
+
|
32
|
+
fs.package_gems
|
33
|
+
fs.remove_non_runtime_files(executor)
|
34
|
+
fs.remove_stdlib(executor) unless options[:stdlib]
|
35
|
+
|
36
|
+
if full_build_options[:target] == "wasm32-unknown-wasip1" && !support_dynamic_linking?
|
37
|
+
# wasi-vfs supports only WASI target
|
38
|
+
wasi_vfs = RubyWasmExt::WasiVfs.new
|
39
|
+
wasi_vfs.map_dir("/bundle", fs.bundle_dir)
|
40
|
+
wasi_vfs.map_dir("/usr", File.dirname(fs.ruby_root))
|
41
|
+
|
42
|
+
wasm_bytes = wasi_vfs.pack(wasm_bytes)
|
43
|
+
end
|
44
|
+
|
45
|
+
wasm_bytes = RubyWasmExt.preinitialize(wasm_bytes) if options[:optimize]
|
46
|
+
wasm_bytes
|
47
|
+
end
|
48
|
+
|
49
|
+
def ruby_core_build
|
50
|
+
@ruby_core_build ||= RubyWasm::Packager::Core.new(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
# The list of excluded gems from the Bundler definition.
|
54
|
+
EXCLUDED_GEMS = %w[ruby_wasm bundler]
|
55
|
+
|
56
|
+
# Retrieves the specs from the Bundler definition, excluding the excluded gems.
|
57
|
+
def specs
|
58
|
+
return [] unless @definition
|
59
|
+
@specs ||= @definition.resolve.materialize(@definition.requested_dependencies)
|
60
|
+
.reject { |spec| EXCLUDED_GEMS.include?(spec.name) }
|
61
|
+
@specs
|
62
|
+
end
|
63
|
+
|
64
|
+
# Checks if dynamic linking is supported.
|
65
|
+
def support_dynamic_linking?
|
66
|
+
ENV["RUBY_WASM_EXPERIMENTAL_DYNAMIC_LINKING"] == "1"
|
67
|
+
end
|
68
|
+
|
69
|
+
ALL_DEFAULT_EXTS =
|
70
|
+
"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"
|
71
|
+
|
72
|
+
# Retrieves the build options used for building Ruby itself.
|
73
|
+
def build_options
|
74
|
+
default = {
|
75
|
+
target: RubyWasm::Target.new("wasm32-unknown-wasip1"),
|
76
|
+
default_exts: ALL_DEFAULT_EXTS
|
77
|
+
}
|
78
|
+
override = @config || {}
|
79
|
+
# Merge the default options with the config options
|
80
|
+
default.merge(override)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Retrieves the resolved build options
|
84
|
+
def full_build_options
|
85
|
+
options = build_options
|
86
|
+
build_dir = File.join(@root, "build")
|
87
|
+
rubies_dir = File.join(@root, "rubies")
|
88
|
+
toolchain = RubyWasm::Toolchain.get(options[:target], build_dir)
|
89
|
+
options.merge(
|
90
|
+
toolchain: toolchain,
|
91
|
+
build_dir: build_dir,
|
92
|
+
rubies_dir: rubies_dir,
|
93
|
+
src: options[:src]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "rake/tasklib"
|
2
|
+
require_relative "./util"
|
3
|
+
require_relative "./build"
|
4
|
+
|
5
|
+
class RubyWasm::BuildTask < ::Rake::TaskLib
|
6
|
+
# Name of the task.
|
7
|
+
attr_accessor :name
|
8
|
+
|
9
|
+
def initialize(
|
10
|
+
name,
|
11
|
+
target:,
|
12
|
+
src:,
|
13
|
+
toolchain: nil,
|
14
|
+
build_dir: nil,
|
15
|
+
rubies_dir: nil,
|
16
|
+
**options,
|
17
|
+
&block
|
18
|
+
)
|
19
|
+
target = Target.new(target)
|
20
|
+
@build =
|
21
|
+
RubyWasm::Build.new(
|
22
|
+
name,
|
23
|
+
target: target,
|
24
|
+
src: src,
|
25
|
+
toolchain: toolchain,
|
26
|
+
build_dir: build_dir || File.join(Dir.pwd, "build"),
|
27
|
+
rubies_dir: rubies_dir || File.join(Dir.pwd, "rubies"),
|
28
|
+
**options,
|
29
|
+
&block
|
30
|
+
)
|
31
|
+
yield @build if block_given?
|
32
|
+
@crossruby = @build.crossruby
|
33
|
+
# Rake.verbose can be Object.new by default, so compare with true explicitly.
|
34
|
+
executor = RubyWasm::BuildExecutor.new(verbose: Rake.verbose == true)
|
35
|
+
|
36
|
+
desc "Cross-build Ruby for #{@target}"
|
37
|
+
task name do
|
38
|
+
next if File.exist? @crossruby.artifact
|
39
|
+
@crossruby.build(executor)
|
40
|
+
end
|
41
|
+
namespace name do
|
42
|
+
task :remake do
|
43
|
+
@crossruby.build(executor, remake: true)
|
44
|
+
end
|
45
|
+
task :reconfigure do
|
46
|
+
@crossruby.build(executor, reconfigure: true)
|
47
|
+
end
|
48
|
+
task :clean do
|
49
|
+
@crossruby.clean(executor)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def hexdigest
|
55
|
+
require "digest"
|
56
|
+
digest = Digest::SHA256.new
|
57
|
+
@build.cache_key(digest)
|
58
|
+
digest.hexdigest
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RubyWasm
|
2
|
+
module SizeFormatter
|
3
|
+
def format(size)
|
4
|
+
units = %w[B KB MB GB TB]
|
5
|
+
unit = 0
|
6
|
+
while size > 1024 and unit < units.size - 1
|
7
|
+
size /= 1024.0
|
8
|
+
unit += 1
|
9
|
+
end
|
10
|
+
"%s #{units[unit]}" % size.round(2)
|
11
|
+
end
|
12
|
+
|
13
|
+
module_function :format
|
14
|
+
end
|
15
|
+
end
|
data/lib/ruby_wasm.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
require_relative "ruby_wasm/version"
|
4
|
+
require_relative "ruby_wasm/util"
|
5
|
+
require_relative "ruby_wasm/build"
|
6
|
+
require_relative "ruby_wasm/packager"
|
7
|
+
require_relative "ruby_wasm/packager/component_adapter"
|
8
|
+
require_relative "ruby_wasm/packager/file_system"
|
9
|
+
require_relative "ruby_wasm/packager/core"
|
10
|
+
|
11
|
+
module RubyWasm
|
12
|
+
class << self
|
13
|
+
attr_accessor :log_level
|
14
|
+
|
15
|
+
def logger
|
16
|
+
@logger ||=
|
17
|
+
begin
|
18
|
+
logger =
|
19
|
+
Logger.new(
|
20
|
+
$stderr,
|
21
|
+
level: @log_level || Logger::INFO,
|
22
|
+
progname: "rbwasm"
|
23
|
+
)
|
24
|
+
logger.formatter =
|
25
|
+
proc { |severity, datetime, progname, msg| "#{severity}: #{msg}\n" }
|
26
|
+
logger
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def logger=(logger)
|
31
|
+
@logger = logger
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|