Skip to content

Commit fb123c6

Browse files
committed
Auto merge of #13979 - tweag:issue-12425, r=epage
Add `cargo update --breaking` Related to #12425. There are two kinds of manifest mutations here. In `upgrade_manifests` we have to mutate `cargo::core::manifest::Manifest` so that `resolve_ws` does what it needs to do, and outputs a correct lock file. Then, in `write_manifest_upgrades` we mutate `cargo::util::toml_mut::manifest::Manifest`, because that is how we can preserve the existing formatting in the manifest files on disk. Some of the code here is copied from `cargo-edit`. # Comparison with `cargo upgrade` Running on the Cargo source itself gives the following: ``` ❯ cargo upgrade --dry-run --incompatible allow --compatible ignore Updating 'https://github.com/rust-lang/crates.io-index' index Checking benchsuite's dependencies Checking capture's dependencies Checking cargo's dependencies name old req compatible latest new req note ==== ======= ========== ====== ======= ==== anstream 0.6.13 0.6.14 0.6.14 0.6.13 compatible anstyle 1.0.6 1.0.7 1.0.7 1.0.6 compatible anyhow 1.0.82 1.0.86 1.0.86 1.0.82 compatible itertools 0.12.1 0.12.1 0.13.0 0.13.0 libc 0.2.154 0.2.155 0.2.155 0.2.154 compatible opener 0.7.0 0.7.1 0.7.1 0.7.0 compatible openssl 0.10.57 0.10.64 0.10.64 0.10.57 compatible openssl-sys =0.9.92 0.9.92 0.9.102 =0.9.92 pinned pulldown-cmark 0.10.3 0.10.3 0.11.0 0.11.0 security-framework 2.10.0 2.11.0 2.11.0 2.10.0 compatible semver 1.0.22 1.0.23 1.0.23 1.0.22 compatible serde 1.0.199 1.0.203 1.0.203 1.0.199 compatible serde-untagged 0.1.5 0.1.6 0.1.6 0.1.5 compatible serde_json 1.0.116 1.0.117 1.0.117 1.0.116 compatible tar 0.4.40 0.4.41 0.4.41 0.4.40 compatible thiserror 1.0.59 1.0.61 1.0.61 1.0.59 compatible toml 0.8.12 0.8.14 0.8.14 0.8.12 compatible toml_edit 0.22.12 0.22.14 0.22.14 0.22.12 compatible unicode-width 0.1.12 0.1.13 0.1.13 0.1.12 compatible Checking cargo-credential's dependencies Checking cargo-credential-1password's dependencies Checking cargo-credential-libsecret's dependencies Checking cargo-credential-macos-keychain's dependencies Checking cargo-credential-wincred's dependencies Checking cargo-platform's dependencies Checking cargo-test-macro's dependencies Checking cargo-test-support's dependencies Checking cargo-util's dependencies Checking cargo-util-schemas's dependencies Checking crates-io's dependencies Checking home's dependencies Checking mdman's dependencies Checking resolver-tests's dependencies Checking rustfix's dependencies Checking semver-check's dependencies Checking xtask-build-man's dependencies Checking xtask-bump-check's dependencies Checking xtask-stale-label's dependencies note: Re-run with `--pinned` to upgrade pinned version requirements note: Re-run with `--verbose` to show all dependencies local: cargo unchanged: annotate-snippets, base64, bytesize, cargo-credential, cargo-credential-libsecret, cargo-credential-macos-keychain, cargo-credential-wincred, cargo-platform, cargo-test-macro, cargo-test-support, cargo-util, cargo-util-schemas, cargo_metadata, clap, color-print, core-foundation, crates-io, criterion, curl, curl-sys, filetime, flate2, git2, git2-curl, gix, glob, handlebars, hex, hmac, home, http-auth, humantime, ignore, im-rc, indexmap, jobserver, lazycell, libgit2-sys, libloading, memchr, miow, os_info, pasetors, pathdiff, percent-encoding, pkg-config, proptest, rand, regex, rusqlite, rustfix, same-file, serde-value, serde_ignored, sha1, sha2, shell-escape, similar, snapbox, supports-hyperlinks, supports-unicode, tempfile, time, tracing, tracing-chrome, tracing-subscriber, unicase, unicode-xid, url, varisat, walkdir, windows-sys warning: aborting upgrade due to dry run ❯ target/debug/cargo update --breaking --dry-run -Zunstable-options Updating crates.io index Upgrading itertools ^0.12.1 -> ^0.13.0 Upgrading pulldown-cmark ^0.10.3 -> ^0.11.0 Updating crates.io index Locking 3 packages to latest compatible versions Updating itertools v0.12.1 -> v0.13.0 Updating pulldown-cmark v0.10.3 -> v0.11.0 Updating pulldown-cmark-escape v0.10.0 -> v0.11.0 warning: not updating any files due to dry run ``` In both cases we see an upgrade of `itertools` to `^0.13.0` and `pulldown-cmark` to `^0.11.0`. The diff to the manifest (when it isn't a dry run) is as follows: ``` --- a/Cargo.toml +++ b/Cargo.toml `@@` -57,7 +57,7 `@@` humantime = "2.1.0" ignore = "0.4.22" im-rc = "15.1.0" indexmap = "2.2.6" -itertools = "0.12.1" +itertools = "0.13.0" jobserver = "0.1.31" lazycell = "1.3.0" libc = "0.2.154" `@@` -74,7 +74,7 `@@` pathdiff = "0.2.1" percent-encoding = "2.3.1" pkg-config = "0.3.30" proptest = "1.4.0" -pulldown-cmark = { version = "0.10.3", default-features = false, features = ["html"] } +pulldown-cmark = { version = "0.11.0", default-features = false, features = ["html"] } rand = "0.8.5" regex = "1.10.4" rusqlite = { version = "0.31.0", features = ["bundled"] } ``` # TODO - [x] In the case of `--incompatible`, we also need to let `update_lockfile` use `upgrades` in order to only update the incompatible dependencies. - [x] Testing all the different cases of package sources, version requirements, pinned versions, renamed dependencies, inherited workspace dependencies, multiple versions of the same dependency, selecting a subset `--package`, etc. - [x] Passing tests. - [x] Implement suggestions from reviews. - [x] The preservation of formatting in manifest files should be improved. - [x] Compare with `cargo upgrade`.
2 parents e8e50d0 + 031b410 commit fb123c6

