-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consistently use crate::display_error on errors during drain #10394
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -77,6 +77,7 @@ use crate::core::compiler::future_incompat::{ | |
use crate::core::resolver::ResolveBehavior; | ||
use crate::core::{PackageId, Shell, TargetKind}; | ||
use crate::util::diagnostic_server::{self, DiagnosticPrinter}; | ||
use crate::util::errors::AlreadyPrintedError; | ||
use crate::util::machine_message::{self, Message as _}; | ||
use crate::util::CargoResult; | ||
use crate::util::{self, internal, profile}; | ||
|
@@ -169,6 +170,10 @@ struct DrainState<'cfg> { | |
per_package_future_incompat_reports: Vec<FutureIncompatReportPackage>, | ||
} | ||
|
||
pub struct ErrorsDuringDrain { | ||
pub count: usize, | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
pub struct JobId(pub u32); | ||
|
||
|
@@ -786,14 +791,14 @@ impl<'cfg> DrainState<'cfg> { | |
// After a job has finished we update our internal state if it was | ||
// successful and otherwise wait for pending work to finish if it failed | ||
// and then immediately return. | ||
let mut error = None; | ||
let mut errors = ErrorsDuringDrain { count: 0 }; | ||
// CAUTION! Do not use `?` or break out of the loop early. Every error | ||
// must be handled in such a way that the loop is still allowed to | ||
// drain event messages. | ||
loop { | ||
if error.is_none() { | ||
if errors.count == 0 { | ||
if let Err(e) = self.spawn_work_if_possible(cx, jobserver_helper, scope) { | ||
self.handle_error(&mut cx.bcx.config.shell(), &mut error, e); | ||
self.handle_error(&mut cx.bcx.config.shell(), &mut errors, e); | ||
} | ||
} | ||
|
||
|
@@ -804,7 +809,7 @@ impl<'cfg> DrainState<'cfg> { | |
} | ||
|
||
if let Err(e) = self.grant_rustc_token_requests() { | ||
self.handle_error(&mut cx.bcx.config.shell(), &mut error, e); | ||
self.handle_error(&mut cx.bcx.config.shell(), &mut errors, e); | ||
} | ||
|
||
// And finally, before we block waiting for the next event, drop any | ||
|
@@ -814,7 +819,7 @@ impl<'cfg> DrainState<'cfg> { | |
// to the jobserver itself. | ||
for event in self.wait_for_events() { | ||
if let Err(event_err) = self.handle_event(cx, jobserver_helper, plan, event) { | ||
self.handle_error(&mut cx.bcx.config.shell(), &mut error, event_err); | ||
self.handle_error(&mut cx.bcx.config.shell(), &mut errors, event_err); | ||
} | ||
} | ||
} | ||
|
@@ -839,30 +844,24 @@ impl<'cfg> DrainState<'cfg> { | |
} | ||
|
||
let time_elapsed = util::elapsed(cx.bcx.config.creation_time().elapsed()); | ||
if let Err(e) = self.timings.finished(cx, &error) { | ||
if error.is_some() { | ||
crate::display_error(&e, &mut cx.bcx.config.shell()); | ||
} else { | ||
return Some(e); | ||
} | ||
if let Err(e) = self.timings.finished(cx, &errors.to_error()) { | ||
self.handle_error(&mut cx.bcx.config.shell(), &mut errors, e); | ||
} | ||
if cx.bcx.build_config.emit_json() { | ||
let mut shell = cx.bcx.config.shell(); | ||
let msg = machine_message::BuildFinished { | ||
success: error.is_none(), | ||
success: errors.count == 0, | ||
} | ||
.to_json_string(); | ||
if let Err(e) = writeln!(shell.out(), "{}", msg) { | ||
if error.is_some() { | ||
crate::display_error(&e.into(), &mut shell); | ||
} else { | ||
return Some(e.into()); | ||
} | ||
self.handle_error(&mut shell, &mut errors, e.into()); | ||
} | ||
} | ||
|
||
if let Some(e) = error { | ||
Some(e) | ||
if let Some(error) = errors.to_error() { | ||
// Any errors up to this point have already been printed via the | ||
// `display_error` inside `handle_error`. | ||
Some(anyhow::Error::new(AlreadyPrintedError::new(error))) | ||
} else if self.queue.is_empty() && self.pending_queue.is_empty() { | ||
let message = format!( | ||
"{} [{}] target(s) in {}", | ||
|
@@ -887,19 +886,14 @@ impl<'cfg> DrainState<'cfg> { | |
fn handle_error( | ||
&self, | ||
shell: &mut Shell, | ||
err_state: &mut Option<anyhow::Error>, | ||
err_state: &mut ErrorsDuringDrain, | ||
new_err: anyhow::Error, | ||
) { | ||
if err_state.is_some() { | ||
// Already encountered one error. | ||
log::warn!("{:?}", new_err); | ||
} else if !self.active.is_empty() { | ||
crate::display_error(&new_err, shell); | ||
crate::display_error(&new_err, shell); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't fully understand why, but this breaks The failure looks like: ---- build::close_output stdout ----
thread 'build::close_output' panicked at 'called `Result::unwrap()` on an `Err` value: Expected lines did not match (ignoring order):
0 100002 Compiling foo v0.1.0 (/home/runner/work/cargo/cargo/target/tmp/cit/t231/foo)
1 0 hello stderr!
2 0 error: Broken pipe (os error 32)
3 99999 warning: build failed, waiting for other jobs to finish...
4 0 error: Broken pipe (os error 32)
5 +error: Broken pipe (os error 32)
6 +error: Broken pipe (os error 32)
7 +error: Broken pipe (os error 32)
8 +error: Broken pipe (os error 32)
9 +error: Broken pipe (os error 32)
10 +error: Broken pipe (os error 32)
11 +error: Broken pipe (os error 32)
12 +error: Broken pipe (os error 32)
13 +error: Broken pipe (os error 32)
14 +error: Broken pipe (os error 32)
15 +error: Broken pipe (os error 32)
16 +error: Broken pipe (os error 32)
17 +error: Broken pipe (os error 32)
18 +error: Broken pipe (os error 32)
19 +error: Broken pipe (os error 32)
20 +error: Broken pipe (os error 32)
[...]
99989 +error: Broken pipe (os error 32)
99990 +error: Broken pipe (os error 32)
99991 +error: Broken pipe (os error 32)
99992 +error: Broken pipe (os error 32)
99993 +error: Broken pipe (os error 32)
99994 +error: Broken pipe (os error 32)
99995 +error: Broken pipe (os error 32)
99996 +error: Broken pipe (os error 32)
99997 +error: Broken pipe (os error 32)
99998 +error: Broken pipe (os error 32)
99999 +error: Broken pipe (os error 32)
100000 +error: Broken pipe (os error 32)
100001 +error: Broken pipe (os error 32)
100002 +error: Broken pipe (os error 32)
', tests/testsuite/build.rs:5680:6
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
build::close_output
test result: FAILED. 2298 passed; 1 failed; 6 ignored; 0 measured; 0 filtered out; finished in 506.90s The test involves a proc macro where we close its stdout and then execute The test passes after the following diff: - crate::display_error(&new_err, shell);
+ if err_state.count > 1 {
+ log::warn!("{:?}", new_err);
+ } else {
+ crate::display_error(&new_err, shell);
+ } There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe the reason for this is that the main loop here gets one-line-at-a-time from the proc-macro, which is 100k prints. Each print fails because of a broken pipe, so Cargo tries to handle the error 100k times. Short of specifically handling broken pipe errors here I'm not entirely sure what to do. We don't want to drop errors on the floor, especially with your future PR of continuing on error, so they need to be handled somehow I think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little concerned specifically about this change (and I realize it is the crux of this PR). It was pretty intentional to not show any errors after the first due to scenarios like this, where Cargo could just spew a bunch of error messages, but only one is really relevant. Perhaps it is possible to put errors into two categories, one which is "important" and should be shown no matter what, and the other where it should only be shown if no other error has been shown? I think the only error that fits the former category is this one, which is the "could not compile foo…" error. The rest of the errors are all things like failed to write to stdout or other internal errors that aren't too critical to display if something else has gone wrong. Would it be possible to wrap that error up in such a way that it will only show duplicates for that? That will still result in more errors being display (imagine there are 16 crates building in parallel and they all fail), but I think that's probably fine (and maybe even preferred?). There still would be some risk that some relevant error would get elided, but at the moment I don't see any that really matter (and wouldn't be any worse than it is today). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have update the PR to revert to |
||
if !self.active.is_empty() && err_state.count == 0 { | ||
drop(shell.warn("build failed, waiting for other jobs to finish...")); | ||
*err_state = Some(anyhow::format_err!("build failed")); | ||
} else { | ||
*err_state = Some(new_err); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In response to:
|
||
err_state.count += 1; | ||
} | ||
|
||
// This also records CPU usage and marks concurrency; we roughly want to do | ||
|
@@ -1216,3 +1210,13 @@ feature resolver. Try updating to diesel 1.4.8 to fix this error. | |
Ok(()) | ||
} | ||
} | ||
|
||
impl ErrorsDuringDrain { | ||
fn to_error(&self) -> Option<anyhow::Error> { | ||
match self.count { | ||
0 => None, | ||
1 => Some(format_err!("1 job failed")), | ||
n => Some(format_err!("{} jobs failed", n)), | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In response to:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I think it should be fine. It's been a while, but I think my thought process was that "if something has gone wrong, might as well exit right away". The consequence is that you might end up with multiple errors displayed.