bazelbuild / bazelbuild/rules_rust
crate_universe: local_crate_mirror does not accept relative paths despite docs implying it might
- Dominant language
- Starlark
- Stars
- 843
- Forks
- 651
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 15
Description
## Summary
`crate_universe`'s `update-repos` (cargo-bazel renderer) emits `local_crate_mirror(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:
```toml
# Cargo.toml
[patch.crates-io]
mycrate = { path = "third_party/rust/vendor/mycrate-1.0.0" }
```
```bash
bazel run //third_party/rust:update-repos
```
cargo-bazel renders this into `third_party/rust/crates/defs.bzl`:
```python
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`:
```python
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:
```python
"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:
```python
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
Contributor guide
Assessment
This issue has not been assessed yet.