Skip to content

Commit 779ec3c

Browse files
Migrate all rake tasks to use rbwasm build
1 parent a03a243 commit 779ec3c

File tree

9 files changed

+175
-140
lines changed

9 files changed

+175
-140
lines changed

Rakefile

Lines changed: 17 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -8,78 +8,14 @@ require "ruby_wasm/rake_task"
88

99
Dir.glob("tasks/**.rake").each { |f| import f }
1010

11-
BUILD_SOURCES = {
12-
"head" => {
13-
type: "github",
14-
repo: "ruby/ruby",
15-
rev: "master",
16-
patches: Dir["./patches/*.patch"].map { |p| File.expand_path(p) }
17-
},
18-
"3.3" => {
19-
type: "tarball",
20-
url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.0.tar.gz"
21-
},
22-
"3.2" => {
23-
type: "tarball",
24-
url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.2.tar.gz"
25-
}
26-
}
27-
28-
# Respect revisions specified in build_manifest.json, which is usually generated on GitHub Actions.
29-
if File.exist?("build_manifest.json")
30-
begin
31-
manifest = JSON.parse(File.read("build_manifest.json"))
32-
manifest["ruby_revisions"].each do |name, rev|
33-
BUILD_SOURCES[name][:rev] = rev
34-
end
35-
rescue StandardError
36-
$stderr.puts "Failed to load build_manifest.json"
37-
end
38-
end
39-
40-
FULL_EXTS =
41-
"bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl"
11+
BUILD_SOURCES = ["3.3", "3.2", "head"]
12+
BUILD_PROFILES = ["full", "minimal"]
4213

43-
BUILD_PROFILES = {
44-
"minimal" => {
45-
debug: false,
46-
default_exts: "",
47-
user_exts: []
48-
},
49-
"minimal-debug" => {
50-
debug: true,
51-
default_exts: "",
52-
user_exts: []
53-
},
54-
"full" => {
55-
debug: false,
56-
default_exts: FULL_EXTS,
57-
user_exts: []
58-
},
59-
"full-debug" => {
60-
debug: true,
61-
default_exts: FULL_EXTS,
62-
user_exts: []
63-
},
64-
"full-js-debug" => {
65-
debug: true,
66-
default_exts: FULL_EXTS,
67-
user_exts: %w[js witapi]
68-
}
69-
}
70-
71-
BUILDS =
72-
%w[wasm32-unknown-wasi wasm32-unknown-emscripten]
73-
.product(BUILD_SOURCES.keys, BUILD_PROFILES.keys)
74-
.select do |target, _, profile_name|
75-
if target == "wasm32-unknown-emscripten"
76-
# Builds only full for Emscripten since minimal, js, debug
77-
# builds are rarely used with Emscripten.
78-
next profile_name == "full"
79-
end
80-
next true
81-
end
82-
.map { |t, s, p| { src: s, target: t, profile: p } }
14+
BUILDS = BUILD_SOURCES.product(BUILD_PROFILES).map do |src, profile|
15+
[src, "wasm32-unknown-wasi", profile]
16+
end + BUILD_SOURCES.map do |src|
17+
[src, "wasm32-unknown-emscripten", "full"]
18+
end
8319