File tree

26 files changed

+1127
-74
lines changed

26 files changed

+1127
-74
lines changed

Diff for: benches/benchsuite/benches/resolve.rs

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn do_resolve<'gctx>(gctx: &'gctx GlobalContext, ws_root: &Path) -> ResolveInfo<
3333
let force_all_targets = ForceAllTargets::No;
3434
// Do an initial run to download anything necessary so that it does
3535
// not confuse criterion's warmup.
36+
let dry_run = false;
3637
let ws_resolve = cargo::ops::resolve_ws_with_opts(
3738
&ws,
3839
&mut target_data,
@@ -41,6 +42,7 @@ fn do_resolve<'gctx>(gctx: &'gctx GlobalContext, ws_root: &Path) -> ResolveInfo<
4142
&specs,
4243
has_dev_units,
4344
force_all_targets,
45+
dry_run,
4446
)
4547
.unwrap();
4648
ResolveInfo {
@@ -71,6 +73,7 @@ fn resolve_ws(c: &mut Criterion) {
7173
// iterator once, and we don't want to call `do_resolve` in every
7274
// "step", since that would just be some useless work.
7375
let mut lazy_info = None;
76+
let dry_run = false;
7477
group.bench_function(&ws_name, |b| {
7578
let ResolveInfo {
7679
ws,
@@ -91,6 +94,7 @@ fn resolve_ws(c: &mut Criterion) {
9194
specs,
9295
*has_dev_units,
9396
*force_all_targets,
97+
dry_run,
9498
)
9599
.unwrap();
96100
})

Diff for: crates/cargo-test-support/src/compare.rs

+1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ static E2E_LITERAL_REDACTIONS: &[(&str, &str)] = &[
176176
("[DIRTY]", " Dirty"),
177177
("[LOCKING]", " Locking"),
178178
("[UPDATING]", " Updating"),
179+
("[UPGRADING]", " Upgrading"),
179180
("[ADDING]", " Adding"),
180181
("[REMOVING]", " Removing"),
181182
("[REMOVED]", " Removed"),

Diff for: src/bin/cargo/commands/add.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,9 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
214214
};
215215
add(&ws, &options)?;
216216

217-
if !dry_run {
218-
// Reload the workspace since we've changed dependencies
219-
let ws = args.workspace(gctx)?;
220-
resolve_ws(&ws)?;
221-
}
217+
// Reload the workspace since we've changed dependencies
218+
let ws = args.workspace(gctx)?;
219+
resolve_ws(&ws, dry_run)?;
222220

223221
Ok(())
224222
}

Diff for: src/bin/cargo/commands/remove.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
121121
ws.gctx()
122122
.shell()
123123
.set_verbosity(cargo::core::Verbosity::Quiet);
124-
let resolve = resolve_ws(&ws);
124+
let resolve = resolve_ws(&ws, dry_run);
125125
ws.gctx().shell().set_verbosity(verbosity);
126126
resolve?.1
127127
};
128128

129129
// Attempt to gc unused patches and re-resolve if anything is removed
130130
if gc_unused_patches(&workspace, &resolve)? {
131131
let ws = args.workspace(gctx)?;
132-
resolve_ws(&ws)?;
132+
resolve_ws(&ws, dry_run)?;
133133
}
134134
}
135135
Ok(())

