Skip to content

Commit cbd1b0b

Browse files
committed
Give dynamically generated instructions on how to replicate errors during the build progress
Example output: ``` Checking stage0 rustdoc artifacts (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu) Checking rustdoc v0.0.0 (/home/joshua/rustc/src/librustdoc) error: expected item, found `/` --> src/librustdoc/lib.rs:1:1 | 1 | / | ^ expected item error: aborting due to previous error error: could not compile `rustdoc` To learn more, run the command again with --verbose. command did not execute successfully: "/home/joshua/rustc/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "check" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-j" "8" "--release" "--manifest-path" "/home/joshua/rustc/src/tools/rustdoc/Cargo.toml" "--message-format" "json-render-diagnostics" expected success, got: exit status: 101 note: failed while building bootstrap::check::Rustdoc help: to replicate this failure, run `./x.py check src/librustdoc` failed to run: /home/joshua/rustc/build/bootstrap/debug/bootstrap check src/librustdoc Build completed unsuccessfully in 0:00:01 ``` Happy to take suggestions on how to improve the error message :) right now it's getting lost in all the other output. Maybe it makes sense to use bold and colors? This works by storing the current step at all times in a global Mutex and registering an `at_exit` handler which looks up the current step.
1 parent 34b9932 commit cbd1b0b

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

src/bootstrap/builder.rs

+65-1
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ use std::cell::{Cell, RefCell};
33
use std::collections::BTreeSet;
44
use std::env;
55
use std::ffi::OsStr;
6-
use std::fmt::Debug;
6+
use std::fmt::{self, Debug, Display};
77
use std::fs;
88
use std::hash::Hash;
99
use std::ops::Deref;
1010
use std::path::{Path, PathBuf};
1111
use std::process::Command;
12+
use std::sync::Mutex;
1213
use std::time::{Duration, Instant};
1314

1415
use build_helper::{output, t};
16+
use lazy_static::lazy_static;
1517

1618
use crate::cache::{Cache, Interned, INTERNER};
1719
use crate::check;
@@ -60,6 +62,10 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
6062
/// If true, then this rule should be skipped if --target was specified, but --host was not
6163
const ONLY_HOSTS: bool = false;
6264

65+
fn name(&self) -> &'static str {
66+
std::any::type_name::<Self>()
67+
}
68+
6369
/// Primary function to execute this rule. Can call `builder.ensure()`
6470
/// with other steps to run those.
6571
fn run(self, builder: &Builder<'_>) -> Self::Output;
@@ -333,6 +339,26 @@ pub enum Kind {
333339
Run,
334340
}
335341

342+
impl Display for Kind {
343+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344+
use Kind::*;
345+
let s = match self {
346+
Build => "build",
347+
Check => "check",
348+
Clippy => "clippy",
349+
Fix => "fix",
350+
Format => "fmt",
351+
Test => "test",
352+
Bench => "bench",
353+
Dist => "dist",
354+
Doc => "doc",
355+
Install => "install",
356+
Run => "run",
357+
};
358+
f.write_str(s)
359+
}
360+
}
361+
336362
impl<'a> Builder<'a> {
337363
fn get_step_descriptions(kind: Kind) -> Vec<StepDescription> {
338364
macro_rules! describe {
@@ -1533,12 +1559,28 @@ impl<'a> Builder<'a> {
15331559
}
15341560

15351561
let (out, dur) = {
1562+
let paths = S::should_run(ShouldRun::new(self)).paths;
1563+
let path = paths.iter().map(|pathset| pathset.path(self)).next();
1564+
let instructions = ReplicationStep {
1565+
name: step.name(),
1566+
cmd: self.kind,
1567+
path: path.expect("no paths for step"),
1568+
};
1569+
// NOTE: don't hold onto this guard, it will cause a deadlock if the current step calls `ensure` recursively.
1570+
let old_instructions = CURRENT_INSTRUCTIONS
1571+
.lock()
1572+
.expect("steps are not run in parallel")
1573+
.replace(instructions);
1574+
15361575
let start = Instant::now();
15371576
let zero = Duration::new(0, 0);
15381577
let parent = self.time_spent_on_dependencies.replace(zero);
15391578
let out = step.clone().run(self);
15401579
let dur = start.elapsed();
15411580
let deps = self.time_spent_on_dependencies.replace(parent + dur);
1581+
1582+
*CURRENT_INSTRUCTIONS.lock().expect("steps are not run in parallel") = old_instructions;
1583+
15421584
(out, dur - deps)
15431585
};
15441586

@@ -1557,6 +1599,28 @@ impl<'a> Builder<'a> {
15571599
}
15581600
}
15591601

1602+
struct ReplicationStep {
1603+
cmd: Kind,
1604+
name: &'static str,
1605+
path: PathBuf,
1606+
}
1607+
1608+
lazy_static! {
1609+
static ref CURRENT_INSTRUCTIONS: Mutex<Option<ReplicationStep>> = Mutex::new(None);
1610+
}
1611+
1612+
pub(crate) extern "C" fn print_replication_steps() {
1613+
if let Some(step) = CURRENT_INSTRUCTIONS.lock().expect("mutex guard is dropped on panic").take()
1614+
{
1615+
println!("note: failed while building {}", step.name);
1616+
println!(
1617+
"help: to replicate this failure, run `./x.py {} {}`",
1618+
step.cmd,
1619+
step.path.display()
1620+
);
1621+
}
1622+
}
1623+
15601624
#[cfg(test)]
15611625
mod tests;
15621626

src/bootstrap/job.rs

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ use winapi::um::winnt::{
4747
};
4848

4949
pub unsafe fn setup(build: &mut Build) {
50+
libc::atexit(print_replication_steps);
51+
5052
// Enable the Windows Error Reporting dialog which msys disables,
5153
// so we can JIT debug rustc
5254
let mode = SetErrorMode(0);

src/bootstrap/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ mod job;
154154
#[cfg(all(unix, not(target_os = "haiku")))]
155155
mod job {
156156
pub unsafe fn setup(build: &mut crate::Build) {
157+
libc::atexit(crate::builder::print_replication_steps);
157158
if build.config.low_priority {
158159
libc::setpriority(libc::PRIO_PGRP as _, 0, 10);
159160
}

0 commit comments

Comments
 (0)