Skip to content

Commit 7660c13

Browse files
Split out Component binaries into @ruby/wasm-wasip2 package
The `@ruby/wasm-wasi` package included both the Core Module and the Component binaries but it has two problems: 1. npm package size was too large and it was very close to the limit of the jsdelivr CDN. 2. The Component model build (in other words, dynamic linking build) well supports incremental build. But the Core Module build does not because it requires to re-link C-extensions every time statically. And it led to clean build every time. Splitting out the Component build makes the p2 package itself incremental buildable.
1 parent 71c25d8 commit 7660c13

File tree

14 files changed

+225
-48
lines changed

14 files changed

+225
-48
lines changed

Rakefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ NPM_PACKAGES = [
3030
ruby_version: "head",
3131
gemfile: "packages/npm-packages/ruby-head-wasm-wasi/Gemfile",
3232
target: "wasm32-unknown-wasip1",
33+
},
34+
{
35+
name: "ruby-head-wasm-wasip2",
36+
ruby_version: "head",
37+
gemfile: "packages/npm-packages/ruby-head-wasm-wasip2/Gemfile",
38+
target: "wasm32-unknown-wasip2",
3339
enable_component_model: true,
3440
},
3541
{

packages/npm-packages/ruby-head-wasm-wasi/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
[![npm version](https://badge.fury.io/js/@ruby%2Fhead-wasm-wasi.svg)](https://www.npmjs.com/package/@ruby/head-wasm-wasi)
44

5-
This package provides WebAssembly binaries of CRuby built from the latest `HEAD` source code targeting WASI-compatible environments.
5+
This package provides WebAssembly binaries of CRuby built from the latest `HEAD` source code targeting environments compatible with WASI Preview1.
66

77
See [`@ruby/wasm-wasi`](https://github.com/ruby/ruby.wasm/blob/main/packages/npm-packages/ruby-wasm-wasi/README.md) for how to use this package.

packages/npm-packages/ruby-head-wasm-wasi/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"README.md"
3030
],
3131
"scripts": {
32-
"test": "RUBY_NPM_PACKAGE_ROOT=../ruby-head-wasm-wasi npm -C ../ruby-wasm-wasi run test:run:all",
32+
"test": "RUBY_NPM_PACKAGE_ROOT=../ruby-head-wasm-wasi npm -C ../ruby-wasm-wasi run test:run",
3333
"build:deps": "cd ../ruby-wasm-wasi && npm run build",
3434
"build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist",
3535
"build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-head-wasm-wasi",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.tgz
2+
/tmp
3+
/bundle
4+
/vendor
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
source "https://rubygems.org"
4+
5+
# We build ./vendor/cache/js-{version}.gem just before evaluating this Gemfile
6+
# so that Bundler builds extensions even from the local gem. (gem extensions
7+
# from "path:" gems are not built by Bundler.)
8+
# Thus even we specify version of "js" gem here, it should always installed
9+
# from the ./vendor/cache/js-{version}.gem, not from rubygems.org. To achieve this,
10+
# we always use non-exist version during development.
11+
require_relative "../../gems/js/lib/js/version.rb"
12+
gem "js", JS::VERSION
13+
gem "ruby_wasm", path: "../../../", group: [:build]
14+
gem "power_assert"
15+
gem "test-unit"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
PATH
2+
remote: ../../..
3+
specs:
4+
ruby_wasm (2.6.2.dev)
5+
6+
GEM
7+
remote: https://rubygems.org/
8+
specs:
9+
js (2.6.2.dev)
10+
power_assert (2.0.3)
11+
test-unit (3.6.2)
12+
power_assert
13+
14+
PLATFORMS
15+
ruby
16+
x86_64-linux
17+
18+
DEPENDENCIES
19+
js (= 2.6.2.dev)
20+
power_assert
21+
ruby_wasm!
22+
test-unit
23+
24+
BUNDLED WITH
25+
2.6.0.dev
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# @ruby/head-wasm-wasip2
2+
3+
[![npm version](https://badge.fury.io/js/@ruby%2Fhead-wasm-wasip2.svg)](https://www.npmjs.com/package/@ruby/head-wasm-wasip2)
4+
5+
This package provides WebAssembly binaries of CRuby built from the latest `HEAD` source code targeting environments compatible with WASI Preview2.
6+
7+
See [`@ruby/wasm-wasi`](https://github.com/ruby/ruby.wasm/blob/main/packages/npm-packages/ruby-wasm-wasi/README.md) for how to use this package.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"name": "@ruby/head-wasm-wasip2",
3+
"version": "2.6.2",
4+
"description": "Ruby head built on WASI Preview 2",
5+
"main": "./dist/cjs/index.js",
6+
"module": "./dist/esm/index.js",
7+
"exports": {
8+
".": {
9+
"browser": "./dist/esm/index.js",
10+
"umd": "./dist/umd/index.js",
11+
"import": "./dist/esm/index.js",
12+
"require": "./dist/cjs/index.js"
13+
},
14+
"./dist/*": {
15+
"browser": "./dist/esm/*.js",
16+
"umd": "./dist/umd/*.js",
17+
"import": "./dist/esm/*.js",
18+
"require": "./dist/cjs/*.js"
19+
},
20+
"./*.wasm": {
21+
"browser": "./*.wasm",
22+
"umd": "./*.wasm",
23+
"import": "./*.wasm",
24+
"require": "./*.wasm"
25+
}
26+
},
27+
"files": [
28+
"dist",
29+
"README.md"
30+
],
31+
"scripts": {
32+
"test": "RUBY_NPM_PACKAGE_ROOT=../ruby-head-wasm-wasi ENABLE_COMPONENT_TESTS=1 npm -C ../ruby-wasm-wasi run test:run",
33+
"build:deps": "cd ../ruby-wasm-wasi && npm run build",
34+
"build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist",
35+
"build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-head-wasm-wasi",
36+
"build:static": "npm run build:static:files && npm run build:static:compat",
37+
"build:rollup": "rollup -c rollup.config.mjs",
38+
"build": "npm run build:deps && npm run build:static && npm run build:rollup && ../ruby-wasm-wasi/tools/post-build.sh ./dist"
39+
},
40+
"repository": "https://github.com/ruby/ruby.wasm",
41+
"homepage": "https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-head-wasm-wasi",
42+
"publishConfig": {
43+
"access": "public"
44+
},
45+
"keywords": [
46+
"wasm",
47+
"webassembly",
48+
"wasi",
49+
"ruby"
50+
],
51+
"license": "MIT",
52+
"dependencies": {
53+
"@ruby/wasm-wasi": "^2.0.0",
54+
"@bytecodealliance/preview2-shim": "^0.16.5"
55+
}
56+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import json from "@rollup/plugin-json";
2+
import { nodeResolve } from "@rollup/plugin-node-resolve";
3+
import fs from "fs";
4+
import path from "path";
5+
6+
/** @type {import('rollup').RollupOptions[]} */
7+
export default [
8+
{
9+
input: "src/browser.script.js",
10+
output: [
11+
{
12+
file: "dist/browser.script.iife.js",
13+
format: "iife",
14+
banner: "/* " + fs.readFileSync(path.resolve("../../../NOTICE"), "utf8") + "*/",
15+
}
16+
],
17+
plugins: [
18+
json(), nodeResolve()
19+
],
20+
},
21+
];
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { instantiate } from "../dist/component/ruby.component"
2+
import { componentMain } from "@ruby/wasm-wasi/dist/browser.script"
3+
import * as wasip2 from "@bytecodealliance/preview2-shim"
4+
import * as pkg from "../package.json"
5+
6+
componentMain(pkg, { instantiate, wasip2 })

packages/npm-packages/ruby-wasm-wasi/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
],
4040
"license": "MIT",
4141
"scripts": {
42-
"test:run:all": "npm run test:run && ENABLE_COMPONENT_TESTS=1 npm run test:run",
4342
"test:run": "npm run test:unit && npm run test:vitest -- --run && npm run test:e2e",
4443
"test:vitest": "vitest ./test/",
4544
"test:unit": "./tools/run-test-unit.mjs",

packages/npm-packages/ruby-wasm-wasi/src/browser.script.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { DefaultRubyVM } from "./browser.js";
2+
import { RubyInitComponentOptions, RubyComponentInstantiator, RubyVM } from "./vm.js";
23

4+
/**
5+
* The main entry point of `<script type="text/ruby">`-based scripting with WebAssembly Core Module.
6+
*/
37
export const main = async (
48
pkg: { name: string; version: string },
59
options?: Parameters<typeof DefaultRubyVM>[1],
@@ -9,7 +13,32 @@ export const main = async (
913
);
1014
const module = await compileWebAssemblyModule(response);
1115
const { vm } = await DefaultRubyVM(module, options);
16+
await mainWithRubyVM(vm);
17+
};
18+
19+
/**
20+
* The main entry point of `<script type="text/ruby">`-based scripting with WebAssembly Component.
21+
*/
22+
export const componentMain = async (
23+
pkg: { name: string; version: string },
24+
options: {
25+
instantiate: RubyComponentInstantiator;
26+
wasip2: any;
27+
}
28+
) => {
29+
const componentUrl = `https://cdn.jsdelivr.net/npm/${pkg.name}@${pkg.version}/dist/component`;
30+
const fetchComponentFile = (relativePath: string) => fetch(`${componentUrl}/${relativePath}`);
31+
const { vm } = await RubyVM.instantiateComponent({
32+
...options,
33+
getCoreModule: (relativePath: string) => {
34+
const response = fetchComponentFile(relativePath);
35+
return compileWebAssemblyModule(response);
36+
},
37+
});
38+
await mainWithRubyVM(vm);
39+
};
1240

41+
const mainWithRubyVM = async (vm: RubyVM) => {
1342
vm.printVersion();
1443

1544
globalThis.rubyVM = vm;

packages/npm-packages/ruby-wasm-wasi/src/vm.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ import {
99
} from "./bindgen/legacy/rb-js-abi-host.js";
1010
import { Binding, ComponentBinding, LegacyBinding, RbAbiValue } from "./binding.js";
1111

12+
/**
13+
* A function type that instantiates a Ruby component
14+
*/
15+
export type RubyComponentInstantiator = (
16+
(
17+
getCoreModule: (path: string) => WebAssembly.Module,
18+
importObject: any,
19+
instantiateCore?: (module: WebAssembly.Module, imports: Record<string, any>) => WebAssembly.Instance | Promise<WebAssembly.Instance>,
20+
) => Promise<({ rubyRuntime: typeof RubyJsRubyRuntime })>
21+
)
22+
1223
export type RubyInitComponentOptions = {
1324
/**
1425
* A lower-level instantiation function that instantiates the Ruby component with the given component
@@ -26,13 +37,7 @@ export type RubyInitComponentOptions = {
2637
/**
2738
* An `instantiate` function generated by `@bytecodealliance/jco` that instantiates the Ruby component.
2839
*/
29-
instantiate: (
30-
(
31-
getCoreModule: (path: string) => WebAssembly.Module,
32-
importObject: any,
33-
instantiateCore?: (module: WebAssembly.Module, imports: Record<string, any>) => WebAssembly.Instance | Promise<WebAssembly.Instance>,
34-
) => Promise<({ rubyRuntime: typeof RubyJsRubyRuntime })>
35-
),
40+
instantiate: RubyComponentInstantiator,
3641

3742
/**
3843
* A function that returns a WebAssembly Core module within the Ruby component transpiled by `@bytecodealliance/jco`.

rakelib/packaging.rake

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ namespace :npm do
6969
# Share ./build and ./rubies in the same workspace
7070
"RUBY_WASM_ROOT" => base_dir
7171
}
72-
cwd = nil
72+
cwd = base_dir
7373
if gemfile_path = pkg[:gemfile]
7474
cwd = File.dirname(gemfile_path)
7575
else
@@ -80,49 +80,53 @@ namespace :npm do
8080
dist_dir = File.join(pkg_dir, "dist")
8181
mkdir_p dist_dir
8282
if pkg[:target].start_with?("wasm32-unknown-wasi")
83-
Dir.chdir(cwd || base_dir) do
84-
# Uninstall js gem to re-install just-built js gem
85-
sh "gem", "uninstall", "js", "-v", js_gem_version, "--force"
86-
# Install gems including js gem
87-
sh "bundle", "install"
88-
89-
sh env,
90-
"bundle", "exec",
91-
*build_command,
92-
"--no-stdlib", "--remake",
93-
"-o",
94-
File.join(dist_dir, "ruby.wasm")
95-
sh env,
96-
"bundle", "exec",
97-
*build_command,
98-
"-o",
99-
File.join(dist_dir, "ruby.debug+stdlib.wasm")
100-
if pkg[:enable_component_model]
101-
component_path = File.join(pkg_dir, "tmp", "ruby.component.wasm")
102-
FileUtils.mkdir_p(File.dirname(component_path))
83+
if pkg[:enable_component_model]
84+
component_path = File.join(pkg_dir, "tmp", "ruby.component.wasm")
85+
FileUtils.mkdir_p(File.dirname(component_path))
10386

104-
# Remove js gem from the ./bundle directory to force Bundler to re-install it
105-
rm_rf FileList[File.join(pkg_dir, "bundle", "**", "js-#{js_gem_version}")]
87+
# Remove js gem from the ./bundle directory to force Bundler to re-install it
88+
rm_rf FileList[File.join(pkg_dir, "bundle", "**", "js-#{js_gem_version}")]
10689

90+
Dir.chdir(cwd) do
10791
sh env.merge("RUBY_WASM_EXPERIMENTAL_DYNAMIC_LINKING" => "1"),
10892
*build_command, "-o", component_path
109-
sh "npx", "jco", "transpile",
110-
"--no-wasi-shim", "--instantiation", "--valid-lifting-optimization",
111-
component_path, "-o", File.join(dist_dir, "component")
112-
# ./component/package.json is required to be an ES module
113-
File.write(File.join(dist_dir, "component", "package.json"), '{ "type": "module" }')
11493
end
94+
sh "npx", "jco", "transpile",
95+
"--no-wasi-shim", "--instantiation", "--valid-lifting-optimization",
96+
component_path, "-o", File.join(dist_dir, "component")
97+
# ./component/package.json is required to be an ES module
98+
File.write(File.join(dist_dir, "component", "package.json"), '{ "type": "module" }')
99+
else
100+
Dir.chdir(cwd) do
101+
# uninstall js gem to re-install just-built js gem
102+
sh "gem", "uninstall", "js", "-v", js_gem_version, "--force"
103+
# install gems including js gem
104+
sh "bundle", "install"
105+
106+
sh env,
107+
"bundle", "exec",
108+
*build_command,
109+
"--no-stdlib", "--remake",
110+
"-o",
111+
File.join(dist_dir, "ruby.wasm")
112+
sh env,
113+
"bundle", "exec",
114+
*build_command,
115+
"-o",
116+
File.join(dist_dir, "ruby.debug+stdlib.wasm")
117+
end
118+
119+
sh wasi_sdk.wasm_opt,
120+
"--strip-debug",
121+
File.join(dist_dir, "ruby.wasm"),
122+
"-o",
123+
File.join(dist_dir, "ruby.wasm")
124+
sh wasi_sdk.wasm_opt,
125+
"--strip-debug",
126+
File.join(dist_dir, "ruby.debug+stdlib.wasm"),
127+
"-o",
128+
File.join(dist_dir, "ruby+stdlib.wasm")
115129
end
116-
sh wasi_sdk.wasm_opt,
117-
"--strip-debug",
118-
File.join(dist_dir, "ruby.wasm"),
119-
"-o",
120-
File.join(dist_dir, "ruby.wasm")
121-
sh wasi_sdk.wasm_opt,
122-
"--strip-debug",
123-
File.join(dist_dir, "ruby.debug+stdlib.wasm"),
124-
"-o",
125-
File.join(dist_dir, "ruby+stdlib.wasm")
126130
elsif pkg[:target] == "wasm32-unknown-emscripten"
127131
Dir.chdir(cwd || base_dir) do
128132
sh env, *build_command, "-o", "/dev/null"

0 commit comments

Comments
 (0)