Buck2 rules for building WebAssembly components.
See also similar rules for bazel: https://github.com/pulseengine/rules_wasm_component
Probably the easiest way of getting buck2 is via dotslash (https://dotslash-cli.com). This will also ensure using the buck2 version that the rules were tested against.
cargo binstall dotslash
#Run hello world example
./buck2 run //examples/hello:run
# Run the validator example
./buck2 run //examples/validator/runner:validator -- "Hello World"
# Run the http example
./buck2 run //examples/http/runner:runnerRust dependencies are resolved in third-party/Cargo.toml and translated to
Buck targets in third-party/BUCK with a pinned Reindeer binary:
./reindeer buckify- Component Building: Create WASM components from Rust, C, and C++
- WIT Bindings: Generate language bindings from WIT definitions
- Composition: Link, plug, and compose components together (via WAC)
- Package Management: Fetch components from WASM registries
- Optimization: Run wasm-opt (Binaryen) on modules
- Partial Evaluation: Specialize WASM interpreters into compiled code (via weval)
- Pre-initialization: Snapshot module initialization state (via wasmtime wizer)
- JavaScript Components: Build components from JavaScript (via jco)
load("@toolchains//wasm:component.bzl", "wasm_component")
# Build the Rust binary with WASM target
rust_binary(
name = "regex",
crate = "regex",
crate_root = "src/lib.rs",
edition = "2024",
srcs = glob(["src/**/*.rs"]) + glob(["wit/**/*.wit"]),
deps = [
"//third-party:regex",
"//third-party:wit-bindgen",
],
)
# Promote output of regex to wasm component
wasm_component(
name = "regex_component",
module = ":regex",
wit = "wit/regex.wit",
visibility = ['PUBLIC'],
)By default, wasm_component builds the module as WASI Preview 2 and runs
wasm-tools component new without a Preview 1 adapter. Setting adapter
selects a WASI Preview 1 adapter and configures the module as wasip1 unless
you set wasi explicitly:
wasm_component(
name = "cli_component",
module = ":cli",
adapter = "command",
)If the dependency already produces a component, use component instead of
module so downstream rules receive WasmInfo without re-running
component new.
For WASI Preview 2 components, and newer component-shaped worlds such as
WASI Preview 3 test components, omit adapter. The adapter attribute is
only for adapting WASI Preview 1 core modules before componentization.
wasm_component- Create a component from a WASM module, or declare an existing componentwasm_component_link- Link multiple componentswasm_plug- Compose components using plug patternwasm_compose- Compose components using WAC composition fileswasm_validate- Validate WASM binarieswasm_print- Convert WASM to text formatwasm_opt- Optimize WASM modules with Binaryenwasm_weval- Partially evaluate WASM modules with wevalwasm_wizer- Pre-initialize WASM modules with wasmtime wizer
wit_bindgen_rust- Generate Rust bindingswit_bindgen_c- Generate C bindingswit_bindgen_cxx- Generate C++ bindingswit_to_markdown- Generate documentation
WIT inputs can be files, directories, or dependencies. Dependencies' WasmInfo.wit
and WitBindingInfo.wit metadata takes precedence over their default outputs, so
generated source files are not mistaken for WIT. Repeated inputs are deduplicated.
Version-qualified worlds such as test:api/api@1.2.3 are supported. The C generator
supports string_encoding = "utf8" or "utf16"; latin1 and compact-utf16 are
not supported by the pinned generator. wit_to_markdown produces a directory
containing the generated documentation.
wasm_componentize_js- Build component from JavaScript (via jco)wasm_jco_run- Run a WASI command component withjco run
wasm_package- Download packages from registrieswit_library- Define a WIT library with automatic dependency resolution (via wkg)
wasm_package requires an exact @version or expected_sha256 digest by default.
Version ranges do not pin a package. allow_unpinned = True explicitly opts out
of this reproducibility check.
wasm_package and wasm_compose require toolchains//:python_bootstrap, included
in the external-cell setup below.
Composition requires explicit dependencies. Replace wasm_plug's old
socket_registry and plugs_registry arguments with pinned wasm_package
targets passed through socket and plugs. Every external package referenced
by a wasm_compose WAC file must appear in deps; composition will not look up
undeclared registry packages or workspace dependencies. Version-qualified keys
such as "test:plug@1.2.3" take precedence over unversioned keys.
# Regex component (Rust)
rust_binary(
name = "regex_lib",
srcs = glob(["src/**/*.rs"]),
)
wasm_component(
name = "regex_component",
module = ":regex_lib",
wit = "wit/regex.wit",
)
# Validator component (C++)
wit_bindgen_cxx(
name = "validator_bindings",
world = "validator",
wit = ["wit/validator.wit"],
)
cxx_binary(
name = "validator",
srcs = ["src/validator.cpp"],
deps = [":validator_bindings"],
_cxx_toolchain = "toolchains//:cxx_wasi_p1",
)
wasm_opt(
name = "validator_opt",
input = ":validator",
optimization = "os",
)
wasm_component(
name = "validator_component",
module = ":validator_opt",
adapter = "reactor",
wit = "wit/validator.wit",
)
# Compose: plug regex into validator
wasm_plug(
name = "app",
socket = ":validator_component",
plugs = [":regex_component"],
)Most toolchains are downloaded and checksum-pinned rather than relying on system installations. The jco and AssemblyScript setup paths pin npm package versions and install them on the selected execution platform, which must have registry access. Their npm workspaces and native Node distribution must match that platform's OS and architecture. For jco, supply package_json and package_lock together to use npm ci instead of the default npm install. Explicit system/preinstalled setups are also supported.
Downloaded binary toolchains are composed of two parts:
- Distribution: Downloads and extracts the tool binary
- Toolchain: Provides commands and subcommands through
RunInfo
Native subcommands compose directly with cmd_args; shell launchers are only
needed where platform dispatch requires them, such as Windows .cmd tools.
This separation makes it easy to control tool sources and versions:
# Download the distribution
download_wasm_tools(
name = "wasm_tools_dist",
version = "1.259.0",
)
# Create toolchain from distribution
wasm_tools_toolchain(
name = "wasm_tools",
distribution = ":wasm_tools_dist",
visibility = ["PUBLIC"],
)Available toolchains:
- wasm-tools - Component manipulation (https://github.com/bytecodealliance/wasm-tools)
- wit-bindgen - Binding generation (https://github.com/bytecodealliance/wit-bindgen)
- wac - Component composition (https://github.com/bytecodealliance/wac)
- wkg - Package management (https://github.com/bytecodealliance/wasm-pkg-tools)
- wasi-sdk - C/C++ wasi toolchain (https://github.com/WebAssembly/wasi-sdk)
- binaryen - WASM optimizer / wasm-opt (https://github.com/WebAssembly/binaryen)
- jco - JavaScript component toolchain (pinned npm install or system install, https://github.com/bytecodealliance/jco)
- weval - WASM partial evaluator (opt-in, https://github.com/bytecodealliance/weval)
- wasmtime - WASM runtime, also provides wizer pre-initialization (https://github.com/bytecodealliance/wasmtime)
Wasmono can be used as a git external cell in other Buck2 projects.
[cells]
root = .
wasmono = wasmono
toolchains = toolchains
prelude = prelude
none = none
[cell_aliases]
config = prelude
ovr_config = prelude
fbcode = none
fbsource = none
fbcode_macros = none
buck = none
[external_cells]
prelude = bundled
wasmono = git
[external_cell_wasmono]
git_origin = https://github.com/andreiltd/wasmono.git
commit_hash = <sha1>
[build]
execution_platforms = prelude//platforms:default
[parser]
target_platform_detector_spec = target:root//...->prelude//platforms:defaultCreate an empty none/BUCK file (required by cell aliases).
The wasmono rules reference toolchains//:wasm_tools, toolchains//:wit_bindgen, etc.
You must define these targets in your own toolchains/BUCK. The wasm_demo_toolchains()
macro creates all WASM toolchain targets with sensible defaults:
load("@prelude//toolchains:cxx.bzl", "system_cxx_toolchain")
load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")
load("@prelude//toolchains:rust.bzl", "system_rust_toolchain")
load("@wasmono//:defs.bzl", "cxx_wasi_toolchain", "download_wasi_sdk", "wasm_demo_toolchains")
_DEFAULT_TRIPLE = select({
"config//os:wasi": select({
"config//cpu:wasm32": select({
"wasmono//wasm/constraints:wasip1": "wasm32-wasip1",
"DEFAULT": "wasm32-wasip2",
}),
}),
"config//os:linux": select({
"config//cpu:arm64": "aarch64-unknown-linux-gnu",
"config//cpu:x86_64": "x86_64-unknown-linux-gnu",
}),
"config//os:macos": select({
"config//cpu:arm64": "aarch64-apple-darwin",
"config//cpu:x86_64": "x86_64-apple-darwin",
}),
})
system_genrule_toolchain(name = "genrule", visibility = ["PUBLIC"])
system_cxx_toolchain(name = "cxx", visibility = ["PUBLIC"])
system_python_bootstrap_toolchain(name = "python_bootstrap", visibility = ["PUBLIC"])
system_rust_toolchain(
name = "rust",
default_edition = "2024",
rustc_target_triple = _DEFAULT_TRIPLE,
visibility = ["PUBLIC"],
)
# WASM toolchains
wasm_demo_toolchains()
download_wasi_sdk(name = "wasi_sdk", version = "34.0")
cxx_wasi_toolchain(name = "cxx_wasi", distribution = ":wasi_sdk", visibility = ["PUBLIC"])
cxx_wasi_toolchain(
name = "cxx_wasi_p1",
distribution = ":wasi_sdk",
target = "wasm32-wasip1",
visibility = ["PUBLIC"],
)platform(
name = "wasm32_wasi",
constraint_values = [
"wasmono//wasm/constraints:wasm32",
"config//os/constraints:wasi",
],
)The Wasmono wasm32 constraint preserves the consuming Prelude's CPU identity,
including newer releases that expose it as cpu[wasm32] rather than a standalone target.
load("@wasmono//:defs.bzl", "wasm_component", "wasm_compose")You can still load implementation modules such as
@wasmono//toolchains/wasm:component.bzl directly, but @wasmono//:defs.bzl
is the stable public entrypoint for user-facing rules and setup macros.
It also exports lower-level setup helpers such as download_node,
download_wasm_tools, host_arch, host_os, and wasm_transition_p1 for
repositories that need more granular toolchain wiring than
wasm_demo_toolchains().
By default, download_* functions and wasm_demo_toolchains() look up tool
versions from the built-in release dictionaries shipped with wasmono. If you
need a version that isn't included yet — or want to use a dev/nightly build —
you can supply your own release data via the releases parameter.
Custom releases overlay the built-in releases: entries you provide take precedence, while built-in versions remain available as fallback.
Create a single releases file in your repo:
# my_releases.bzl
my_releases = {
"wasm_tools": {
"1.250.0": {
"x86_64-linux": {
"url": "<url>/wasm-tools-1.250.0-x86_64-linux.tar.gz",
"shasum": "<sha256>",
},
"aarch64-macos": {
"url": "<url>/wasm-tools-1.250.0-aarch64-macos.tar.gz",
"shasum": "<sha256>",
},
# ... add platforms you need
},
},
}Then pass it to wasm_demo_toolchains():
load(":my_releases.bzl", "my_releases")
load("@wasmono//toolchains/wasm:demo.bzl", "wasm_demo_toolchains")
wasm_demo_toolchains(
wasm_tools_version = "1.259.0",
releases = my_releases,
)The http example is port of great p3 demo from https://github.com/ejrgilbert/component-interposition