Skip to content

Commit bac4884

Browse files
committed
Auto merge of #4646 - alexcrichton:progress, r=matklad
Add a number of progress indicators to Cargo This commit is an attempt to stem the tide of "cargo is stuck updating the registry" issues by giving a better indication as to what's happening in long-running steps. The primary addition here is a `Progress` helper module which prints and manages a progress bar for long-running operations like git fetches, git checkouts, HTTP downloads, etc. The second addition here is to print out when we've been stuck in resolve for some time. We never really have a progress indicator for crate graph resolution nor do we know when we're done updating sources. Instead we make a naive assumption that when you've spent 0.5s in the resolution loop itself (not updating deps) you're probably done updating dependencies and on to acutal resolution. This will print out `Resolving crate graph...` and help inform that Cargo is indeed not stuck looking at the registry, but rather it's churning away in resolution. **Downloading all Servo's dependencies** [![asciicast](https://asciinema.org/a/JX9yQZtyFo5ED0Pwg45barBco.png)](https://asciinema.org/a/JX9yQZtyFo5ED0Pwg45barBco) **Long running resolution** [![asciicast](https://asciinema.org/a/p7xAkSVeMlkyvgcI6Gx7DZjAV.png)](https://asciinema.org/a/p7xAkSVeMlkyvgcI6Gx7DZjAV)
2 parents b83550e + 143b060 commit bac4884

File tree

9 files changed

+342
-91
lines changed

9 files changed

+342
-91
lines changed

src/cargo/core/resolver/mod.rs

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@
4747
4848
use std::cmp::Ordering;
4949
use std::collections::{HashSet, HashMap, BinaryHeap, BTreeMap};
50-
use std::iter::FromIterator;
5150
use std::fmt;
51+
use std::iter::FromIterator;
5252
use std::ops::Range;
5353
use std::rc::Rc;
54+
use std::time::{Instant, Duration};
5455

