Summary
crate_universe's update-repos (cargo-bazel renderer) emits local_crate_mirror(path = "<workspace-relative path>") calls when consumers use a standard Cargo [patch.crates-io] entry with a relative path. But the local_crate_mirror repository rule cannot consume the path it was given: repository_ctx.path(string) resolves relative paths against the external repo's own directory, not the workspace root, so the readdir() call inside the rule fails immediately.
So the bug is entirely internal to rules_rust — the generator produces a value the consumer can't use.
Reproducer
In a Bazel workspace, vendor a crate hand-tweak it, then patch it into Cargo:
# Cargo.toml
[patch.crates-io]
mycrate = { path = "third_party/rust/vendor/mycrate-1.0.0" }
bazel run //third_party/rust:update-repos
cargo-bazel renders this into third_party/rust/crates/defs.bzl:
local_crate_mirror(
name = "crate_index__mycrate-1.0.0",
options_json = "{...,\"local_path\":\"third_party/rust/vendor/mycrate-1.0.0\",...}",
path = "third_party/rust/vendor/mycrate-1.0.0",
)
crate_universe/src/metadata/metadata_annotation.rs:284–310 explicitly produces this workspace-relative form, and the in-code comment says:
// This replacement allows in-repo patches sections to work as intended using local_crate_mirror.
So upstream's intent is clearly to support relative [patch.crates-io] paths. But crate_universe/private/local_crate_mirror.bzl:9:
def _local_crate_mirror_impl(repository_ctx):
path = repository_ctx.path(repository_ctx.attr.path)
...
for child in repository_ctx.path(path).readdir():
...
repository_ctx.path(relative_str) resolves under the external repo's own directory. The external repo @@_main~rust_crates_extension~crate_index__mycrate-1.0.0 doesn't contain third_party/rust/vendor/mycrate-1.0.0, so:
Error in readdir: can't readdir(), not a directory:
.../external/_main~rust_crates_extension~crate_index__mycrate-1.0.0/third_party/rust/vendor/mycrate-1.0.0
The path attribute docstring even has a TODO flagging this:
"path": attr.string(
# TODO: Verify what happens if this is not an absolute path.
doc = "Absolute path to the BUILD.bazel file to generate.",
),
The docstring says "absolute" but the generator emits relative — that contradiction is the bug.
Proposed fix
Make local_crate_mirror resolve path relative to the workspace root when relative:
def _local_crate_mirror_impl(repository_ctx):
path = repository_ctx.workspace_root.get_child(repository_ctx.attr.path)
...
repository_ctx.workspace_root is well-defined and always points at the main workspace root from inside an external repo's fetch.
Either that, or update the renderer in metadata/metadata_annotation.rs to emit an absolute path (and update the path attr docs/checks accordingly). The first option seems cleaner because Cargo [patch.crates-io] paths are always relative-to-the-manifest by Cargo's design.
We carry the first option as a local patch against 0.70.0. Happy to send a PR if maintainers prefer.
Versions
- rules_rust: 0.70.0
- bazel: 7.7.1
Summary
crate_universe'supdate-repos(cargo-bazel renderer) emitslocal_crate_mirror(path = "<workspace-relative path>")calls when consumers use a standard Cargo[patch.crates-io]entry with a relative path. But thelocal_crate_mirrorrepository rule cannot consume the path it was given:repository_ctx.path(string)resolves relative paths against the external repo's own directory, not the workspace root, so thereaddir()call inside the rule fails immediately.So the bug is entirely internal to rules_rust — the generator produces a value the consumer can't use.
Reproducer
In a Bazel workspace, vendor a crate hand-tweak it, then patch it into Cargo:
cargo-bazel renders this into
third_party/rust/crates/defs.bzl:crate_universe/src/metadata/metadata_annotation.rs:284–310explicitly produces this workspace-relative form, and the in-code comment says:So upstream's intent is clearly to support relative
[patch.crates-io]paths. Butcrate_universe/private/local_crate_mirror.bzl:9:repository_ctx.path(relative_str)resolves under the external repo's own directory. The external repo@@_main~rust_crates_extension~crate_index__mycrate-1.0.0doesn't containthird_party/rust/vendor/mycrate-1.0.0, so:The
pathattribute docstring even has a TODO flagging this:The docstring says "absolute" but the generator emits relative — that contradiction is the bug.
Proposed fix
Make
local_crate_mirrorresolvepathrelative to the workspace root when relative:repository_ctx.workspace_rootis well-defined and always points at the main workspace root from inside an external repo's fetch.Either that, or update the renderer in
metadata/metadata_annotation.rsto emit an absolute path (and update thepathattr docs/checks accordingly). The first option seems cleaner because Cargo[patch.crates-io]paths are always relative-to-the-manifest by Cargo's design.We carry the first option as a local patch against 0.70.0. Happy to send a PR if maintainers prefer.
Versions