Skip to content

Commit c8d9fb2

Browse files
committed
Print active bootstrap step stack when a failure happens on CI
1 parent a1f5bbe commit c8d9fb2

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

src/bootstrap/src/core/builder/mod.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use std::any::{Any, type_name};
2-
use std::cell::{Cell, RefCell};
2+
use std::cell::Cell;
33
use std::collections::BTreeSet;
44
use std::fmt::{self, Debug, Write};
55
use std::hash::Hash;
66
use std::ops::Deref;
77
use std::path::{Path, PathBuf};
8-
use std::sync::{LazyLock, OnceLock};
8+
use std::sync::{Arc, LazyLock, Mutex, OnceLock};
99
use std::time::{Duration, Instant};
1010
use std::{env, fs};
1111

@@ -32,6 +32,25 @@ mod cargo;
3232
#[cfg(test)]
3333
mod tests;
3434

35+
type StepStack = Arc<Mutex<Vec<Box<dyn AnyDebug>>>>;
36+
37+
/// Pointer to the active step stack of the currently executing `Builder`.
38+
static ACTIVE_BUILDER_STEP_STACK: Mutex<Option<StepStack>> = Mutex::new(None);
39+
40+
/// Prints the current contents of the active builder's step stack.
41+
fn print_step_stack() {
42+
if let Ok(stack) = ACTIVE_BUILDER_STEP_STACK.try_lock()
43+
&& let Some(ref stack) = *stack
44+
&& let Ok(stack) = stack.try_lock()
45+
{
46+
eprintln!("\n---BOOTSTRAP step stack start---\n");
47+
for step in &*stack {
48+
eprintln!("{step:?}");
49+
}
50+
eprintln!("\n---BOOTSTRAP step stack end---\n");
51+
}
52+
}
53+
3554
/// Builds and performs different [`Self::kind`]s of stuff and actions, taking
3655
/// into account build configuration from e.g. bootstrap.toml.
3756
pub struct Builder<'a> {
@@ -52,7 +71,7 @@ pub struct Builder<'a> {
5271

5372
/// A stack of [`Step`]s to run before we can run this builder. The output
5473
/// of steps is cached in [`Self::cache`].
55-
stack: RefCell<Vec<Box<dyn AnyDebug>>>,
74+
stack: StepStack,
5675

5776
/// The total amount of time we spent running [`Step`]s in [`Self::stack`].
5877
time_spent_on_dependencies: Cell<Duration>,
@@ -78,8 +97,8 @@ impl Deref for Builder<'_> {
7897
/// type's [`Debug`] implementation.
7998
///
8099
/// (Trying to debug-print `dyn Any` results in the unhelpful `"Any { .. }"`.)
81-
pub trait AnyDebug: Any + Debug {}
82-
impl<T: Any + Debug> AnyDebug for T {}
100+
pub trait AnyDebug: Any + Debug + Send {}
101+
impl<T: Any + Debug + Send> AnyDebug for T {}
83102
impl dyn AnyDebug {
84103
/// Equivalent to `<dyn Any>::downcast_ref`.
85104
fn downcast_ref<T: Any>(&self) -> Option<&T> {
@@ -89,7 +108,7 @@ impl dyn AnyDebug {
89108
// Feel free to add other `dyn Any` methods as necessary.
90109
}
91110

92-
pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
111+
pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash + Send {
93112
/// Result type of `Step::run`.
94113
type Output: Clone;
95114

@@ -1265,12 +1284,20 @@ impl<'a> Builder<'a> {
12651284
}
12661285

12671286
fn new_internal(build: &Build, kind: Kind, paths: Vec<PathBuf>) -> Builder<'_> {
1287+
let stack = Arc::new(Mutex::new(Vec::new()));
1288+
1289+
// Store the current step stack
1290+
*ACTIVE_BUILDER_STEP_STACK.lock().unwrap() = Some(stack.clone());
1291+
1292+
// And configure `build_helper` to print it on exit
1293+
*build_helper::util::EXIT_CONTEXT.lock().unwrap() = Some(print_step_stack);
1294+
12681295
Builder {
12691296
build,
12701297
top_stage: build.config.stage,
12711298
kind,
12721299
cache: Cache::new(),
1273-
stack: RefCell::new(Vec::new()),
1300+
stack,
12741301
time_spent_on_dependencies: Cell::new(Duration::new(0, 0)),
12751302
paths,
12761303
submodule_paths_cache: Default::default(),
@@ -1673,9 +1700,9 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
16731700
/// Ensure that a given step is built, returning its output. This will
16741701
/// cache the step, so it is safe (and good!) to call this as often as
16751702
/// needed to ensure that all dependencies are built.
1676-
pub fn ensure<S: Step>(&'a self, step: S) -> S::Output {
1703+
pub fn ensure<S: Step + Send>(&'a self, step: S) -> S::Output {
16771704
{
1678-
let mut stack = self.stack.borrow_mut();
1705+
let mut stack = self.stack.lock().unwrap();
16791706
for stack_step in stack.iter() {
16801707
// should skip
16811708
if stack_step.downcast_ref::<S>().is_none_or(|stack_step| *stack_step != step) {
@@ -1754,7 +1781,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
17541781
self.metrics.exit_step(self);
17551782

17561783
{
1757-
let mut stack = self.stack.borrow_mut();
1784+
let mut stack = self.stack.lock().unwrap();
17581785
let cur_step = stack.pop().expect("step stack empty");
17591786
assert_eq!(cur_step.downcast_ref(), Some(&step));
17601787
}

src/build_helper/src/util.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::fs::File;
22
use std::io::{BufRead, BufReader};
33
use std::path::Path;
44
use std::process::Command;
5+
use std::sync::Mutex;
56

67
use crate::ci::CiEnv;
78

@@ -15,6 +16,10 @@ macro_rules! exit {
1516
};
1617
}
1718

19+
/// When the program ends using `detail_exit`, this function will be called (if present)
20+
/// to output additional context.
21+
pub static EXIT_CONTEXT: Mutex<Option<fn()>> = Mutex::new(None);
22+
1823
/// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.)
1924
/// If `is_test` true and code is an error code, it will cause a panic.
2025
pub fn detail_exit(code: i32, is_test: bool) -> ! {
@@ -28,7 +33,13 @@ pub fn detail_exit(code: i32, is_test: bool) -> ! {
2833
// Skip the first argument, as it will be some absolute path to the bootstrap binary.
2934
let bootstrap_args =
3035
std::env::args().skip(1).map(|a| a.to_string()).collect::<Vec<_>>().join(" ");
31-
eprintln!("Bootstrap failed while executing `{bootstrap_args}`");
36+
eprintln!("Bootstrap failed while executing `x {bootstrap_args}`");
37+
38+
if let Ok(ctx) = EXIT_CONTEXT.lock() {
39+
if let Some(ctx) = *ctx {
40+
ctx();
41+
}
42+
}
3243
}
3344

3445
// otherwise, exit with provided status code

0 commit comments

Comments
 (0)