Skip to content

Commit d14c85f

Browse files
committed
Auto merge of #12602 - epage:resolver, r=Eh2406
fix(resolver): Make resolver behavior independent of package order This address one of the problems mentioned in #12599 The intent behind the `path_pkg` check is to make sure we update workspace members in the lockfile if their version number changed. In this case, we don't need to recursively walk, because the change doesn't affect dependencies. However, we also shouldn't *prevent* recursive walks which is what we are doing today, both for packages marked to keep and for packages that have been "poisoned". So we fix this by moving that call after all recursive adds are complete so as to not interfere with them. This should not affect `Cargo.lock` at rest, so no upgrade compatibility concerns. This just allows more packages to be considered available to change which can prevent unclear failures. The main case I can think of that this does something "undesirable" is when wanting to prevent another "bug" from manifesting: the updating of git dependencies when updating workspace members (#12599). I think I'm ok with that as that needs to be looked into separately.
2 parents 292b0a8 + 0af2f65 commit d14c85f

File tree

2 files changed

+201
-13
lines changed

2 files changed

+201
-13
lines changed

src/cargo/ops/resolve.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -632,23 +632,11 @@ fn register_previous_locks(
632632
// however, nothing else in the dependency graph depends on `log` and the
633633
// newer version of `serde` requires a new version of `log` it'll get pulled
634634
// in (as we didn't accidentally lock it to an old version).
635-
//
636-
// Additionally, here we process all path dependencies listed in the previous
637-
// resolve. They can not only have their dependencies change but also
638-
// the versions of the package change as well. If this ends up happening
639-
// then we want to make sure we don't lock a package ID node that doesn't
640-
// actually exist. Note that we don't do transitive visits of all the
641-
// package's dependencies here as that'll be covered below to poison those
642-
// if they changed.
643635
let mut avoid_locking = HashSet::new();
644636
registry.add_to_yanked_whitelist(resolve.iter().filter(keep));
645637
for node in resolve.iter() {
646638
if !keep(&node) {
647639
add_deps(resolve, node, &mut avoid_locking);
648-
} else if let Some(pkg) = path_pkg(node.source_id()) {
649-
if pkg.package_id() != node {
650-
avoid_locking.insert(node);
651-
}
652640
}
653641
}
654642

@@ -741,6 +729,24 @@ fn register_previous_locks(
741729
}
742730
}
743731

732+
// Additionally, here we process all path dependencies listed in the previous
733+
// resolve. They can not only have their dependencies change but also
734+
// the versions of the package change as well. If this ends up happening
735+
// then we want to make sure we don't lock a package ID node that doesn't
736+
// actually exist. Note that we don't do transitive visits of all the
737+
// package's dependencies here as that'll be covered below to poison those
738+
// if they changed.
739+
//
740+
// This must come after all other `add_deps` calls to ensure it recursively walks the tree when
741+
// called.
742+
for node in resolve.iter() {
743+
if let Some(pkg) = path_pkg(node.source_id()) {
744+
if pkg.package_id() != node {
745+
avoid_locking.insert(node);
746+
}
747+
}
748+
}
749+
744750
// Alright now that we've got our new, fresh, shiny, and refined `keep`
745751
// function let's put it to action. Take a look at the previous lock file,
746752
// filter everything by this callback, and then shove everything else into

tests/testsuite/update.rs

Lines changed: 183 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Tests for the `cargo update` command.
22
33
use cargo_test_support::registry::Package;
4-
use cargo_test_support::{basic_manifest, project};
4+
use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project};
55

