Skip to content

Commit b1d4a2e

Browse files
committed
Auto merge of #8554 - alexcrichton:update-lockfile-rollout, r=ehuss
Update lock file encodings on changes This commit alters Cargo's lockfile encoding update strategy from its previous incarnation. Previously Cargo had two versions, one for new lock files and one for old lock files. Each of these versions were different and would affect how Cargo manages lock file updates. The intention was that we'd roll out defaults to new lock files first and then later to preexisting lock files. This requires two separate changes, though, and it's not necessarily clear when to start updating old lock files. Additionally when old lock files were opted in it would break builds using `--locked` if they simply updated Cargo because Cargo would would want to bring the lock file versions forward. The purpose of this change is to solve these issues. The new strategy for updating a lock file's encoding is to simply preserve what's already existing on the filesystem until we actually decide to write out a new lock file. When Cargo updates a lock file on-disk then it will, at that time, update the lock file encoding to whatever the current default is. This means that there's only one version number to keep track of (the default for encoding). Cargo will always preserve the preexisting encoding unless another change is required to the lock file.
2 parents b170a07 + fdbe9f8 commit b1d4a2e

File tree

8 files changed

+129
-90
lines changed

8 files changed

+129
-90
lines changed

src/cargo/core/resolver/encode.rs

+24-9
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,32 @@
1919
//! We do, however, want to change `Cargo.lock` over time. (and we have!). To do
2020
//! this the rules that we currently have are:
2121
//!
22-
//! * Add support for the new format to Cargo
23-
//! * Continue to, by default, generate the old format
24-
//! * Preserve the new format if found
25-
//! * Wait a "long time" (e.g. 6 months or so)
26-
//! * Change Cargo to by default emit the new format
22+
//! * Add support for the new format to Cargo. This involves code changes in
23+
//! Cargo itself, likely by adding a new variant of `ResolveVersion` and
24+
//! branching on that where necessary. This is accompanied with tests in the
25+
//! `lockfile_compat` module.
26+
//!
27+
//! * Do not update `ResolveVersion::default()`. The new lockfile format will
28+
//! not be used yet.
29+
//!
30+
//! * Preserve the new format if found. This means that if Cargo finds the new
31+
//! version it'll keep using it, but otherwise it continues to use whatever
32+
//! format it previously found.
33+
//!
34+
//! * Wait a "long time". This is at least until the changes here hit stable
35+
//! Rust. Often though we wait a little longer to let the changes percolate
36+
//! into one or two older stable releases.
37+
//!
38+
//! * Change the return value of `ResolveVersion::default()` to the new format.
39+
//! This will cause new lock files to use the latest encoding as well as
40+
//! causing any operation which updates the lock file to update to the new
41+
//! format.
2742
//!
2843
//! This migration scheme in general means that Cargo we'll get *support* for a
29-
//! new format into Cargo ASAP, but it won't really be exercised yet (except in
30-
//! Cargo's own tests really). Eventually when stable/beta/nightly all have
31-
//! support for the new format (and maybe a few previous stable versions) we
32-
//! flip the switch. Projects on nightly will quickly start seeing changes, but
44+
//! new format into Cargo ASAP, but it won't be exercised yet (except in Cargo's
45+
//! own tests). Eventually when stable/beta/nightly all have support for the new
46+
//! format (and maybe a few previous stable versions) we flip the switch.
47+
//! Projects on nightly will quickly start seeing changes, but
3348
//! stable/beta/nightly will all understand this new format and will preserve
3449
//! it.
3550
//!

src/cargo/core/resolver/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ pub fn resolve(
161161
cksums,
162162
BTreeMap::new(),
163163
Vec::new(),
164-
ResolveVersion::default_for_new_lockfiles(),
164+
ResolveVersion::default(),
165165
summaries,
166166
);
167167

src/cargo/core/resolver/resolve.rs

+18-48
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::util::errors::CargoResult;
55
use crate::util::interning::InternedString;
66
use crate::util::Graph;
77
use std::borrow::Borrow;
8-
use std::cmp;
98
use std::collections::{HashMap, HashSet};
109
use std::fmt;
1110

@@ -219,35 +218,8 @@ unable to verify that `{0}` is the same as when the lockfile was generated
219218
// Be sure to just copy over any unknown metadata.
220219
self.metadata = previous.metadata.clone();
221220

