Skip to content

Commit f816d3a

Browse files
committed
add a splash of color
1 parent f96774b commit f816d3a

File tree

5 files changed

+82
-29
lines changed

5 files changed

+82
-29
lines changed

src/bootstrap/Cargo.lock

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ dependencies = [
1111
"memchr",
1212
]
1313

14+
[[package]]
15+
name = "atty"
16+
version = "0.2.14"
17+
source = "registry+https://github.com/rust-lang/crates.io-index"
18+
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
19+
dependencies = [
20+
"hermit-abi",
21+
"libc",
22+
"winapi",
23+
]
24+
1425
[[package]]
1526
name = "autocfg"
1627
version = "1.1.0"
@@ -36,6 +47,7 @@ dependencies = [
3647
name = "bootstrap"
3748
version = "0.0.0"
3849
dependencies = [
50+
"atty",
3951
"build_helper",
4052
"cc",
4153
"cmake",
@@ -59,6 +71,7 @@ dependencies = [
5971
"walkdir",
6072
"winapi",
6173
"xz2",
74+
"yansi-term",
6275
]
6376

6477
[[package]]
@@ -800,3 +813,12 @@ name = "yansi"
800813
version = "0.5.1"
801814
source = "registry+https://github.com/rust-lang/crates.io-index"
802815
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
816+
817+
[[package]]
818+
name = "yansi-term"
819+
version = "0.1.2"
820+
source = "registry+https://github.com/rust-lang/crates.io-index"
821+
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
822+
dependencies = [
823+
"winapi",
824+
]

src/bootstrap/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ path = "bin/sccache-plus-cl.rs"
3030
test = false
3131

3232
[dependencies]
33+
atty = "0.2.14"
3334
build_helper = { path = "../tools/build_helper" }
3435
cmake = "0.1.38"
3536
fd-lock = "3.0.8"
@@ -52,6 +53,7 @@ opener = "0.5"
5253
once_cell = "1.7.2"
5354
xz2 = "0.1"
5455
walkdir = "2"
56+
yansi-term = "0.1.2"
5557

5658
# Dependencies needed by the build-metrics feature
5759
sysinfo = { version = "0.26.0", optional = true }

src/bootstrap/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ pub struct Config {
8787
pub patch_binaries_for_nix: bool,
8888
pub stage0_metadata: Stage0Metadata,
8989

90+
pub stdout_is_tty: bool,
91+
pub stderr_is_tty: bool,
92+
9093
pub on_fail: Option<String>,
9194
pub stage: u32,
9295
pub keep_stage: Vec<u32>,
@@ -822,6 +825,9 @@ impl Config {
822825
config.deny_warnings = true;
823826
config.bindir = "bin".into();
824827

828+
config.stdout_is_tty = atty::is(atty::Stream::Stdout);
829+
config.stderr_is_tty = atty::is(atty::Stream::Stderr);
830+
825831
// set by build.rs
826832
config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
827833

src/bootstrap/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub use crate::builder::PathSet;
8989
use crate::cache::{Interned, INTERNER};
9090
pub use crate::config::Config;
9191
pub use crate::flags::Subcommand;
92+
use yansi_term::Color;
9293

9394
const LLVM_TOOLS: &[&str] = &[
9495
"llvm-cov", // used to generate coverage report
@@ -1575,6 +1576,23 @@ to download LLVM rather than building it.
15751576

15761577
self.config.ninja_in_file
15771578
}
1579+
1580+
pub fn color_for_stdout(&self, color: Color, message: &str) -> String {
1581+
self.color_for_inner(color, message, self.config.stdout_is_tty)
1582+
}
1583+
1584+
pub fn color_for_stderr(&self, color: Color, message: &str) -> String {
1585+
self.color_for_inner(color, message, self.config.stderr_is_tty)
1586+
}
1587+
1588+
fn color_for_inner(&self, color: Color, message: &str, is_tty: bool) -> String {
1589+
let use_color = match self.config.color {
1590+
flags::Color::Always => true,
1591+
flags::Color::Never => false,
1592+
flags::Color::Auto => is_tty,
1593+
};
1594+
if use_color { color.paint(message).to_string() } else { message.to_string() }
1595+
}
15781596
}
15791597

15801598
#[cfg(unix)]

src/bootstrap/render_tests.rs

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::builder::Builder;
1010
use std::io::{BufRead, BufReader, Write};
1111
use std::process::{ChildStdout, Command, Stdio};
1212
use std::time::Duration;
13+
use yansi_term::Color;
1314

1415
const TERSE_TESTS_PER_LINE: usize = 88;
1516

@@ -37,13 +38,12 @@ fn run_tests(builder: &Builder<'_>, cmd: &mut Command) -> bool {
3738
builder.verbose(&format!("running: {cmd:?}"));
3839

3940
let mut process = cmd.spawn().unwrap();
40-
let stdout = process.stdout.take().unwrap();
41-
let verbose = builder.config.verbose_tests;
42-
let handle = std::thread::spawn(move || Renderer::new(stdout, verbose).render_all());
4341

44-
let result = process.wait().unwrap();
45-
handle.join().expect("test formatter thread failed");
42+
// This runs until the stdout of the child is closed, which means the child exited. We don't
43+
// run this on another thread since the builder is not Sync.
44+
Renderer::new(process.stdout.take().unwrap(), builder).render_all();
4645

46+
let result = process.wait().unwrap();
4747
if !result.success() && builder.is_verbose() {
4848
println!(
4949
"\n\ncommand did not execute successfully: {cmd:?}\n\
@@ -54,21 +54,21 @@ fn run_tests(builder: &Builder<'_>, cmd: &mut Command) -> bool {
5454
result.success()
5555
}
5656

57-
struct Renderer {
57+
struct Renderer<'a> {
5858
stdout: BufReader<ChildStdout>,
5959
failures: Vec<TestOutcome>,
60-
verbose: bool,
60+
builder: &'a Builder<'a>,
6161
tests_count: Option<usize>,
6262
executed_tests: usize,
6363
terse_tests_in_line: usize,
6464
}
6565

66-
impl Renderer {
67-
fn new(stdout: ChildStdout, verbose: bool) -> Self {
66+
impl<'a> Renderer<'a> {
67+
fn new(stdout: ChildStdout, builder: &'a Builder<'a>) -> Self {
6868
Self {
6969
stdout: BufReader::new(stdout),
7070
failures: Vec::new(),
71-
verbose,
71+
builder,
7272
tests_count: None,
7373
executed_tests: 0,
7474
terse_tests_in_line: 0,
@@ -99,7 +99,7 @@ impl Renderer {
9999

100100
fn render_test_outcome(&mut self, outcome: Outcome<'_>, test: &TestOutcome) {
101101
self.executed_tests += 1;
102-
if self.verbose {
102+
if self.builder.config.verbose_tests {
103103
self.render_test_outcome_verbose(outcome, test);
104104
} else {
105105
self.render_test_outcome_terse(outcome, test);
@@ -109,12 +109,13 @@ impl Renderer {
109109
fn render_test_outcome_verbose(&self, outcome: Outcome<'_>, test: &TestOutcome) {
110110
if let Some(exec_time) = test.exec_time {
111111
println!(
112-
"test {} ... {outcome} (in {:.2?})",
112+
"test {} ... {} (in {:.2?})",
113113
test.name,
114+
outcome.long(self.builder),
114115
Duration::from_secs_f64(exec_time)
115116
);
116117
} else {
117-
println!("test {} ... {outcome}", test.name);
118+
println!("test {} ... {}", test.name, outcome.long(self.builder));
118119
}
119120
}
120121

@@ -130,20 +131,13 @@ impl Renderer {
130131
}
131132

132133
self.terse_tests_in_line += 1;
133-
print!(
134-
"{}",
135-
match outcome {
136-
Outcome::Ok => ".",
137-
Outcome::Failed => "F",
138-
Outcome::Ignored { .. } => "i",
139-
}
140-
);
134+
print!("{}", outcome.short(self.builder));
141135
let _ = std::io::stdout().flush();
142136
}
143137

144138
fn render_suite_outcome(&self, outcome: Outcome<'_>, suite: &SuiteOutcome) {
145139
// The terse output doesn't end with a newline, so we need to add it ourselves.
146-
if !self.verbose {
140+
if !self.builder.config.verbose_tests {
147141
println!();
148142
}
149143

@@ -163,8 +157,9 @@ impl Renderer {
163157
}
164158

165159
println!(
166-
"\ntest result: {outcome}. {} passed; {} failed; {} ignored; {} measured; \
160+
"\ntest result: {}. {} passed; {} failed; {} ignored; {} measured; \
167161
{} filtered out; finished in {:.2?}\n",
162+
outcome.long(self.builder),
168163
suite.passed,
169164
suite.failed,
170165
suite.ignored,
@@ -213,13 +208,23 @@ enum Outcome<'a> {
213208
Ignored { reason: Option<&'a str> },
214209
}
215210

216-
impl std::fmt::Display for Outcome<'_> {
217-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211+
impl Outcome<'_> {
212+
fn short(&self, builder: &Builder<'_>) -> String {
218213
match self {
219-
Outcome::Ok => f.write_str("ok"),
220-
Outcome::Failed => f.write_str("FAILED"),
221-
Outcome::Ignored { reason: None } => f.write_str("ignored"),
222-
Outcome::Ignored { reason: Some(reason) } => write!(f, "ignored, {reason}"),
214+
Outcome::Ok => builder.color_for_stdout(Color::Green, "."),
215+
Outcome::Failed => builder.color_for_stdout(Color::Red, "F"),
216+
Outcome::Ignored { .. } => builder.color_for_stdout(Color::Yellow, "i"),
217+
}
218+
}
219+
220+
fn long(&self, builder: &Builder<'_>) -> String {
221+
match self {
222+
Outcome::Ok => builder.color_for_stdout(Color::Green, "ok"),
223+
Outcome::Failed => builder.color_for_stdout(Color::Red, "FAILED"),
224+
Outcome::Ignored { reason: None } => builder.color_for_stdout(Color::Yellow, "ignored"),
225+
Outcome::Ignored { reason: Some(reason) } => {
226+
builder.color_for_stdout(Color::Yellow, &format!("ignored, {reason}"))
227+
}
223228
}
224229
}
225230
}

0 commit comments

Comments
 (0)