Skip to content

Commit 3dbe701

Browse files
committed
Displays a one line progress of what crates are currently built.
cc #2536, #3448.
1 parent 9e4845e commit 3dbe701

File tree

2 files changed

+67
-28
lines changed

2 files changed

+67
-28
lines changed

src/cargo/core/compiler/job_queue.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use core::{PackageId, Target};
1414
use handle_error;
1515
use util::{internal, profile, CargoResult, CargoResultExt, ProcessBuilder};
1616
use util::{Config, DependencyQueue, Dirty, Fresh, Freshness};
17+
use util::Progress;
1718

1819
use super::job::Job;
1920
use super::{BuildContext, BuildPlan, CompileMode, Context, Kind, Unit};
@@ -28,7 +29,7 @@ pub struct JobQueue<'a> {
2829
queue: DependencyQueue<Key<'a>, Vec<(Job, Freshness)>>,
2930
tx: Sender<Message<'a>>,
3031
rx: Receiver<Message<'a>>,
31-
active: usize,
32+
active: HashSet<Key<'a>>,
3233
pending: HashMap<Key<'a>, PendingBuild>,
3334
compiled: HashSet<&'a PackageId>,
3435
documented: HashSet<&'a PackageId>,
@@ -98,7 +99,7 @@ impl<'a> JobQueue<'a> {
9899
queue: DependencyQueue::new(),
99100
tx,
100101
rx,
101-
active: 0,
102+
active: HashSet::new(),
102103
pending: HashMap::new(),
103104
compiled: HashSet::new(),
104105
documented: HashSet::new(),
@@ -180,6 +181,8 @@ impl<'a> JobQueue<'a> {
180181
// successful and otherwise wait for pending work to finish if it failed
181182
// and then immediately return.
182183
let mut error = None;
184+
let mut progress = Progress::new("Building", cx.bcx.config);
185+
let queue_len = self.queue.len();
183186
loop {
184187
// Dequeue as much work as we can, learning about everything
185188
// possible that can run. Note that this is also the point where we
@@ -196,7 +199,7 @@ impl<'a> JobQueue<'a> {
196199
);
197200
for (job, f) in jobs {
198201
queue.push((key, job, f.combine(fresh)));
199-
if self.active + queue.len() > 0 {
202+
if !self.active.is_empty() || !queue.is_empty() {
200203
jobserver_helper.request_token();
201204
}
202205
}
@@ -205,14 +208,14 @@ impl<'a> JobQueue<'a> {
205208
// Now that we've learned of all possible work that we can execute
206209
// try to spawn it so long as we've got a jobserver token which says
207210
// we're able to perform some parallel work.
208-
while error.is_none() && self.active < tokens.len() + 1 && !queue.is_empty() {
211+
while error.is_none() && self.active.len() < tokens.len() + 1 && !queue.is_empty() {
209212
let (key, job, fresh) = queue.remove(0);
210213
self.run(key, fresh, job, cx.bcx.config, scope, build_plan)?;
211214
}
212215

213216
// If after all that we're not actually running anything then we're
214217
// done!
215-
if self.active == 0 {
218+
if self.active.is_empty() {
216219
break;
217220
}
218221

@@ -221,9 +224,19 @@ impl<'a> JobQueue<'a> {
221224
// jobserver interface is architected we may acquire a token that we
222225
// don't actually use, and if this happens just relinquish it back
223226
// to the jobserver itself.
224-
tokens.truncate(self.active - 1);
225-
226-
match self.rx.recv().unwrap() {
227+
tokens.truncate(self.active.len() - 1);
228+
229+
let count = queue_len - self.queue.len();
230+
let mut active_names = self.active.iter().map(|key| match key.mode {
231+
CompileMode::Doc { .. } => format!("{}(doc)", key.pkg.name()),
232+
_ => key.pkg.name().to_string(),
233+
}).collect::<Vec<_>>();
234+
active_names.sort_unstable();
235+
drop(progress.tick_now(count, queue_len, format!(": {}", active_names.join(", "))));
236+
let event = self.rx.recv().unwrap();
237+
progress.clear();
238+
239+
match event {
227240
Message::Run(cmd) => {
228241
cx.bcx
229242
.config
@@ -245,8 +258,9 @@ impl<'a> JobQueue<'a> {
245258
}
246259
Message::Finish(key, result) => {
247260
info!("end: {:?}", key);
248-
self.active -= 1;
249-
if self.active > 0 {
261+
262+
self.active.remove(&key);
263+
if !self.active.is_empty() {
250264
assert!(!tokens.is_empty());
251265
drop(tokens.pop());
252266
}
@@ -256,7 +270,7 @@ impl<'a> JobQueue<'a> {
256270
let msg = "The following warnings were emitted during compilation:";
257271
self.emit_warnings(Some(msg), &key, cx)?;
258272

259-
if self.active > 0 {
273+
if !self.active.is_empty() {
260274
error = Some(format_err!("build failed"));
261275
handle_error(e, &mut *cx.bcx.config.shell());
262276
cx.bcx.config.shell().warn(
@@ -274,6 +288,7 @@ impl<'a> JobQueue<'a> {
274288
}
275289
}
276290
}
291+
drop(progress);
277292

278293
let build_type = if self.is_release { "release" } else { "dev" };
279294
// NOTE: This may be a bit inaccurate, since this may not display the
@@ -334,7 +349,7 @@ impl<'a> JobQueue<'a> {
334349
) -> CargoResult<()> {
335350
info!("start: {:?}", key);
336351

337-
self.active += 1;
352+
self.active.insert(key);
338353
*self.counts.get_mut(key.pkg).unwrap() -= 1;
339354

340355
let my_tx = self.tx.clone();

src/cargo/util/progress.rs

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::cmp;
22
use std::env;
3-
use std::iter;
43
use std::time::{Duration, Instant};
54

65
use core::shell::Verbosity;
@@ -12,6 +11,7 @@ pub struct Progress<'cfg> {
1211

1312
struct State<'cfg> {
1413
config: &'cfg Config,
14+
max_width: usize,
1515
width: usize,
1616
first: bool,
1717
last_update: Instant,
@@ -33,6 +33,7 @@ impl<'cfg> Progress<'cfg> {
3333
Progress {
3434
state: cfg.shell().err_width().map(|n| State {
3535
config: cfg,
36+
max_width: n,
3637
width: cmp::min(n, 80),
3738
first: true,
3839
last_update: Instant::now(),
@@ -44,14 +45,27 @@ impl<'cfg> Progress<'cfg> {
4445

4546
pub fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
4647
match self.state {
47-
Some(ref mut s) => s.tick(cur, max),
48+
Some(ref mut s) => s.tick(cur, max, String::new(), true),
49+
None => Ok(()),
50+
}
51+
}
52+
53+
pub fn clear(&mut self) {
54+
if let Some(ref mut s) = self.state {
55+
clear(s.max_width, s.config);
56+
}
57+
}
58+
59+
pub fn tick_now(&mut self, cur: usize, max: usize, msg: String) -> CargoResult<()> {
60+
match self.state {
61+
Some(ref mut s) => s.tick(cur, max, msg, false),
4862
None => Ok(()),
4963
}
5064
}
5165
}
5266

5367
impl<'cfg> State<'cfg> {
54-
fn tick(&mut self, cur: usize, max: usize) -> CargoResult<()> {
68+
fn tick(&mut self, cur: usize, max: usize, msg: String, throttle: bool) -> CargoResult<()> {
5569
if self.done {
5670
return Ok(());
5771
}
@@ -68,19 +82,21 @@ impl<'cfg> State<'cfg> {
6882
// 2. If we've drawn something, then we rate limit ourselves to only
6983
// draw to the console every so often. Currently there's a 100ms
7084
// delay between updates.
71-
if self.first {
72-
let delay = Duration::from_millis(500);
73-
if self.last_update.elapsed() < delay {
74-
return Ok(());
75-
}
76-
self.first = false;
77-
} else {
78-
let interval = Duration::from_millis(100);
79-
if self.last_update.elapsed() < interval {
80-
return Ok(());
85+
if throttle {
86+
if self.first {
87+
let delay = Duration::from_millis(500);
88+
if self.last_update.elapsed() < delay {
89+
return Ok(());
90+
}
91+
self.first = false;
92+
} else {
93+
let interval = Duration::from_millis(100);
94+
if self.last_update.elapsed() < interval {
95+
return Ok(());
96+
}
8197
}
98+
self.last_update = Instant::now();
8299
}
83-
self.last_update = Instant::now();
84100

85101
// Render the percentage at the far right and then figure how long the
86102
// progress bar is
@@ -116,6 +132,14 @@ impl<'cfg> State<'cfg> {
116132
string.push_str("]");
117133
string.push_str(&stats);
118134

135+
let avail_msg_len = self.max_width - self.width;
136+
if avail_msg_len >= msg.len() + 3 {
137+
string.push_str(&msg);
138+
} else if avail_msg_len >= 4 {
139+
string.push_str(&msg[..(avail_msg_len - 3)]);
140+
string.push_str("...");
141+
}
142+
119143
// Write out a pretty header, then the progress bar itself, and then
120144
// return back to the beginning of the line for the next print.
121145
self.config.shell().status_header(&self.name)?;
@@ -125,12 +149,12 @@ impl<'cfg> State<'cfg> {
125149
}
126150

127151
fn clear(width: usize, config: &Config) {
128-
let blank = iter::repeat(" ").take(width).collect::<String>();
152+
let blank = " ".repeat(width);
129153
drop(write!(config.shell().err(), "{}\r", blank));
130154
}
131155

132156
impl<'cfg> Drop for State<'cfg> {
133157
fn drop(&mut self) {
134-
clear(self.width, self.config);
158+
clear(self.max_width, self.config);
135159
}
136160
}

0 commit comments

Comments
 (0)