From ac405fd52ab31ba872757952f0a43c249c266bfa Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Sun, 14 Jun 2026 13:05:11 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(wasm):=20wasm=5Fcomponent=5Fimport=20?= =?UTF-8?q?=E2=80=94=20adopt=20a=20prebuilt=20.wasm=20component=20(#489)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a first-class rule to bring an already-built component (fetched release asset, vendored, or committed fixture) into the build graph as a WasmComponentInfo, so wasm_validate / wasm_optimize / meld_fuse / synth_compile / signing can consume it without rebuilding from source. Per the issue direction: - returns WasmComponentInfo (wasm_file set, component_type "component", profile "release", empty profile_variants, wit_info None) - optional caller-declared imports/exports (default empty — does not hard-fail on a stripped/opaque component) - wired into wasm/defs.bzl exports + a bzl_library in wasm/private/BUILD.bazel - examples/component_import_example: adopt-then-validate build_test Replaces the downstream adopt_wasm_component shim consumers (e.g. jess) carry. Closes #489 (item 1; item 2 synth_compile already landed in #468/v1.1.0). --- examples/component_import_example/BUILD.bazel | 32 +++++++ wasm/defs.bzl | 5 ++ wasm/private/BUILD.bazel | 10 +++ wasm/private/wasm_component_import.bzl | 88 +++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 examples/component_import_example/BUILD.bazel create mode 100644 wasm/private/wasm_component_import.bzl diff --git a/examples/component_import_example/BUILD.bazel b/examples/component_import_example/BUILD.bazel new file mode 100644 index 00000000..32888035 --- /dev/null +++ b/examples/component_import_example/BUILD.bazel @@ -0,0 +1,32 @@ +"""Example: adopt a prebuilt .wasm component, then validate/optimize it. + +`wasm_component_import` brings an already-built component (fetched release +asset, vendored artifact, or — here — a committed fixture) into the build graph +as a `WasmComponentInfo`, so the downstream rules can consume it without +rebuilding from source. See rules_wasm_component#489. + +The fixture here is a committed `.wasm` used purely as a stand-in for a +prebuilt artifact; in a real pipeline `wasm` would point at an `http_file` +release asset (e.g. a flight-control component). +""" + +load("@bazel_skylib//rules:build_test.bzl", "build_test") +load("@rules_wasm_component//wasm:defs.bzl", "wasm_component_import", "wasm_validate") + +wasm_component_import( + name = "adopted_component", + wasm = "prebuilt_component.wasm", +) + +wasm_validate( + name = "adopted_validate", + component = ":adopted_component", +) + +build_test( + name = "component_import_test", + targets = [ + ":adopted_component", + ":adopted_validate", + ], +) diff --git a/wasm/defs.bzl b/wasm/defs.bzl index 2393c343..6b60b5bc 100644 --- a/wasm/defs.bzl +++ b/wasm/defs.bzl @@ -94,6 +94,10 @@ load( _wasm_embed_aot = "wasm_embed_aot", _wasm_extract_aot = "wasm_extract_aot", ) +load( + "//wasm/private:wasm_component_import.bzl", + _wasm_component_import = "wasm_component_import", +) load( "//wasm/private:wasm_optimize.bzl", _wasm_optimize = "wasm_optimize", @@ -157,6 +161,7 @@ wasm_extract_aot = _wasm_extract_aot ssh_keygen = _ssh_keygen # WebAssembly optimization rules +wasm_component_import = _wasm_component_import wasm_optimize = _wasm_optimize binaryen_optimize = _binaryen_optimize diff --git a/wasm/private/BUILD.bazel b/wasm/private/BUILD.bazel index 3a09f646..d3b6c797 100644 --- a/wasm/private/BUILD.bazel +++ b/wasm/private/BUILD.bazel @@ -139,6 +139,16 @@ bzl_library( ], ) +bzl_library( + name = "wasm_component_import", + srcs = ["wasm_component_import.bzl"], + visibility = [ + "//docs:__pkg__", + "//wasm:__pkg__", + ], + deps = ["//providers"], +) + bzl_library( name = "wasm_optimize", srcs = ["wasm_optimize.bzl"], diff --git a/wasm/private/wasm_component_import.bzl b/wasm/private/wasm_component_import.bzl new file mode 100644 index 00000000..865fc659 --- /dev/null +++ b/wasm/private/wasm_component_import.bzl @@ -0,0 +1,88 @@ +"""wasm_component_import rule — adopt a prebuilt .wasm component into the graph. + +Wraps an already-built (fetched, vendored, or released) WebAssembly component +file in a `WasmComponentInfo` so the downstream rules (`wasm_validate`, +`wasm_optimize`, `meld_fuse`, `synth_compile`, signing) can consume it without +rebuilding it from source. + +Motivating use case (rules_wasm_component#489): a consumer adopts a released +component artifact — e.g. a flight-control `.wasm` fetched via `http_file` — and +runs validate / optimize / fuse / compile over it. Before this rule that +required a hand-rolled shim in every consuming repo. +""" + +load("//providers:providers.bzl", "WasmComponentInfo") + +def _wasm_component_import_impl(ctx): + wasm_file = ctx.file.wasm + + metadata = dict(ctx.attr.metadata) + metadata.setdefault("imported", "true") + metadata.setdefault("name", ctx.label.name) + + return [ + DefaultInfo(files = depset([wasm_file])), + WasmComponentInfo( + wasm_file = wasm_file, + # No WIT is recovered from a prebuilt binary here; consumers that + # need typed bindings can supply it out of band. None is the + # documented "no WIT available" value. + wit_info = None, + component_type = "component", + # Optional, caller-declared interface surface. Left empty when the + # caller does not know it — downstream rules must not hard-fail on a + # stripped/opaque adopted component. + imports = ctx.attr.imports, + exports = ctx.attr.exports, + metadata = metadata, + profile = "release", + profile_variants = {}, + ), + ] + +wasm_component_import = rule( + implementation = _wasm_component_import_impl, + doc = """Adopt a prebuilt `.wasm` component as a `WasmComponentInfo` target. + +Use this to bring an external/released component into a rules_wasm_component +build so `wasm_validate`, `wasm_optimize`, `meld_fuse`, `synth_compile`, and the +signing rules can consume it: + +```starlark +load("@rules_wasm_component//wasm:defs.bzl", "wasm_component_import", "wasm_validate") + +wasm_component_import( + name = "falcon_flight", + wasm = "@falcon_flight_wasm//file", # e.g. an http_file-fetched release asset +) + +wasm_validate( + name = "falcon_validate", + component = ":falcon_flight", +) +``` + +`imports`/`exports` are optional and caller-declared; omit them for an opaque +component (downstream rules tolerate empty lists). `wit_info` is always `None` +for an imported binary — supply typed bindings out of band if needed. +""", + attrs = { + "wasm": attr.label( + allow_single_file = [".wasm"], + mandatory = True, + doc = "The prebuilt WebAssembly component file to adopt.", + ), + "imports": attr.string_list( + default = [], + doc = "Optional, caller-declared import interface names. Empty if unknown.", + ), + "exports": attr.string_list( + default = [], + doc = "Optional, caller-declared export interface names. Empty if unknown.", + ), + "metadata": attr.string_dict( + default = {}, + doc = "Optional extra metadata to attach to the WasmComponentInfo.", + ), + }, +) From 2a0deb5b3f9d9f4385182201d6acef9b015265ef Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Sun, 14 Jun 2026 14:21:48 +0200 Subject: [PATCH 2/2] fix(example): repoint wasm_component_import example at a real component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The example referenced prebuilt_component.wasm, which was never committed, so //examples/component_import_example couldn't build. Point `wasm` at an existing built component (//examples/basic:hello_component) instead — hermetic (no binary in git) and proves the adopt -> validate path round-trips on a real component. Verified: bazel test //examples/component_import_example:component_import_test PASSES. Co-Authored-By: Claude Opus 4.8 (1M context) --- examples/component_import_example/BUILD.bazel | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/component_import_example/BUILD.bazel b/examples/component_import_example/BUILD.bazel index 32888035..23d8e07b 100644 --- a/examples/component_import_example/BUILD.bazel +++ b/examples/component_import_example/BUILD.bazel @@ -1,13 +1,14 @@ -"""Example: adopt a prebuilt .wasm component, then validate/optimize it. +"""Example: adopt a prebuilt .wasm component, then validate it. `wasm_component_import` brings an already-built component (fetched release -asset, vendored artifact, or — here — a committed fixture) into the build graph -as a `WasmComponentInfo`, so the downstream rules can consume it without +asset, vendored artifact, or — here — another target's output) into the build +graph as a `WasmComponentInfo`, so the downstream rules can consume it without rebuilding from source. See rules_wasm_component#489. -The fixture here is a committed `.wasm` used purely as a stand-in for a -prebuilt artifact; in a real pipeline `wasm` would point at an `http_file` -release asset (e.g. a flight-control component). +To keep the example hermetic (no binary committed to the repo), `wasm` points +at an existing built component (`//examples/basic:hello_component`) as a +stand-in for a prebuilt artifact. In a real pipeline `wasm` would point at an +`http_file`-fetched release asset (e.g. a flight-control component). """ load("@bazel_skylib//rules:build_test.bzl", "build_test") @@ -15,7 +16,7 @@ load("@rules_wasm_component//wasm:defs.bzl", "wasm_component_import", "wasm_vali wasm_component_import( name = "adopted_component", - wasm = "prebuilt_component.wasm", + wasm = "//examples/basic:hello_component", ) wasm_validate(