Skip to content

Commit 4ab2797

Browse files
committed
feat(lock): Print lockfile changes on all commands
1 parent 030da59 commit 4ab2797

File tree

166 files changed

+1304
-284
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

166 files changed

+1304
-284
lines changed

crates/cargo-test-support/src/compare.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ fn substitute_macros(input: &str) -> String {
204204
("[SCRAPING]", " Scraping"),
205205
("[FRESH]", " Fresh"),
206206
("[DIRTY]", " Dirty"),
207+
("[LOCKING]", " Locking"),
207208
("[UPDATING]", " Updating"),
208209
("[ADDING]", " Adding"),
209210
("[REMOVING]", " Removing"),

src/cargo/ops/cargo_generate_lockfile.rs

Lines changed: 180 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,19 @@ pub struct UpdateOptions<'a> {
2525

2626
pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
2727
let mut registry = PackageRegistry::new(ws.gctx())?;
28+
let previous_resolve = None;
2829
let mut resolve = ops::resolve_with_previous(
2930
&mut registry,
3031
ws,
3132
&CliFeatures::new_all(true),
3233
HasDevUnits::Yes,
33-
None,
34+
previous_resolve,
3435
None,
3536
&[],
3637
true,
3738
)?;
3839
ops::write_pkg_lockfile(ws, &mut resolve)?;
40+
print_lockfile_changes(ws.gctx(), previous_resolve, &resolve, &mut registry)?;
3941
Ok(())
4042
}
4143

@@ -164,7 +166,164 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
164166
Ok(())
165167
}
166168

