Skip to content

Cargo vendor doesn't replace all sources used by dependencies in its output #14729

Open
@P-E-Meunier

Description

@P-E-Meunier

Problem

Let's say we have three crates, A, B and C, where A depends on B and C and B depends on C.

We serve them using two identical registries x and y. Crate A has the following line in its Cargo.toml:

B = { version = ..., registry = "x" }
C = { version = ..., registry = "x" }

Now, let's also assume that the authors of B preferred registry y, B would have the following Cargo.toml:

C = { version = ..., registry = "y" }

Since the hashes of C, as served by x and y, are identical, cargo chooses only one source in Cargo.lock. In our example let's say it chooses x.

When running cargo vendor, the stdout shows what replacements are needed, and that stdout is meant to be consumed by Cargo itself when doing cargo build --offline.

Because the sources are taken from Cargo.lock, in our case the output will replace x only. While this seems intuitively correct (all crates are indeed vendored, and all their source registries are replaced), cargo build --offline still fails in our example, since the Cargo.toml of B mentions registry y.

Steps

No response

Possible Solution(s)

One fix could have been to tell cargo build to ignore source registries and only look for crate names and versions, but the same crate+version may appear multiple times in Cargo.lock if the different registries disagree on the hash.

A simpler fix, implemented by #14716, is to read the Cargo.toml of all dependencies when vendoring them to make sure we replace all the sources.

Notes

Now, I agree some parts of that explanation sound like shooting yourself in the foot:

  • Why would anyone mirror registries in this way rather than use the same domain name? Well, in our case this was caused by a migration to a different provider, with a transition period where both addresses were still in use.
  • Why would you want two different registries to serve the same crate name+version with different hashes? Now, that sounds like setting you up for really hard debugging sessions, but I suppose our mirror of B on x could have used C = { registry = "x" } to avoid this issue in the first place.

Version

Last commit on master.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: bugS-triageStatus: This issue is waiting on initial triage.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions