Skip to content

andreiltd/wasmono

Repository files navigation

Wasmono

Buck2 rules for building WebAssembly components.

See also similar rules for bazel: https://github.com/pulseengine/rules_wasm_component

Quick Start

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:runner

Regenerate Rust third-party rules

Rust dependencies are resolved in third-party/Cargo.toml and translated to Buck targets in third-party/BUCK with a pinned Reindeer binary:

./reindeer buckify

Features

  • 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)

Build a Component

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.

Available Rules

Component Rules

  • wasm_component - Create a component from a WASM module, or declare an existing component
  • wasm_component_link - Link multiple components
  • wasm_plug - Compose components using plug pattern
  • wasm_compose - Compose components using WAC composition files
  • wasm_validate - Validate WASM binaries
  • wasm_print - Convert WASM to text format
  • wasm_opt - Optimize WASM modules with Binaryen
  • wasm_weval - Partially evaluate WASM modules with weval
  • wasm_wizer - Pre-initialize WASM modules with wasmtime wizer

Binding Generation

  • wit_bindgen_rust - Generate Rust bindings
  • wit_bindgen_c - Generate C bindings
  • wit_bindgen_cxx - Generate C++ bindings
  • wit_to_markdown - Generate documentation

JavaScript

  • wasm_componentize_js - Build component from JavaScript (via jco)
  • wasm_jco_run - Run a WASI command component with jco run

Package Management

  • wasm_package - Download packages from registries
  • wit_library - Define a WIT library with automatic dependency resolution (via wkg)

Example: Multi-Component App

# 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"],
)

Toolchains

Most toolchains are downloaded and checksum-pinned rather than relying on system installations. The jco and AssemblyScript setup paths pin npm package versions but run npm install as local-only Buck actions, so they need network access unless you use an explicit system/preinstalled setup.

Downloaded binary toolchains are composed of two parts:

  1. Distribution: Downloads and extracts the tool binary
  2. Toolchain: Provides convenient wrappers and subcommands

This separation makes it easy to control tool sources and versions:

# Download the distribution
download_wasm_tools(
    name = "wasm_tools_dist",
    version = "1.239.0",
)

# Create toolchain from distribution
wasm_tools_toolchain(
    name = "wasm_tools",
    distribution = ":wasm_tools_dist",
    visibility = ["PUBLIC"],
)

Available toolchains:

Using as an External Cell

Wasmono can be used as a git external cell in other Buck2 projects.

1. Configure .buckconfig

[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:default

Create an empty none/BUCK file (required by cell aliases).

2. Set up toolchains/BUCK

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 = "27.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"],
)

3. Add platforms/BUCK

platform(
    name = "wasm32_wasi",
    constraint_values = [
        "config//cpu/constraints:wasm32",
        "config//os/constraints:wasi",
    ],
)

4. Use the rules

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().

Custom Tool Releases

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.

Example: Using a newer wasm-tools version

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.250.0",
    releases = my_releases,
)

Acknowledgments

The http example is port of great p3 demo from https://github.com/ejrgilbert/component-interposition

About

Buck2 rules for building WebAssembly components

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors