Skip to content

crate_universe: local_crate_mirror does not accept relative paths despite docs implying it might #4036

@ashi009

Description

@ashi009

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

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