fix(package): Fix lookups to capitalized workspace member's index entry (#15216)

### What does this PR try to resolve?

When investigating a report of a package-rename bug in
`-Zpackage-workspace`, I found that we weren't correctly naming the file
for index entries for generating `Cargo.lock` and verifying. We must
first `to_lowercase` the name.

In fixing this, I also tried to clarify the API to reduce the chance of
this happening in the future. Still not great that the caller is
expected to handle this and know about it. The problem is
`make_dep_path` is shared between the index and `.crate` files which are
handled differently (and sharing of the `to_lowercase` would be nice).
Maybe if made a `make_index_path` that had an assert and called
`make_dep_path`. I held off on that for now.

### How should we test and review this PR?

### Additional information
This commit is contained in:
Weihang Lo 2025-02-21 21:38:53 +00:00 committed by GitHub
commit 1d1d646c06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 7 deletions

View File

@ -3,6 +3,12 @@
/// - [index from of Cargo's index on filesystem][1], and
/// - [index from Crates.io][2].
///
/// <div class="warning">
///
/// Note: For index files, `dep_name` must have had `to_lowercase` called on it.
///
/// </div>
///
/// [1]: https://docs.rs/cargo/latest/cargo/sources/registry/index.html#the-format-of-the-index
/// [2]: https://github.com/rust-lang/crates.io-index
pub fn make_dep_path(dep_name: &str, prefix_only: bool) -> String {

View File

@ -1068,7 +1068,8 @@ impl<'a> TmpRegistry<'a> {
v: Some(2),
})?;
let file = cargo_util::registry::make_dep_path(package.name().as_str(), false);
let file =
cargo_util::registry::make_dep_path(&package.name().as_str().to_lowercase(), false);
let mut dst = self.index_path().open_rw_exclusive_create(
file,
self.gctx,

View File

@ -584,19 +584,19 @@ impl Summaries {
) -> Poll<CargoResult<Option<Summaries>>> {
// This is the file we're loading from cache or the index data.
// See module comment in `registry/mod.rs` for why this is structured the way it is.
let name = &name.to_lowercase();
let relative = make_dep_path(&name, false);
let lowered_name = &name.to_lowercase();
let relative = make_dep_path(&lowered_name, false);
let mut cached_summaries = None;
let mut index_version = None;
if let Some(contents) = cache_manager.get(name) {
if let Some(contents) = cache_manager.get(lowered_name) {
match Summaries::parse_cache(contents) {
Ok((s, v)) => {
cached_summaries = Some(s);
index_version = Some(v);
}
Err(e) => {
tracing::debug!("failed to parse {name:?} cache: {e}");
tracing::debug!("failed to parse {lowered_name:?} cache: {e}");
}
}
}
@ -609,7 +609,7 @@ impl Summaries {
return Poll::Ready(Ok(cached_summaries));
}
LoadResponse::NotFound => {
cache_manager.invalidate(name);
cache_manager.invalidate(lowered_name);
return Poll::Ready(Ok(None));
}
LoadResponse::Data {
@ -658,7 +658,7 @@ impl Summaries {
// Once we have our `cache_bytes` which represents the `Summaries` we're
// about to return, write that back out to disk so future Cargo
// invocations can use it.
cache_manager.put(name, &cache_bytes);
cache_manager.put(lowered_name, &cache_bytes);
// If we've got debug assertions enabled read back in the cached values
// and assert they match the expected result.

View File

@ -6430,6 +6430,68 @@ fn workspace_with_local_and_remote_deps() {
.run();
}
#[cargo_test]
fn workspace_with_capitalized_member() {
let reg = registry::init();
let p = project()
.file(
"Cargo.toml",
r#"
[workspace]
members = ["dep", "main"]
"#,
)
.file(
"main/Cargo.toml",
r#"
[package]
name = "main"
version = "0.0.1"
edition = "2015"
authors = []
license = "MIT"
description = "main"
repository = "bar"
[dependencies]
DEP = { path = "../dep", version = "0.1.0" }
"#,
)
.file("main/src/main.rs", "fn main() {}")
.file(
"dep/Cargo.toml",
r#"
[package]
name = "DEP"
version = "0.1.0"
edition = "2015"
authors = []
license = "MIT"
description = "dep"
repository = "bar"
"#,
)
.file("dep/src/lib.rs", "")
.build();
p.cargo("package -Zpackage-workspace --no-verify")
.masquerade_as_nightly_cargo(&["package-workspace"])
.replace_crates_io(reg.index_url())
.with_stderr_data(
str![[r#"
[PACKAGING] main v0.0.1 ([ROOT]/foo/main)
[UPDATING] crates.io index
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
[PACKAGING] DEP v0.1.0 ([ROOT]/foo/dep)
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
"#]]
.unordered(),
)
.run();
}
#[cargo_test]
fn registry_not_in_publish_list() {
let p = project()