66
#[cargo_test]
77
fn minor_update_two_places() {
@@ -910,3 +910,185 @@ required by package `foo v0.1.0 ([ROOT]/foo)`
910910
)
911911
.run();
912912
}
913+
914+
#[cargo_test]
915+
fn update_only_members_order_one() {
916+
let git_project = git::new("rustdns", |project| {
917+
project
918+
.file("Cargo.toml", &basic_lib_manifest("rustdns"))
919+
.file("src/lib.rs", "pub fn bar() {}")
920+
});
921+
922+
let workspace_toml = format!(
923+
r#"
924+
[workspace.package]
925+
version = "2.29.8"
926+
edition = "2021"
927+
publish = false
928+
929+
[workspace]
930+
members = [
931+
"rootcrate",
932+
"subcrate",
933+
]
934+
resolver = "2"
935+
936+
[workspace.dependencies]
937+
# Internal crates
938+
subcrate = {{ version = "*", path = "./subcrate" }}
939+
940+
# External dependencies
941+
rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }}
942+
"#,
943+
git_project.url()
944+
);
945+
let p = project()
946+
.file("Cargo.toml", &workspace_toml)
947+
.file(
948+
"rootcrate/Cargo.toml",
949+
r#"
950+
[package]
951+
name = "rootcrate"
952+
version.workspace = true
953+
edition.workspace = true
954+
publish.workspace = true
955+
956+
[dependencies]
957+
subcrate.workspace = true
958+
"#,
959+
)
960+
.file("rootcrate/src/main.rs", "fn main() {}")
961+
.file(
962+
"subcrate/Cargo.toml",
963+
r#"
964+
[package]
965+
name = "subcrate"
966+
version.workspace = true
967+
edition.workspace = true
968+
publish.workspace = true
969+
970+
[dependencies]
971+
rustdns.workspace = true
972+
"#,
973+
)
974+
.file("subcrate/src/lib.rs", "pub foo() {}")
975+
.build();
976+
977+
// First time around we should compile both foo and bar
978+
p.cargo("generate-lockfile")
979+
.with_stderr(&format!(
980+
"[UPDATING] git repository `{}`\n",
981+
git_project.url(),
982+
))
983+
.run();
984+
// Modify a file manually, shouldn't trigger a recompile
985+
git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#);
986+
// Commit the changes and make sure we don't trigger a recompile because the
987+
// lock file says not to change
988+
let repo = git2::Repository::open(&git_project.root()).unwrap();
989+
git::add(&repo);
990+
git::commit(&repo);
991+
p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81"));
992+
993+
p.cargo("update -p rootcrate")
994+
.with_stderr(&format!(
995+
"\
996+
[UPDATING] git repository `{}`
997+
[UPDATING] rootcrate v2.29.8 ([CWD]/rootcrate) -> v2.29.81
998+
[UPDATING] rustdns v0.5.0 ([..]) -> [..]
999+
[UPDATING] subcrate v2.29.8 ([CWD]/subcrate) -> v2.29.81",
1000+
git_project.url(),
1001+
))
1002+
.run();
1003+
}
1004+
1005+
#[cargo_test]
1006+
fn update_only_members_order_two() {
1007+
let git_project = git::new("rustdns", |project| {
1008+
project
1009+
.file("Cargo.toml", &basic_lib_manifest("rustdns"))
1010+
.file("src/lib.rs", "pub fn bar() {}")
1011+
});
1012+
1013+
let workspace_toml = format!(
1014+
r#"
1015+
[workspace.package]
1016+
version = "2.29.8"
1017+
edition = "2021"
1018+
publish = false
1019+
1020+
[workspace]
1021+
members = [
1022+
"crate2",
1023+
"crate1",
1024+
]
1025+
resolver = "2"
1026+
1027+
[workspace.dependencies]
1028+
# Internal crates
1029+
crate1 = {{ version = "*", path = "./crate1" }}
1030+
1031+
# External dependencies
1032+
rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }}
1033+
"#,
1034+
git_project.url()
1035+
);
1036+
let p = project()
1037+
.file("Cargo.toml", &workspace_toml)
1038+
.file(
1039+
"crate2/Cargo.toml",
1040+
r#"
1041+
[package]
1042+
name = "crate2"
1043+
version.workspace = true
1044+
edition.workspace = true
1045+
publish.workspace = true
1046+
1047+
[dependencies]
1048+
crate1.workspace = true
1049+
"#,
1050+
)
1051+
.file("crate2/src/main.rs", "fn main() {}")
1052+
.file(
1053+
"crate1/Cargo.toml",
1054+
r#"
1055+
[package]
1056+
name = "crate1"
1057+
version.workspace = true
1058+
edition.workspace = true
1059+
publish.workspace = true
1060+
1061+
[dependencies]
1062+
rustdns.workspace = true
1063+
"#,
1064+
)
1065+
.file("crate1/src/lib.rs", "pub foo() {}")
1066+
.build();
1067+
1068+
// First time around we should compile both foo and bar
1069+
p.cargo("generate-lockfile")
1070+
.with_stderr(&format!(
1071+
"[UPDATING] git repository `{}`\n",
1072+
git_project.url(),
1073+
))
1074+
.run();
1075+
// Modify a file manually, shouldn't trigger a recompile
1076+
git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#);
1077+
// Commit the changes and make sure we don't trigger a recompile because the
1078+
// lock file says not to change
1079+
let repo = git2::Repository::open(&git_project.root()).unwrap();
1080+
git::add(&repo);
1081+
git::commit(&repo);
1082+
p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81"));
1083+
1084+
p.cargo("update -p crate2")
1085+
.with_stderr(&format!(
1086+
"\
1087+
[UPDATING] git repository `{}`
1088+
[UPDATING] crate1 v2.29.8 ([CWD]/crate1) -> v2.29.81
1089+
[UPDATING] crate2 v2.29.8 ([CWD]/crate2) -> v2.29.81
1090+
[UPDATING] rustdns v0.5.0 ([..]) -> [..]",
1091+
git_project.url(),
1092+
))
1093+
.run();
1094+
}

0 commit comments

Comments
 (0)