8420
NPM_PACKAGES = [
8521
{
@@ -117,45 +53,19 @@ STANDALONE_PACKAGES = [
11753
LIB_ROOT = File.dirname(__FILE__)
11854

11955
TOOLCHAINS = {}
56+
BUILDS.map { |_, target, _| target }.uniq.each do |target|
57+
toolchain = RubyWasm::Toolchain.get(target)
58+
TOOLCHAINS[toolchain.name] = toolchain
59+
end
12060

12161
namespace :build do
12262
BUILD_TASKS =
123-
BUILDS.map do |params|
124-
name = "#{params[:src]}-#{params[:target]}-#{params[:profile]}"
125-
source = BUILD_SOURCES[params[:src]].merge(name: params[:src])
126-
profile = BUILD_PROFILES[params[:profile]]
127-
options = {
128-
src: source,
129-
target: params[:target],
130-
default_exts: profile[:default_exts]
131-
}
132-
debug = profile[:debug]
133-
RubyWasm::BuildTask.new(name, **options) do |t|
134-
if debug
135-
t.crossruby.debugflags = %w[-g]
136-
t.crossruby.wasmoptflags = %w[-O3 -g]
137-
t.crossruby.ldflags = %w[
138-
-Xlinker
139-
--stack-first
140-
-Xlinker
141-
-z
142-
-Xlinker
143-
stack-size=16777216
144-
]
145-
else
146-
t.crossruby.debugflags = %w[-g0]
147-
t.crossruby.ldflags = %w[-Xlinker -zstack-size=16777216]
148-
end
149-
150-
toolchain = t.toolchain
151-
t.crossruby.user_exts =
152-
profile[:user_exts].map do |ext|
153-
srcdir = File.join(LIB_ROOT, "packages", "gems", "js", "ext", ext)
154-
RubyWasm::CrossRubyExtProduct.new(srcdir, toolchain)
155-
end
156-
unless TOOLCHAINS.key? toolchain.name
157-
TOOLCHAINS[toolchain.name] = toolchain
158-
end
63+
BUILDS.map do |src, target, profile|
64+
name = "#{src}-#{target}-#{profile}"
65+
66+
desc "Cross-build Ruby for #{@target}"
67+
task name do
68+
sh *["exe/rbwasm", "build", "--ruby-version", src, "--target", target, "--build-profile", profile, "-o", "/dev/null"]
15969
end
16070
end
16171

lib/ruby_wasm/cli.rb

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ def build(args)
4545
save_temps: false,
4646
optimize: false,
4747
remake: false,
48+
ruby_version: "3.3",
4849
target_triplet: "wasm32-unknown-wasi",
50+
profile: "full",
4951
stdlib: true
5052
}
5153
OptionParser
@@ -60,10 +62,18 @@ def build(args)
6062
options[:save_temps] = true
6163
end
6264

65+
opts.on("--ruby-version VERSION", "Ruby version") do |version|
66+
options[:ruby_version] = version
67+
end
68+
6369
opts.on("--target TRIPLET", "Target triplet") do |triplet|
6470
options[:target_triplet] = triplet
6571
end
6672

73+
opts.on("--build-profile PROFILE", "Build profile. full or minimal") do |profile|
74+
options[:profile] = profile
75+
end
76+
6777
opts.on("--optimize", "Optimize the output") do
6878
options[:optimize] = true
6979
end
@@ -85,28 +95,54 @@ def build(args)
8595
verbose = RubyWasm.logger.level == :debug
8696
executor = RubyWasm::BuildExecutor.new(verbose: verbose)
8797

98+
unless options[:output]
99+
@stderr.puts "Output file is not specified"
100+
exit 1
101+
end
102+
88103
require "tmpdir"
89104

90105
if options[:save_temps]
91106
tmpdir = Dir.mktmpdir
92107
self.do_build(executor, tmpdir, options)
93-
@stdout.puts "Temporary files are saved to #{tmpdir}"
108+
@stderr.puts "Temporary files are saved to #{tmpdir}"
94109
exit
95110
else
96111
Dir.mktmpdir { |tmpdir| self.do_build(executor, tmpdir, options) }
97112
end
98113
end
99114

100-
private def do_build(executor, tmp_dir, options)
101-
packager = RubyWasm::Packager.new(tmp_dir, options[:target_triplet])
102-
wasm_bytes = packager.package(executor, options)
103-
@stdout.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}"
104-
if options[:output]
105-
File.binwrite(options[:output], wasm_bytes.pack("C*"))
106-
@stdout.puts "Wrote #{options[:output]}"
115+
private
116+
117+
def build_config(options)
118+
config = {
119+
target: options[:target_triplet],
120+
src: options[:ruby_version],
121+
}
122+
case options[:profile]
123+
when "full"
124+
config[:profile] = RubyWasm::Packager::ALL_DEFAULT_EXTS
125+
when "minimal"
126+
config[:profile] = ""
107127
else
108-
@stderr.puts "Writing to stdout"
128+
RubyWasm.logger.error "Unknown profile: #{options[:profile]} (available: full, minimal)"
129+
exit 1
130+
end
131+
end
132+
133+
def do_build(executor, tmp_dir, options)
134+
if defined?(Bundler)
135+
definition = Bundler.definition
136+
end
137+
packager = RubyWasm::Packager.new(tmp_dir, options[:target_triplet], definition)
138+
wasm_bytes = packager.package(executor, options)
139+
@stderr.puts "Size: #{SizeFormatter.format(wasm_bytes.size)}"
140+
case options[:output]
141+
when "-"
109142
@stdout.write wasm_bytes.pack("C*")
143+
else
144+
File.binwrite(options[:output], wasm_bytes.pack("C*"))
145+
@stderr.puts "Wrote #{options[:output]}"
110146
end
111147
end
112148
end

lib/ruby_wasm/packager.rb

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1+
# A class responsible for packaging whole Ruby project
12
class RubyWasm::Packager
2-
def initialize(dest_dir, target_triplet, definition = Bundler.definition)
3+
# Initializes a new instance of the RubyWasm::Packager class.
4+
#
5+
# @param dest_dir [String] The destination used to construct the filesystem.
6+
# @param config [Hash] The build config used for building Ruby.
7+
# @param definition [Bundler::Definition] The Bundler definition.
8+
def initialize(dest_dir, config = nil, definition = nil)
39
@dest_dir = dest_dir
410
@definition = definition
5-
@target_triplet = target_triplet
11+
@config = config
612
end
713

14+
# Packages the Ruby code into a Wasm binary. (including extensions)
15+
#
16+
# @param executor [RubyWasm::BuildExecutor] The executor for building the Wasm binary.
17+
# @param options [Hash] The packaging options.
18+
# @return [Array<Integer>] The bytes of the packaged Wasm binary.
819
def package(executor, options)
920
ruby_core = RubyWasm::Packager::Core.new(self)
1021
tarball = ruby_core.build(executor, options)
@@ -29,42 +40,111 @@ def package(executor, options)
2940
wasm_bytes
3041
end
3142

43+
# The list of excluded gems from the Bundler definition.
3244
EXCLUDED_GEMS = %w[ruby_wasm bundler]
3345

46+
# Retrieves the specs from the Bundler definition, excluding the excluded gems.
3447
def specs
48+
return [] unless @definition
3549
@definition.specs.reject { |spec| EXCLUDED_GEMS.include?(spec.name) }
3650
end
3751

52+
# Checks if dynamic linking is supported.
3853
def support_dynamic_linking?
3954
@ruby_channel == "head"
4055
end
4156

57+
# Retrieves the root directory of the Ruby project.
58+
# The root directory contains the following stuff:
59+
# * patches/*.patch
60+
# * build_manifest.json
61+
# * rubies
62+
# * build
4263
def root
4364
@root ||=
4465
begin
4566
if explicit = ENV["RUBY_WASM_ROOT"]
4667
File.expand_path(explicit)
47-
else
68+
elsif defined?(Bundler)
4869
Bundler.root
70+
else
71+
Dir.pwd
4972
end
5073
rescue Bundler::GemfileNotFound
5174
Dir.pwd
5275
end
5376
end
5477

55-
def build_options
56-
{
57-
target: @target_triplet,
58-
src: {
59-
name: "3.3",
78+
# Retrieves the alias definitions for the Ruby sources.
79+
def build_source_aliases
80+
patches = Dir[File.join(root, "patches", "*.patch")]
81+
sources = {
82+
"head" => {
83+
type: "github",
84+
repo: "ruby/ruby",
85+
rev: "master",
86+
patches: patches.map { |p| File.expand_path(p) }
87+
},
88+
"3.3" => {
6089
type: "tarball",
6190
url: "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.0.tar.gz"
6291
},
63-
default_exts:
64-
"bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl",
65-
toolchain: RubyWasm::Toolchain.get(@target_triplet),
66-
build_dir: File.join(root, "build"),
67-
rubies_dir: File.join(root, "rubies")
92+
"3.2" => {
93+
type: "tarball",
94+
url: "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.2.tar.gz"
95+
}
96+
}
97+
sources.each do |name, source|
98+
source[:name] = name
99+
end
100+
101+
build_manifest = File.join(root, "build_manifest.json")
102+
if File.exist?(build_manifest)
103+
begin
104+
manifest = JSON.parse(File.read(build_manifest))
105+
manifest["ruby_revisions"].each do |name, rev|
106+
sources[name][:rev] = rev
107+
end
108+
rescue StandardError => e
109+
RubyWasm.logger.warn "Failed to load build_manifest.json: #{e}"
110+
end
111+
end
112+
sources
113+
end
114+
115+
ALL_DEFAULT_EXTS = "bigdecimal,cgi/escape,continuation,coverage,date,dbm,digest/bubblebabble,digest,digest/md5,digest/rmd160,digest/sha1,digest/sha2,etc,fcntl,fiber,gdbm,json,json/generator,json/parser,nkf,objspace,pathname,psych,racc/cparse,rbconfig/sizeof,ripper,stringio,strscan,monitor,zlib,openssl"
116+
117+
# Retrieves the build options used for building Ruby itself.
118+
def build_options
119+
default = {
120+
target: "wasm32-unknown-wasi",
121+
src: "3.3",
122+
default_exts: ALL_DEFAULT_EXTS,
68123
}
124+
override = {}
125+
if @config
126+
override = @config["build_options"] || {}
127+
end
128+
# Merge the default options with the config options
129+
default.merge(override)
130+
end
131+
132+
# Retrieves the resolved build options
133+
def full_build_options
134+
options = build_options
135+
toolchain = RubyWasm::Toolchain.get(options[:target])
136+
build_dir = File.join(root, "build")
137+
rubies_dir = File.join(root, "rubies")
138+
src = if options[:src].is_a?(Hash)
139+
options[:src]
140+
else
141+
src_name = options[:src]
142+
aliases = build_source_aliases
143+
aliases[src_name] || raise("Unknown Ruby source: #{src_name} (available: #{aliases.keys.join(", ")})")
144+
end
145+
options.merge(
146+
toolchain: toolchain, build_dir: build_dir,
147+
rubies_dir: rubies_dir, src: src
148+
)
69149
end
70150
end

0 commit comments

Comments
 (0)