Skip to content

rust_proto_library fails in downstream Bzlmod builds due to relative resolution of //rust:use_upb_kernel #29090

Description

@anforowicz

What version of protobuf and what language are you using?

Version: main/v35.1
Language: Rust

What
supported operating system version
are you using (e.g. Linux, Windows) ?

Host / Target OS: Linux

What
supported runtime / compiler version
are you using (e.g. python version, gcc version)

Bazel Version: 9.2.0 (managed via bazelisk). See also MODULE.bazel below for rustc version. C/C++ compiler version seems mostly irrelavant as the issue is in the Bazel / Rust integration.

What did you do?

$ cd protobuf_repro_35
$ cat MODULE.bazel
module(
    name = "protobuf_repro_35",
    version = "0.0.1",
)

bazel_dep(name = "protobuf", version = "35.1")
bazel_dep(name = "rules_rust", version = "0.71.3")
bazel_dep(name = "rules_proto", version = "7.1.0")

rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
    edition = "2024",
    versions = ["1.97.1"],
)
use_repo(rust, "rust_toolchains")
register_toolchains("@rust_toolchains//:all")

$ cat BUILD
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@protobuf//rust:defs.bzl", "rust_proto_library")

proto_library(
    name = "foo_proto",
    srcs = ["foo.proto"],
)

rust_proto_library(
    name = "foo_rust_proto",
    visibility = ["//visibility:public"],
    deps = [":foo_proto"],
)

$ cat foo.proto
syntax = "proto3";

package foo;

message Bar {
    string baz = 1;
}

$ bazelisk build //:foo_rust_proto
ERROR: no such package 'rust': BUILD file not found in any of the following directories. Add a BUILD file to a directory to mark it as a package.
 - rust
ERROR: /usr/local/google/home/lukasza/src/protobuf_repro_35/BUILD:9:19: errors encountered resolving select() keys for //:foo_rust_proto
ERROR: Analysis of target '//:foo_rust_proto' failed; build aborted
INFO: Elapsed time: 0.257s, Critical Path: 0.00s
INFO: 1 process: 1 internal.
ERROR: Build did NOT complete successfully

What did you expect to see

I expected the build to succeed.

What did you see instead?

The build failed - see the repro steps above.

Anything else we should know about your project / environment

Proposed Fix

The issue is that the rust_proto_library macro in @protobuf//rust:defs.bzl uses a raw string literal for the select key:

      native.alias(
          name = name + "_rust_proto",
          actual = select({
              "//rust:use_upb_kernel": name + "_upb_rust_proto",
              "//conditions:default": name + "_cpp_rust_proto",
          }),
          **alias_args
      )

In Bazel, raw string labels in macros are resolved relative to the caller's repository/package. When a downstream project calls rust_proto_library, Bazel looks for //rust:use_upb_kernel in
the downstream project instead of the @protobuf repository.

To fix this, the label should be wrapped in Label() to force it to resolve relative to the defining repository (@protobuf):

       native.alias(
           name = name + "_rust_proto",
           actual = select({
  -            "//rust:use_upb_kernel": name + "_upb_rust_proto",
  +            Label("//rust:use_upb_kernel"): name + "_upb_rust_proto",
               "//conditions:default": name + "_cpp_rust_proto",
           }),
           **alias_args
       )

According to Bazel documentation on Starlark Macros https://bazel.build/extending/macros#label-resolution:

A string literal is resolved as a label in the context of the package where the target is declared (the caller). If you want to refer to a target in the same package where the macro is
defined, you must use Label().

Using Label("//rust:use_upb_kernel") ensures that the label is resolved to @protobuf//rust:use_upb_kernel (or its canonical Bzlmod equivalent @@protobuf+//rust:use_upb_kernel) regardless of
which downstream module calls the macro.

Note on 36.0-rc2 Verification Blocker

An attempt was made to verify if this issue persists in the 36.0-rc2 release candidate. However, the build failed early during module resolution due to an independent Bzlmod configuration
issue in the protobuf module's own dependencies:

Error Output:

  ERROR: .../external/rules_rust+/crate_universe/extensions.bzl:621:17:
  Error in fail: crate_universe extension call `crates` is in a non-root module but has no lockfile. Transitive crate_universe repositories must ship a `lockfile = ...` because repinning i
not supported across module boundaries.
  ERROR: Analysis of target '//:foo_rust_proto' failed; build aborted: error evaluating module extension @@rules_rust+//crate_universe:extension.bzl%crate

Technical Details of the Blocker:

• The protobuf module (v36.0-rc2) utilizes rules_rust's crate_universe extension transitively to manage its Rust dependencies.
• Under Bzlmod, when a non-root module (which protobuf is, from the perspective of a downstream project) calls crate_universe, it must supply a pre-generated lockfile via the lockfile
attribute. This is because Bazel does not support generating ("re-pinning") lockfiles for transitive dependencies across module boundaries.
• The protobuf v36.0-rc2 module appears to be missing this lockfile configuration in its transition to Bzlmod, which completely blocks downstream projects from using its Rust rules under
Bazel 8/9.
• Because of this blocker, we could not verify if the use_upb_kernel label resolution bug was fixed in 36.0-rc2, though static analysis suggests the macro implementation remains unchanged.

AI usage disclaimer

This bug report was created with the help of an AI agent. I have independently, manually verified (and cleaned up slightly) the main repro. OTOH the "Proposed Fix" and "Note on 36.0-rc2 Verification Blocker" are almost verbatim copies of AI agent's output.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions