Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/cargo/ops/cargo_output_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,21 @@ fn build_resolve_graph_r(
if node_map.contains_key(&pkg_id) {
return;
}
// This normalizes the IDs so that they are consistent between the
// `packages` array and the `resolve` map. This is a bit of a hack to
// compensate for the fact that
// SourceKind::Git(GitReference::Branch("master")) is the same as
// SourceKind::Git(GitReference::DefaultBranch). We want IDs in the JSON
// to be opaque, and compare with basic string equality, so this will
// always prefer the style of ID in the Package instead of the resolver.
// Cargo generally only exposes PackageIds from the Package struct, and
// AFAIK this is the only place where the resolver variant is exposed.
//
// This diverges because the SourceIds created for Packages are built
// based on the Dependency declaration, but the SourceIds in the resolver
// are deserialized from Cargo.lock. Cargo.lock may have been generated by
// an older (or newer!) version of Cargo which uses a different style.
let normalize_id = |id| -> PackageId { *package_map.get_key_value(&id).unwrap().0 };
let features = resolve.features(pkg_id).to_vec();

let deps: Vec<Dep> = resolve
Expand All @@ -203,15 +218,15 @@ fn build_resolve_graph_r(
.and_then(|lib_target| resolve.extern_crate_name(pkg_id, dep_id, lib_target).ok())
.map(|name| Dep {
name,
pkg: dep_id,
pkg: normalize_id(dep_id),
dep_kinds,
})
})
.collect();
let dumb_deps: Vec<PackageId> = deps.iter().map(|dep| dep.pkg).collect();
let dumb_deps: Vec<PackageId> = deps.iter().map(|dep| normalize_id(dep.pkg)).collect();
let to_visit = dumb_deps.clone();
let node = MetadataResolveNode {
id: pkg_id,
id: normalize_id(pkg_id),
dependencies: dumb_deps,
deps,
features,
Expand Down
206 changes: 206 additions & 0 deletions tests/testsuite/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3020,3 +3020,209 @@ warning: [..]
)
.run();
}

#[cargo_test]
fn metadata_master_consistency() {
// SourceId consistency in the `cargo metadata` output when `master` is
// explicit or implicit, using new or old Cargo.lock.
let (git_project, git_repo) = git::new_repo("bar", |project| {
project
.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
.file("src/lib.rs", "")
});
let bar_hash = git_repo.head().unwrap().target().unwrap().to_string();

// Explicit branch="master" with a lock file created before 1.47 (does not contain ?branch=master).
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"

[dependencies]
bar = {{ git = "{}", branch = "master" }}
"#,
git_project.url()
),
)
.file(
"Cargo.lock",
&format!(
r#"
[[package]]
name = "bar"
version = "1.0.0"
source = "git+{}#{}"

[[package]]
name = "foo"
version = "0.1.0"
dependencies = [
"bar",
]
"#,
git_project.url(),
bar_hash,
),
)
.file("src/lib.rs", "")
.build();

let metadata = |bar_source| -> String {
r#"
{
"packages": [
{
"name": "bar",
"version": "1.0.0",
"id": "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)",
"license": null,
"license_file": null,
"description": null,
"source": "__BAR_SOURCE__#__BAR_HASH__",
"dependencies": [],
"targets": "{...}",
"features": {},
"manifest_path": "[..]",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"homepage": null,
"documentation": null,
"edition": "2015",
"links": null
},
{
"name": "foo",
"version": "0.1.0",
"id": "foo 0.1.0 [..]",
"license": null,
"license_file": null,
"description": null,
"source": null,
"dependencies": [
{
"name": "bar",
"source": "__BAR_SOURCE__",
"req": "*",
"kind": null,
"rename": null,
"optional": false,
"uses_default_features": true,
"features": [],
"target": null,
"registry": null
}
],
"targets": "{...}",
"features": {},
"manifest_path": "[..]",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"homepage": null,
"documentation": null,
"edition": "2015",
"links": null
}
],
"workspace_members": [
"foo 0.1.0 [..]"
],
"resolve": {
"nodes": [
{
"id": "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)",
"dependencies": [],
"deps": [],
"features": []
},
{
"id": "foo 0.1.0 [..]",
"dependencies": [
"bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)"
],
"deps": [
{
"name": "bar",
"pkg": "bar 1.0.0 (__BAR_SOURCE__#__BAR_HASH__)",
"dep_kinds": [
{
"kind": null,
"target": null
}
]
}
],
"features": []
}
],
"root": "foo 0.1.0 [..]"
},
"target_directory": "[..]",
"version": 1,
"workspace_root": "[..]",
"metadata": null
}
"#
.replace("__BAR_SOURCE__", bar_source)
.replace("__BAR_HASH__", &bar_hash)
};

let bar_source = format!("git+{}?branch=master", git_project.url());
p.cargo("metadata").with_json(&metadata(&bar_source)).run();

// Conversely, remove branch="master" from Cargo.toml, but use a new Cargo.lock that has ?branch=master.
let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"

[dependencies]
bar = {{ git = "{}" }}
"#,
git_project.url()
),
)
.file(
"Cargo.lock",
&format!(
r#"
[[package]]
name = "bar"
version = "1.0.0"
source = "git+{}?branch=master#{}"

[[package]]
name = "foo"
version = "0.1.0"
dependencies = [
"bar",
]
"#,
git_project.url(),
bar_hash
),
)
.file("src/lib.rs", "")
.build();

// No ?branch=master!
let bar_source = format!("git+{}", git_project.url());
p.cargo("metadata").with_json(&metadata(&bar_source)).run();
}