Skip to content

Bazel: Skylark rules for buffa_rust_library and connectrpc_rust_library #39

@iainmcgin

Description

@iainmcgin

Background

PR #38 (Add Bazel example for buffa+connectrpc protoc plugins) introduces a working Bazel integration for the codegen pipeline using a hand-rolled genrule. The genrule:

  • lists every .proto file as a srcs entry,
  • pre-declares each generated .rs filename in outs,
  • inlines the protoc invocation (or generates a buf.gen.yaml) directly in the cmd,
  • requires the caller to know the output naming convention used by protoc-gen-buffa/protoc-gen-buffa-packaging/protoc-gen-connect-rust.

This is fine as a demo but not what teams should be writing for every proto target in a real Bazel codebase. The natural next step is two custom Starlark rules — buffa_rust_library and connectrpc_rust_library — modeled on rules_rust_prosts rust_prost_library.

Proposed shape

load("@rules_buffa//:defs.bzl", "buffa_rust_library")
load("@rules_connectrpc//:defs.bzl", "connectrpc_rust_library")

proto_library(
    name = "greet_proto",
    srcs = ["greet.proto"],
    strip_import_prefix = "/proto",
)

# Rust message types from buffa codegen.
buffa_rust_library(
    name = "greet_buffa",
    proto = ":greet_proto",
    options = {"views": True, "json": True},
)

# Rust service stubs (server traits, client structs) from connectrpc codegen.
connectrpc_rust_library(
    name = "greet_connect",
    proto = ":greet_proto",
    buffa_module = "crate::proto",
    deps = [":greet_buffa"],   # references buffa-generated message types
)

What the rules would do internally:

  1. Pull ProtoInfo from the proto = ... attribute to get the transitive descriptor set, source files, and import paths.
  2. Resolve plugin binaries via a registered toolchain (buffa_toolchain / connectrpc_toolchain) — modeled on rust_prost_toolchain.
  3. Run protoc (or invoke buf) as a Bazel action.
  4. Predict output filenames from .proto file paths (the proto_path_to_rust_module convention is deterministic — foo/bar/v1.protofoo.bar.v1.rs) so they can be declared at analysis time.
  5. Compile the generated files into a rust_library and return both RustAnalyzerInfo and CrateInfo providers, so downstream rust_library/rust_test targets compose naturally.

Open questions

  • Do these live in this repo, or would Buf Inc host them in a rules_buf_rust extension repo (mirroring rules_rust_prost under bazelbuild/rules_rust)? Splitting into two rules (buffa_rust_library for messages, connectrpc_rust_library for services) lets buffa be used standalone, but means the connectrpc rule has a soft dependency on the buffa rules output.
  • Toolchain shape: should the buffa toolchain be parameterized over views/json/text/utf8_validation/etc., or should those stay rule-attribute knobs?
  • How should we expose the buf-driven path? A use_buf_generate = True attribute that swaps protoc for buf, or a separate rule? PR Add Bazel example for buffa+connectrpc protoc plugins #38 demonstrates both wrappers in plain genrules — for the Skylark rule wed want to pick one default.
  • Does this overlap usefully with a hypothetical language-agnostic buf_generate rule from rules_buf (separate proposal being drafted for Buf Inc)? If buf_generate exists, the buffa/connectrpc rules might layer on top of it instead of invoking protoc/buf directly.

Out of scope for this issue

A language-agnostic buf_generate rule that would serve as the foundation here is a separate proposal that should land upstream in bufbuild/rules_buf, not in this repo.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions