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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.clang-format +8 -0
  3. data/CONTRIBUTING.md +128 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE +21 -0
  6. data/NOTICE +1293 -0
  7. data/README.md +154 -0
  8. data/Rakefile +164 -0
  9. data/Steepfile +24 -0
  10. data/benchmarks/vm_deep_call.rb +55 -0
  11. data/docs/api.md +2 -0
  12. data/docs/cheat_sheet.md +195 -0
  13. data/docs/faq.md +25 -0
  14. data/exe/rbwasm +7 -0
  15. data/ext/.gitignore +2 -0
  16. data/ext/README.md +11 -0
  17. data/ext/extinit.c.erb +32 -0
  18. data/lib/ruby_wasm/3.1/ruby_wasm.so +0 -0
  19. data/lib/ruby_wasm/3.2/ruby_wasm.so +0 -0
  20. data/lib/ruby_wasm/3.3/ruby_wasm.so +0 -0
  21. data/lib/ruby_wasm/build/build_params.rb +3 -0
  22. data/lib/ruby_wasm/build/downloader.rb +18 -0
  23. data/lib/ruby_wasm/build/executor.rb +191 -0
  24. data/lib/ruby_wasm/build/product/baseruby.rb +37 -0
  25. data/lib/ruby_wasm/build/product/crossruby.rb +360 -0
  26. data/lib/ruby_wasm/build/product/libyaml.rb +70 -0
  27. data/lib/ruby_wasm/build/product/openssl.rb +93 -0
  28. data/lib/ruby_wasm/build/product/product.rb +39 -0
  29. data/lib/ruby_wasm/build/product/ruby_source.rb +103 -0
  30. data/lib/ruby_wasm/build/product/wasi_vfs.rb +45 -0
  31. data/lib/ruby_wasm/build/product/zlib.rb +70 -0
  32. data/lib/ruby_wasm/build/product.rb +8 -0
  33. data/lib/ruby_wasm/build/target.rb +24 -0
  34. data/lib/ruby_wasm/build/toolchain/wit_bindgen.rb +31 -0
  35. data/lib/ruby_wasm/build/toolchain.rb +193 -0
  36. data/lib/ruby_wasm/build.rb +92 -0
  37. data/lib/ruby_wasm/cli.rb +347 -0
  38. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.command.wasm +0 -0
  39. data/lib/ruby_wasm/packager/component_adapter/wasi_snapshot_preview1.reactor.wasm +0 -0
  40. data/lib/ruby_wasm/packager/component_adapter.rb +14 -0
  41. data/lib/ruby_wasm/packager/core.rb +333 -0
  42. data/lib/ruby_wasm/packager/file_system.rb +160 -0
  43. data/lib/ruby_wasm/packager.rb +96 -0
  44. data/lib/ruby_wasm/rake_task.rb +60 -0
  45. data/lib/ruby_wasm/util.rb +15 -0
  46. data/lib/ruby_wasm/version.rb +3 -0
  47. data/lib/ruby_wasm.rb +34 -0
  48. data/package-lock.json +9777 -0
  49. data/package.json +12 -0
  50. data/rakelib/check.rake +37 -0
  51. data/rakelib/ci.rake +152 -0
  52. data/rakelib/doc.rake +29 -0
  53. data/rakelib/format.rake +35 -0
  54. data/rakelib/gem.rake +22 -0
  55. data/rakelib/packaging.rake +165 -0
  56. data/rakelib/version.rake +40 -0
  57. data/sig/open_uri.rbs +4 -0
  58. data/sig/ruby_wasm/build.rbs +327 -0
  59. data/sig/ruby_wasm/cli.rbs +51 -0
  60. data/sig/ruby_wasm/ext.rbs +26 -0
  61. data/sig/ruby_wasm/packager.rbs +122 -0
  62. data/sig/ruby_wasm/util.rbs +5 -0
  63. data/tools/clang-format-diff.sh +18 -0
  64. data/tools/exe/rbminify +12 -0
  65. data/tools/lib/syntax_tree/minify_ruby.rb +63 -0
  66. 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
@@ -0,0 +1,3 @@
1
+ module RubyWasm
2
+ VERSION = "2.5.1"
3
+ 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