167-
fn print_lockfile_updates(
169+
pub fn print_lockfile_changes(
170+
gctx: &GlobalContext,
171+
previous_resolve: Option<&Resolve>,
172+
resolve: &Resolve,
173+
registry: &mut PackageRegistry<'_>,
174+
) -> CargoResult<()> {
175+
if let Some(previous_resolve) = previous_resolve {
176+
print_lockfile_sync(gctx, previous_resolve, resolve, registry)
177+
} else {
178+
print_lockfile_generation(gctx, resolve, registry)
179+
}
180+
}
181+
182+
fn print_lockfile_generation(
183+
gctx: &GlobalContext,
184+
resolve: &Resolve,
185+
registry: &mut PackageRegistry<'_>,
186+
) -> CargoResult<()> {
187+
let mut shell = gctx.shell();
188+
189+
let diff = PackageDiff::new(&resolve);
190+
let num_pkgs: usize = diff.iter().map(|d| d.added.len()).sum();
191+
if num_pkgs <= 1 {
192+
// just ourself, nothing worth reporting
193+
return Ok(());
194+
}
195+
shell.status("Locking", format!("{num_pkgs} packages"))?;
196+
197+
for diff in diff {
198+
fn format_latest(version: semver::Version) -> String {
199+
let warn = style::WARN;
200+
format!(" {warn}(latest: v{version}){warn:#}")
201+
}
202+
let possibilities = if let Some(query) = diff.alternatives_query() {
203+
loop {
204+
match registry.query_vec(&query, QueryKind::Exact) {
205+
std::task::Poll::Ready(res) => {
206+
break res?;
207+
}
208+
std::task::Poll::Pending => registry.block_until_ready()?,
209+
}
210+
}
211+
} else {
212+
vec![]
213+
};
214+
215+
for package in diff.added.iter() {
216+
let latest = if !possibilities.is_empty() {
217+
possibilities
218+
.iter()
219+
.map(|s| s.as_summary())
220+
.filter(|s| is_latest(s.version(), package.version()))
221+
.map(|s| s.version().clone())
222+
.max()
223+
.map(format_latest)
224+
} else {
225+
None
226+
};
227+
228+
if let Some(latest) = latest {
229+
shell.status_with_color("Adding", format!("{package}{latest}"), &style::NOTE)?;
230+
}
231+
}
232+
}
233+
234+
Ok(())
235+
}
236+
237+
fn print_lockfile_sync(
238+
gctx: &GlobalContext,
239+
previous_resolve: &Resolve,
240+
resolve: &Resolve,
241+
registry: &mut PackageRegistry<'_>,
242+
) -> CargoResult<()> {
243+
let mut shell = gctx.shell();
244+
245+
let diff = PackageDiff::diff(&previous_resolve, &resolve);
246+
let num_pkgs: usize = diff.iter().map(|d| d.added.len()).sum();
247+
if num_pkgs == 0 {
248+
return Ok(());
249+
}
250+
let plural = if num_pkgs == 1 { "" } else { "s" };
251+
shell.status("Locking", format!("{num_pkgs} package{plural}"))?;
252+
253+
for diff in diff {
254+
fn format_latest(version: semver::Version) -> String {
255+
let warn = style::WARN;
256+
format!(" {warn}(latest: v{version}){warn:#}")
257+
}
258+
let possibilities = if let Some(query) = diff.alternatives_query() {
259+
loop {
260+
match registry.query_vec(&query, QueryKind::Exact) {
261+
std::task::Poll::Ready(res) => {
262+
break res?;
263+
}
264+
std::task::Poll::Pending => registry.block_until_ready()?,
265+
}
266+
}
267+
} else {
268+
vec![]
269+
};
270+
271+
if let Some((removed, added)) = diff.change() {
272+
let latest = if !possibilities.is_empty() {
273+
possibilities
274+
.iter()
275+
.map(|s| s.as_summary())
276+
.filter(|s| is_latest(s.version(), added.version()))
277+
.map(|s| s.version().clone())
278+
.max()
279+
.map(format_latest)
280+
} else {
281+
None
282+
}
283+
.unwrap_or_default();
284+
285+
let msg = if removed.source_id().is_git() {
286+
format!(
287+
"{removed} -> #{}",
288+
&added.source_id().precise_git_fragment().unwrap()[..8],
289+
)
290+
} else {
291+
format!("{removed} -> v{}{latest}", added.version())
292+
};
293+
294+
// If versions differ only in build metadata, we call it an "update"
295+
// regardless of whether the build metadata has gone up or down.
296+
// This metadata is often stuff like git commit hashes, which are
297+
// not meaningfully ordered.
298+
if removed.version().cmp_precedence(added.version()) == Ordering::Greater {
299+
shell.status_with_color("Downgrading", msg, &style::WARN)?;
300+
} else {
301+
shell.status_with_color("Updating", msg, &style::GOOD)?;
302+
}
303+
} else {
304+
for package in diff.added.iter() {
305+
let latest = if !possibilities.is_empty() {
306+
possibilities
307+
.iter()
308+
.map(|s| s.as_summary())
309+
.filter(|s| is_latest(s.version(), package.version()))
310+
.map(|s| s.version().clone())
311+
.max()
312+
.map(format_latest)
313+
} else {
314+
None
315+
}
316+
.unwrap_or_default();
317+
318+
shell.status_with_color("Adding", format!("{package}{latest}"), &style::NOTE)?;
319+
}
320+
}
321+
}
322+
323+
Ok(())
324+
}
325+
326+
pub fn print_lockfile_updates(
168327
gctx: &GlobalContext,
169328
previous_resolve: &Resolve,
170329
resolve: &Resolve,
@@ -317,11 +476,21 @@ pub struct PackageDiff {
317476
}
318477

319478
impl PackageDiff {
320-
pub fn diff(previous_resolve: &Resolve, resolve: &Resolve) -> Vec<Self> {
321-
fn key(dep: PackageId) -> (&'static str, SourceId) {
322-
(dep.name().as_str(), dep.source_id())
479+
pub fn new(resolve: &Resolve) -> Vec<Self> {
480+
let mut changes = BTreeMap::new();
481+
let empty = Self::default();
482+
for dep in resolve.iter() {
483+
changes
484+
.entry(Self::key(dep))
485+
.or_insert_with(|| empty.clone())
486+
.added
487+
.push(dep);
323488
}
324489

490+
changes.into_iter().map(|(_, v)| v).collect()
491+
}
492+
493+
pub fn diff(previous_resolve: &Resolve, resolve: &Resolve) -> Vec<Self> {
325494
fn vec_subset(a: &[PackageId], b: &[PackageId]) -> Vec<PackageId> {
326495
a.iter().filter(|a| !contains_id(b, a)).cloned().collect()
327496
}
@@ -362,14 +531,14 @@ impl PackageDiff {
362531
let empty = Self::default();
363532
for dep in previous_resolve.iter() {
364533
changes
365-
.entry(key(dep))
534+
.entry(Self::key(dep))
366535
.or_insert_with(|| empty.clone())
367536
.removed
368537
.push(dep);
369538
}
370539
for dep in resolve.iter() {
371540
changes
372-
.entry(key(dep))
541+
.entry(Self::key(dep))
373542
.or_insert_with(|| empty.clone())
374543
.added
375544
.push(dep);
@@ -395,6 +564,10 @@ impl PackageDiff {
395564
changes.into_iter().map(|(_, v)| v).collect()
396565
}
397566

567+
fn key(dep: PackageId) -> (&'static str, SourceId) {
568+
(dep.name().as_str(), dep.source_id())
569+
}
570+
398571
/// Guess if a package upgraded/downgraded
399572
///
400573
/// All `PackageDiff` knows is that entries were added/removed within [`Resolve`].

src/cargo/ops/lockfile.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,18 @@ pub fn resolve_to_string(ws: &Workspace<'_>, resolve: &Resolve) -> CargoResult<S
3333
Ok(out)
3434
}
3535

36+
/// Ensure the resolve result is written to fisk
37+
///
38+
/// Returns `true` if the lockfile changed
3639
#[tracing::instrument(skip_all)]
37-
pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<()> {
40+
pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<bool> {
3841
let (orig, mut out, lock_root) = resolve_to_string_orig(ws, resolve);
3942

4043
// If the lock file contents haven't changed so don't rewrite it. This is
4144
// helpful on read-only filesystems.
4245
if let Some(orig) = &orig {
4346
if are_equal_lockfiles(orig, &out, ws) {
44-
return Ok(());
47+
return Ok(false);
4548
}
4649
}
4750

@@ -93,7 +96,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
9396
lock_root.as_path_unlocked().join("Cargo.lock").display()
9497
)
9598
})?;
96-
Ok(())
99+
Ok(true)
97100
}
98101

99102
fn resolve_to_string_orig(

src/cargo/ops/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub use self::cargo_compile::{CompileFilter, FilterRule, LibRule, Packages};
88
pub use self::cargo_doc::{doc, DocOptions, OutputFormat};
99
pub use self::cargo_fetch::{fetch, FetchOptions};
1010
pub use self::cargo_generate_lockfile::generate_lockfile;
11+
pub use self::cargo_generate_lockfile::print_lockfile_changes;
12+
pub use self::cargo_generate_lockfile::print_lockfile_updates;
1113
pub use self::cargo_generate_lockfile::update_lockfile;
1214
pub use self::cargo_generate_lockfile::UpdateOptions;
1315
pub use self::cargo_install::{install, install_list};

src/cargo/ops/resolve.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,18 @@ fn resolve_with_registry<'gctx>(
250250
true,
251251
)?;
252252

253-
if !ws.is_ephemeral() && ws.require_optional_deps() {
254-
ops::write_pkg_lockfile(ws, &mut resolve)?;
253+
let print = if !ws.is_ephemeral() && ws.require_optional_deps() {
254+
ops::write_pkg_lockfile(ws, &mut resolve)?
255+
} else {
256+
false
257+
};
258+
if print {
259+
// We only want one Cargo at a time resolving a crate graph since this can
260+
// involve a lot of frobbing of the global caches.
261+
let _lock = ws
262+
.gctx()
263+
.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
264+
ops::print_lockfile_changes(ws.gctx(), prev.as_ref(), &resolve, registry)?;
255265
}
256266
Ok(resolve)
257267
}

tests/testsuite/alt_registry.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn depend_on_alt_registry() {
3333
.with_stderr(
3434
"\
3535
[UPDATING] `alternative` index
36+
[LOCKING] 2 packages
3637
[DOWNLOADING] crates ...
3738
[DOWNLOADED] bar v0.0.1 (registry `alternative`)
3839
[CHECKING] bar v0.0.1 (registry `alternative`)
@@ -87,6 +88,7 @@ fn depend_on_alt_registry_depends_on_same_registry_no_index() {
8788
.with_stderr(
8889
"\
8990
[UPDATING] `alternative` index
91+
[LOCKING] 3 packages
9092
[DOWNLOADING] crates ...
9193
[DOWNLOADED] [..] v0.0.1 (registry `alternative`)
9294
[DOWNLOADED] [..] v0.0.1 (registry `alternative`)
@@ -130,6 +132,7 @@ fn depend_on_alt_registry_depends_on_same_registry() {
130132
.with_stderr(
131133
"\
132134
[UPDATING] `alternative` index
135+
[LOCKING] 3 packages
133136
[DOWNLOADING] crates ...
134137
[DOWNLOADED] [..] v0.0.1 (registry `alternative`)
135138
[DOWNLOADED] [..] v0.0.1 (registry `alternative`)
@@ -174,6 +177,7 @@ fn depend_on_alt_registry_depends_on_crates_io() {
174177
"\
175178
[UPDATING] `alternative` index
176179
[UPDATING] `dummy-registry` index
180+
[LOCKING] 3 packages
177181
[DOWNLOADING] crates ...
178182
[DOWNLOADED] baz v0.0.1 (registry `dummy-registry`)
179183
[DOWNLOADED] bar v0.0.1 (registry `alternative`)
@@ -213,6 +217,7 @@ fn registry_and_path_dep_works() {
213217
p.cargo("check")
214218
.with_stderr(
215219
"\
220+
[LOCKING] 2 packages
216221
[CHECKING] bar v0.0.1 ([CWD]/bar)
217222
[CHECKING] foo v0.0.1 ([CWD])
218223
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..]s
@@ -416,6 +421,7 @@ fn alt_registry_and_crates_io_deps() {
416421
"\
417422
[UPDATING] `alternative` index
418423
[UPDATING] `dummy-registry` index
424+
[LOCKING] 3 packages
419425
[DOWNLOADING] crates ...
420426
[DOWNLOADED] crates_io_dep v0.0.1 (registry `dummy-registry`)
421427
[DOWNLOADED] alt_reg_dep v0.1.0 (registry `alternative`)
@@ -692,6 +698,7 @@ fn patch_alt_reg() {
692698
.with_stderr(
693699
"\
694700
[UPDATING] `alternative` index
701+
[LOCKING] 2 packages
695702
[CHECKING] bar v0.1.0 ([CWD]/bar)
696703
[CHECKING] foo v0.0.1 ([CWD])
697704
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..]
@@ -784,6 +791,7 @@ fn no_api() {
784791
.with_stderr(
785792
"\
786793
[UPDATING] `alternative` index
794+
[LOCKING] 2 packages
787795
[DOWNLOADING] crates ...
788796
[DOWNLOADED] bar v0.0.1 (registry `alternative`)
789797
[CHECKING] bar v0.0.1 (registry `alternative`)
@@ -1346,6 +1354,7 @@ fn registries_index_relative_url() {
13461354
.with_stderr(
13471355
"\
13481356
[UPDATING] `relative` index
1357+
[LOCKING] 2 packages
13491358
[DOWNLOADING] crates ...
13501359
[DOWNLOADED] bar v0.0.1 (registry `relative`)
13511360
[CHECKING] bar v0.0.1 (registry `relative`)

0 commit comments

Comments
 (0)