5556
use semver;
5657
use url::Url;
@@ -362,7 +363,8 @@ type Activations = HashMap<String, HashMap<SourceId, Vec<Summary>>>;
362363
pub fn resolve(summaries: &[(Summary, Method)],
363364
replacements: &[(PackageIdSpec, Dependency)],
364365
registry: &mut Registry,
365-
config: Option<&Config>) -> CargoResult<Resolve> {
366+
config: Option<&Config>,
367+
print_warnings: bool) -> CargoResult<Resolve> {
366368
let cx = Context {
367369
resolve_graph: RcList::new(),
368370
resolve_features: HashMap::new(),
@@ -372,7 +374,7 @@ pub fn resolve(summaries: &[(Summary, Method)],
372374
warnings: RcList::new(),
373375
};
374376
let _p = profile::start("resolving");
375-
let cx = activate_deps_loop(cx, registry, summaries)?;
377+
let cx = activate_deps_loop(cx, registry, summaries, config)?;
376378

377379
let mut resolve = Resolve {
378380
graph: cx.graph(),
@@ -398,11 +400,13 @@ pub fn resolve(summaries: &[(Summary, Method)],
398400

399401
// If we have a shell, emit warnings about required deps used as feature.
400402
if let Some(config) = config {
401-
let mut shell = config.shell();
402-
let mut warnings = &cx.warnings;
403-
while let Some(ref head) = warnings.head {
404-
shell.warn(&head.0)?;
405-
warnings = &head.1;
403+
if print_warnings {
404+
let mut shell = config.shell();
405+
let mut warnings = &cx.warnings;
406+
while let Some(ref head) = warnings.head {
407+
shell.warn(&head.0)?;
408+
warnings = &head.1;
409+
}
406410
}
407411
}
408412

@@ -420,7 +424,7 @@ fn activate(cx: &mut Context,
420424
parent: Option<&Summary>,
421425
candidate: Candidate,
422426
method: &Method)
423-
-> CargoResult<Option<DepsFrame>> {
427+
-> CargoResult<Option<(DepsFrame, Duration)>> {
424428
if let Some(parent) = parent {
425429
cx.resolve_graph.push(GraphNode::Link(parent.package_id().clone(),
426430
candidate.summary.package_id().clone()));
@@ -448,12 +452,13 @@ fn activate(cx: &mut Context,
448452
}
449453
};
450454

455+
let now = Instant::now();
451456
let deps = cx.build_deps(registry, &candidate, method)?;
452-
453-
Ok(Some(DepsFrame {
457+
let frame = DepsFrame {
454458
parent: candidate,
455459
remaining_siblings: RcVecIter::new(Rc::new(deps)),
456-
}))
460+
};
461+
Ok(Some((frame, now.elapsed())))
457462
}
458463

459464
struct RcVecIter<T> {
@@ -580,7 +585,8 @@ impl RemainingCandidates {
580585
/// dependency graph, cx.resolve is returned.
581586
fn activate_deps_loop<'a>(mut cx: Context<'a>,
582587
registry: &mut Registry,
583-
summaries: &[(Summary, Method)])
588+
summaries: &[(Summary, Method)],
589+
config: Option<&Config>)
584590
-> CargoResult<Context<'a>> {
585591
// Note that a `BinaryHeap` is used for the remaining dependencies that need
586592
// activation. This heap is sorted such that the "largest value" is the most
@@ -594,10 +600,18 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
594600
for &(ref summary, ref method) in summaries {
595601
debug!("initial activation: {}", summary.package_id());
596602
let candidate = Candidate { summary: summary.clone(), replace: None };
597-
remaining_deps.extend(activate(&mut cx, registry, None, candidate,
598-
method)?);
603+
let res = activate(&mut cx, registry, None, candidate, method)?;
604+
if let Some((frame, _)) = res {
605+
remaining_deps.push(frame);
606+
}
599607
}
600608

609+
let mut ticks = 0;
610+
let start = Instant::now();
611+
let time_to_print = Duration::from_millis(500);
612+
let mut printed = false;
613+
let mut deps_time = Duration::new(0, 0);
614+
601615
// Main resolution loop, this is the workhorse of the resolution algorithm.
602616
//
603617
// You'll note that a few stacks are maintained on the side, which might
@@ -612,6 +626,28 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
612626
// backtracking states where if we hit an error we can return to in order to
613627
// attempt to continue resolving.
614628
while let Some(mut deps_frame) = remaining_deps.pop() {
629+
630+
// If we spend a lot of time here (we shouldn't in most cases) then give
631+
// a bit of a visual indicator as to what we're doing. Only enable this
632+
// when stderr is a tty (a human is likely to be watching) to ensure we
633+
// get deterministic output otherwise when observed by tools.
634+
//
635+
// Also note that we hit this loop a lot, so it's fairly performance
636+
// sensitive. As a result try to defer a possibly expensive operation
637+
// like `Instant::now` by only checking every N iterations of this loop
638+
// to amortize the cost of the current time lookup.
639+
ticks += 1;
640+
if let Some(config) = config {
641+
if config.shell().is_err_tty() &&
642+
!printed &&
643+
ticks % 1000 == 0 &&
644+
start.elapsed() - deps_time > time_to_print
645+
{
646+
printed = true;
647+
config.shell().status("Resolving", "dependency graph...")?;
648+
}
649+
}
650+
615651
let frame = match deps_frame.remaining_siblings.next() {
616652
Some(sibling) => {
617653
let parent = Summary::clone(&deps_frame.parent);
@@ -695,8 +731,11 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
695731
};
696732
trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(),
697733
candidate.summary.version());
698-
remaining_deps.extend(activate(&mut cx, registry, Some(&parent),
699-
candidate, &method)?);
734+
let res = activate(&mut cx, registry, Some(&parent), candidate, &method)?;
735+
if let Some((frame, dur)) = res {
736+
remaining_deps.push(frame);
737+
deps_time += dur;
738+
}
700739
}
701740

702741
Ok(cx)

0 commit comments

Comments
 (0)