222-
// The goal of Cargo is largely to preserve the encoding of `Cargo.lock`
223-
// that it finds on the filesystem. Sometimes `Cargo.lock` changes are
224-
// in the works where they haven't been set as the default yet but will
225-
// become the default soon.
226-
//
227-
// The scenarios we could be in are:
228-
//
229-
// * This is a brand new lock file with nothing previous. In that case
230-
// this method isn't actually called at all, but instead
231-
// `default_for_new_lockfiles` called below was encoded during the
232-
// resolution step, so that's what we're gonna use.
233-
//
234-
// * We have an old lock file. In this case we want to switch the
235-
// version to `default_for_old_lockfiles`. That puts us in one of
236-
// three cases:
237-
//
238-
// * Our version is older than the default. This means that we're
239-
// migrating someone forward, so we switch the encoding.
240-
// * Our version is equal to the default, nothing to do!
241-
// * Our version is *newer* than the default. This is where we
242-
// critically keep the new version of encoding.
243-
//
244-
// This strategy should get new lockfiles into the pipeline more quickly
245-
// while ensuring that any time an old cargo sees a future lock file it
246-
// keeps the future lockfile encoding.
247-
self.version = cmp::max(
248-
previous.version,
249-
ResolveVersion::default_for_old_lockfiles(),
250-
);
221+
// Preserve the lockfile encoding where possible to avoid lockfile churn
222+
self.version = previous.version;
251223

252224
Ok(())
253225
}
@@ -383,6 +355,10 @@ unable to verify that `{0}` is the same as when the lockfile was generated
383355
self.version
384356
}
385357

358+
pub fn set_version(&mut self, version: ResolveVersion) {
359+
self.version = version;
360+
}
361+
386362
pub fn summary(&self, pkg_id: PackageId) -> &Summary {
387363
&self.summaries[&pkg_id]
388364
}
@@ -418,27 +394,21 @@ impl fmt::Debug for Resolve {
418394
}
419395
}
420396