Diff for: src/bin/cargo/commands/update.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ pub fn cli() -> Command {
3535
.value_name("PRECISE")
3636
.requires("package-group"),
3737
)
38+
.arg(
39+
flag(
40+
"breaking",
41+
"Upgrade [SPEC] to latest breaking versions, unless pinned (unstable)",
42+
)
43+
.short('b'),
44+
)
3845
.arg_silent_suggestion()
3946
.arg(
4047
flag("workspace", "Only update the workspace packages")
@@ -59,7 +66,8 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
5966
gctx.cli_unstable().msrv_policy,
6067
)?;
6168
}
62-
let ws = args.workspace(gctx)?;
69+
70+
let mut ws = args.workspace(gctx)?;
6371

6472
if args.is_present_with_zero_values("package") {
6573
print_available_packages(&ws)?;
@@ -89,6 +97,24 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
8997
workspace: args.flag("workspace"),
9098
gctx,
9199
};
92-
ops::update_lockfile(&ws, &update_opts)?;
100+
101+
if args.flag("breaking") {
102+
gctx.cli_unstable()
103+
.fail_if_stable_opt("--breaking", 12425)?;
104+
105+
let upgrades = ops::upgrade_manifests(&mut ws, &update_opts.to_update)?;
106+
ops::resolve_ws(&ws, update_opts.dry_run)?;
107+
ops::write_manifest_upgrades(&ws, &upgrades, update_opts.dry_run)?;
108+
109+
if update_opts.dry_run {
110+
update_opts
111+
.gctx
112+
.shell()
113+
.warn("aborting update due to dry run")?;
114+
}
115+
} else {
116+
ops::update_lockfile(&ws, &update_opts)?;
117+
}
118+
93119
Ok(())
94120
}

Diff for: src/cargo/core/compiler/standard_lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ pub fn resolve_std<'gctx>(
149149
let cli_features = CliFeatures::from_command_line(
150150
&features, /*all_features*/ false, /*uses_default_features*/ false,
151151
)?;
152+
let dry_run = false;
152153
let resolve = ops::resolve_ws_with_opts(
153154
&std_ws,
154155
target_data,
@@ -157,6 +158,7 @@ pub fn resolve_std<'gctx>(
157158
&specs,
158159
HasDevUnits::No,
159160
crate::core::resolver::features::ForceAllTargets::No,
161+
dry_run,
160162
)?;
161163
Ok((
162164
resolve.pkg_set,

Diff for: src/cargo/core/summary.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,25 @@ impl Summary {
103103
Rc::make_mut(&mut self.inner).checksum = Some(cksum);
104104
}
105105

106-
pub fn map_dependencies<F>(mut self, f: F) -> Summary
106+
pub fn map_dependencies<F>(self, mut f: F) -> Summary
107107
where
108108
F: FnMut(Dependency) -> Dependency,
109+
{
110+
self.try_map_dependencies(|dep| Ok(f(dep))).unwrap()
111+
}
112+
113+
pub fn try_map_dependencies<F>(mut self, f: F) -> CargoResult<Summary>
114+
where
115+
F: FnMut(Dependency) -> CargoResult<Dependency>,
109116
{
110117
{
111118
let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
112-
*slot = mem::take(slot).into_iter().map(f).collect();
119+
*slot = mem::take(slot)
120+
.into_iter()
121+
.map(f)
122+
.collect::<CargoResult<_>>()?;
113123
}
114-
self
124+
Ok(self)
115125
}
116126

117127
pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Summary {

Diff for: src/cargo/ops/cargo_clean.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,14 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
7777
if opts.spec.is_empty() {
7878
clean_ctx.remove_paths(&[target_dir.into_path_unlocked()])?;
7979
} else {
80-
clean_specs(&mut clean_ctx, &ws, &profiles, &opts.targets, &opts.spec)?;
80+
clean_specs(
81+
&mut clean_ctx,
82+
&ws,
83+
&profiles,
84+
&opts.targets,
85+
&opts.spec,
86+
opts.dry_run,
87+
)?;
8188
}
8289
}
8390

@@ -91,11 +98,12 @@ fn clean_specs(
9198
profiles: &Profiles,
9299
targets: &[String],
93100
spec: &[String],
101+
dry_run: bool,
94102
) -> CargoResult<()> {
95103
// Clean specific packages.
96104
let requested_kinds = CompileKind::from_requested_targets(clean_ctx.gctx, targets)?;
97105
let target_data = RustcTargetData::new(ws, &requested_kinds)?;
98-
let (pkg_set, resolve) = ops::resolve_ws(ws)?;
106+
let (pkg_set, resolve) = ops::resolve_ws(ws, dry_run)?;
99107
let prof_dir_name = profiles.get_dir_name();
100108
let host_layout = Layout::new(ws, None, &prof_dir_name)?;
101109
// Convert requested kinds to a Vec of layouts.

Diff for: src/cargo/ops/cargo_compile/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ pub fn create_bcx<'a, 'gctx>(
264264
HasDevUnits::No
265265
}
266266
};
267+
let dry_run = false;
267268
let resolve = ops::resolve_ws_with_opts(
268269
ws,
269270
&mut target_data,
@@ -272,6 +273,7 @@ pub fn create_bcx<'a, 'gctx>(
272273
&specs,
273274
has_dev_units,
274275
crate::core::resolver::features::ForceAllTargets::No,
276+
dry_run,
275277
)?;
276278
let WorkspaceResolve {
277279
mut pkg_set,

Diff for: src/cargo/ops/cargo_fetch.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ pub fn fetch<'a>(
1919
options: &FetchOptions<'a>,
2020
) -> CargoResult<(Resolve, PackageSet<'a>)> {
2121
ws.emit_warnings()?;
22-
let (mut packages, resolve) = ops::resolve_ws(ws)?;
22+
let dry_run = false;
23+
let (mut packages, resolve) = ops::resolve_ws(ws, dry_run)?;
2324

2425
let jobs = Some(JobsConfig::Integer(1));
2526
let keep_going = false;

Diff for: src/cargo/ops/cargo_install.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,8 @@ impl<'gctx> InstallablePackage<'gctx> {
561561
// It would be best if `source` could be passed in here to avoid a
562562
// duplicate "Updating", but since `source` is taken by value, then it
563563
// wouldn't be available for `compile_ws`.
564-
let (pkg_set, resolve) = ops::resolve_ws(&self.ws)?;
564+
let dry_run = false;
565+
let (pkg_set, resolve) = ops::resolve_ws(&self.ws, dry_run)?;
565566
ops::check_yanked(
566567
self.ws.gctx(),
567568
&pkg_set,

Diff for: src/cargo/ops/cargo_output_metadata.rs

+2
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ fn build_resolve_graph(
142142

143143
// Note that even with --filter-platform we end up downloading host dependencies as well,
144144
// as that is the behavior of download_accessible.
145+
let dry_run = false;
145146
let ws_resolve = ops::resolve_ws_with_opts(
146147
ws,
147148
&mut target_data,
@@ -150,6 +151,7 @@ fn build_resolve_graph(
150151
&specs,
151152
HasDevUnits::Yes,
152153
force_all,
154+
dry_run,
153155
)?;
154156

155157
let package_map: BTreeMap<PackageId, Package> = ws_resolve

Diff for: src/cargo/ops/cargo_package.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
190190

191191
if ws.root().join("Cargo.lock").exists() {
192192
// Make sure the Cargo.lock is up-to-date and valid.
193-
let _ = ops::resolve_ws(ws)?;
193+
let dry_run = false;
194+
let _ = ops::resolve_ws(ws, dry_run)?;
194195
// If Cargo.lock does not exist, it will be generated by `build_lock`
195196
// below, and will be validated during the verification step.
196197
}

0 commit comments

Comments
 (0)