421-
impl ResolveVersion {
422-
/// The default way to encode new `Cargo.lock` files.
397+
impl Default for ResolveVersion {
398+
/// The default way to encode new or updated `Cargo.lock` files.
423399
///
424400
/// It's important that if a new version of `ResolveVersion` is added that
425401
/// this is not updated until *at least* the support for the version is in
426-
/// the stable release of Rust. It's ok for this to be newer than
427-
/// `default_for_old_lockfiles` below.
428-
pub fn default_for_new_lockfiles() -> ResolveVersion {
429-
ResolveVersion::V2
430-
}
431-
432-
/// The default way to encode old preexisting `Cargo.lock` files. This is
433-
/// often trailing the new lockfiles one above to give older projects a
434-
/// longer time to catch up.
402+
/// the stable release of Rust.
435403
///
436-
/// It's important that this trails behind `default_for_new_lockfiles` for
437-
/// quite some time. This gives projects a quite large window to update in
438-
/// where we don't force updates, so if projects span many versions of Cargo
439-
/// all those versions of Cargo will have support for a new version of the
440-
/// lock file.
441-
pub fn default_for_old_lockfiles() -> ResolveVersion {
442-
ResolveVersion::V1
404+
/// This resolve version will be used for all new lock files, for example
405+
/// those generated by `cargo update` (update everything) or building after
406+
/// a `cargo new` (where no lock file previously existed). This is also used
407+
/// for *updated* lock files such as when a dependency is added or when a
408+
/// version requirement changes. In this situation Cargo's updating the lock
409+
/// file anyway so it takes the opportunity to bump the lock file version
410+
/// forward.
411+
fn default() -> ResolveVersion {
412+
ResolveVersion::V2
443413
}
444414
}

src/cargo/ops/cargo_generate_lockfile.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub struct UpdateOptions<'a> {
2121

2222
pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
2323
let mut registry = PackageRegistry::new(ws.config())?;
24-
let resolve = ops::resolve_with_previous(
24+
let mut resolve = ops::resolve_with_previous(
2525
&mut registry,
2626
ws,
2727
&ResolveOpts::everything(),
@@ -30,7 +30,7 @@ pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
3030
&[],
3131
true,
3232
)?;
33-
ops::write_pkg_lockfile(ws, &resolve)?;
33+
ops::write_pkg_lockfile(ws, &mut resolve)?;
3434
Ok(())
3535
}
3636

@@ -113,7 +113,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
113113
registry.add_sources(sources)?;
114114
}
115115

116-
let resolve = ops::resolve_with_previous(
116+
let mut resolve = ops::resolve_with_previous(
117117
&mut registry,
118118
ws,
119119
&ResolveOpts::everything(),
@@ -153,7 +153,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
153153
.shell()
154154
.warn("not updating lockfile due to dry run")?;
155155
} else {
156-
ops::write_pkg_lockfile(ws, &resolve)?;
156+
ops::write_pkg_lockfile(ws, &mut resolve)?;
157157
}
158158
return Ok(());
159159

src/cargo/ops/cargo_package.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,14 @@ fn build_lock(ws: &Workspace<'_>) -> CargoResult<String> {
284284

285285
// Regenerate Cargo.lock using the old one as a guide.
286286
let tmp_ws = Workspace::ephemeral(new_pkg, ws.config(), None, true)?;
287-
let (pkg_set, new_resolve) = ops::resolve_ws(&tmp_ws)?;
287+
let (pkg_set, mut new_resolve) = ops::resolve_ws(&tmp_ws)?;
288288

289289
if let Some(orig_resolve) = orig_resolve {
290290
compare_resolve(config, tmp_ws.current()?, &orig_resolve, &new_resolve)?;
291291
}
292292
check_yanked(config, &pkg_set, &new_resolve)?;
293293

294-
ops::resolve_to_string(&tmp_ws, &new_resolve)
294+
ops::resolve_to_string(&tmp_ws, &mut new_resolve)
295295
}
296296

297297
// Checks that the package has some piece of metadata that a human can

src/cargo/ops/lockfile.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
2727
}
2828

2929
/// Generate a toml String of Cargo.lock from a Resolve.
30-
pub fn resolve_to_string(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<String> {
30+
pub fn resolve_to_string(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<String> {
3131
let (_orig, out, _ws_root) = resolve_to_string_orig(ws, resolve)?;
3232
Ok(out)
3333
}
3434

35-
pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<()> {
36-
let (orig, out, ws_root) = resolve_to_string_orig(ws, resolve)?;
35+
pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<()> {
36+
let (orig, mut out, ws_root) = resolve_to_string_orig(ws, resolve)?;
3737

3838
// If the lock file contents haven't changed so don't rewrite it. This is
3939
// helpful on read-only filesystems.
40-
if let Some(orig) = orig {
40+
if let Some(orig) = &orig {
4141
if are_equal_lockfiles(orig, &out, ws) {
4242
return Ok(());
4343
}
@@ -62,6 +62,16 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<
6262
);
6363
}
6464

65+
// While we're updating the lock file anyway go ahead and update its
66+
// encoding to whatever the latest default is. That way we can slowly roll
67+
// out lock file updates as they're otherwise already updated, and changes
68+
// which don't touch dependencies won't seemingly spuriously update the lock
69+
// file.
70+
if resolve.version() < ResolveVersion::default() {
71+
resolve.set_version(ResolveVersion::default());
72+
out = serialize_resolve(resolve, orig.as_deref());
73+
}
74+
6575
// Ok, if that didn't work just write it out
6676
ws_root
6777
.open_rw("Cargo.lock", ws.config(), "Cargo.lock file")
@@ -76,7 +86,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<
7686

7787
fn resolve_to_string_orig(
7888
ws: &Workspace<'_>,
79-
resolve: &Resolve,
89+
resolve: &mut Resolve,
8090
) -> CargoResult<(Option<String>, String, Filesystem)> {
8191
// Load the original lock file if it exists.
8292
let ws_root = Filesystem::new(ws.root().to_path_buf());
@@ -86,7 +96,11 @@ fn resolve_to_string_orig(
8696
f.read_to_string(&mut s)?;
8797
Ok(s)
8898
});
99+
let out = serialize_resolve(resolve, orig.as_ref().ok().map(|s| &**s));
100+
Ok((orig.ok(), out, ws_root))
101+
}
89102

103+
fn serialize_resolve(resolve: &Resolve, orig: Option<&str>) -> String {
90104
let toml = toml::Value::try_from(resolve).unwrap();
91105

92106
let mut out = String::new();
@@ -100,7 +114,7 @@ fn resolve_to_string_orig(
100114
out.push_str(extra_line);
101115
out.push('\n');
102116
// and preserve any other top comments
103-
if let Ok(orig) = &orig {
117+
if let Some(orig) = orig {
104118
let mut comments = orig.lines().take_while(|line| line.starts_with('#'));
105119
if let Some(first) = comments.next() {
106120
if first != marker_line {
@@ -156,11 +170,11 @@ fn resolve_to_string_orig(
156170
out.pop();
157171
}
158172
}
159-
160-
Ok((orig.ok(), out, ws_root))
173+
out
161174
}
162175

163-
fn are_equal_lockfiles(mut orig: String, current: &str, ws: &Workspace<'_>) -> bool {
176+
fn are_equal_lockfiles(orig: &str, current: &str, ws: &Workspace<'_>) -> bool {
177+
let mut orig = orig.to_string();
164178
if has_crlf_line_endings(&orig) {
165179
orig = orig.replace("\r\n", "\n");
166180
}

src/cargo/ops/resolve.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ fn resolve_with_registry<'cfg>(
165165
registry: &mut PackageRegistry<'cfg>,
166166
) -> CargoResult<Resolve> {
167167
let prev = ops::load_pkg_lockfile(ws)?;
168-
let resolve = resolve_with_previous(
168+
let mut resolve = resolve_with_previous(
169169
registry,
170170
ws,
171171
&ResolveOpts::everything(),
@@ -176,7 +176,7 @@ fn resolve_with_registry<'cfg>(
176176
)?;
177177

178178
if !ws.is_ephemeral() && ws.require_optional_deps() {
179-
ops::write_pkg_lockfile(ws, &resolve)?;
179+
ops::write_pkg_lockfile(ws, &mut resolve)?;
180180
}
181181
Ok(resolve)
182182
}

0 commit comments

Comments
 (0)