Skip to content

Commit 50b8696

Browse files
committed
Implement output of colored messages with optional check context
1 parent 1777cc2 commit 50b8696

File tree

7 files changed

+107
-40
lines changed

7 files changed

+107
-40
lines changed

src/tools/tidy/src/deps.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use build_helper::ci::CiEnv;
99
use cargo_metadata::semver::Version;
1010
use cargo_metadata::{Metadata, Package, PackageId};
1111

12-
use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
12+
use crate::diagnostics::{DiagCtx, RunningCheck};
1313

1414
#[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"]
1515
mod proc_macro_deps;
@@ -664,7 +664,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
664664
/// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
665665
/// to the cargo executable.
666666
pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) {
667-
let mut check = diag_ctx.start_check(CheckId::new("deps").path(root));
667+
let mut check = diag_ctx.start_check("deps");
668668

669669
let mut checked_runtime_licenses = false;
670670

src/tools/tidy/src/diagnostics.rs

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::collections::HashSet;
2-
use std::fmt::Display;
2+
use std::fmt::{Display, Formatter};
33
use std::path::{Path, PathBuf};
44
use std::sync::{Arc, Mutex};
55

6-
use termcolor::WriteColor;
6+
use termcolor::{Color, WriteColor};
77

88
/// Collects diagnostics from all tidy steps, and contains shared information
99
/// that determines how should message and logs be presented.
@@ -14,40 +14,50 @@ use termcolor::WriteColor;
1414
pub struct DiagCtx(Arc<Mutex<DiagCtxInner>>);
1515

1616
impl DiagCtx {
17-
pub fn new(verbose: bool) -> Self {
17+
pub fn new(root_path: &Path, verbose: bool) -> Self {
1818
Self(Arc::new(Mutex::new(DiagCtxInner {
1919
running_checks: Default::default(),
2020
finished_checks: Default::default(),
21+
root_path: root_path.to_path_buf(),
2122
verbose,
2223
})))
2324
}
2425

2526
pub fn start_check<Id: Into<CheckId>>(&self, id: Id) -> RunningCheck {
26-
let id = id.into();
27+
let mut id = id.into();
2728

2829
let mut ctx = self.0.lock().unwrap();
30+
31+
// Shorten path for shorter diagnostics
32+
id.path = match id.path {
33+
Some(path) => Some(path.strip_prefix(&ctx.root_path).unwrap_or(&path).to_path_buf()),
34+
None => None,
35+
};
36+
2937
ctx.start_check(id.clone());
3038
RunningCheck { id, bad: false, ctx: self.0.clone() }
3139
}
3240

33-
pub fn into_conclusion(self) -> bool {
34-
let ctx = self.0.lock().unwrap();
41+
pub fn into_failed_checks(self) -> Vec<FinishedCheck> {
42+
let ctx = Arc::into_inner(self.0).unwrap().into_inner().unwrap();
3543
assert!(ctx.running_checks.is_empty(), "Some checks are still running");
36-
ctx.finished_checks.iter().any(|c| c.bad)
44+
ctx.finished_checks.into_iter().filter(|c| c.bad).collect()
3745
}
3846
}
3947

4048
struct DiagCtxInner {
4149
running_checks: HashSet<CheckId>,
4250
finished_checks: HashSet<FinishedCheck>,
4351
verbose: bool,
52+
root_path: PathBuf,
4453
}
4554

4655
impl DiagCtxInner {
4756
fn start_check(&mut self, id: CheckId) {
4857
if self.has_check_id(&id) {
4958
panic!("Starting a check named `{id:?}` for the second time");
5059
}
60+
5161
self.running_checks.insert(id);
5262
}
5363

@@ -57,6 +67,13 @@ impl DiagCtxInner {
5767
"Finishing check `{:?}` that was not started",
5868
check.id
5969
);
70+
71+
if check.bad {
72+
output_message("FAIL", Some(&check.id), Some(COLOR_ERROR));
73+
} else if self.verbose {
74+
output_message("OK", Some(&check.id), Some(COLOR_SUCCESS));
75+
}
76+
6077
self.finished_checks.insert(check);
6178
}
6279

@@ -71,8 +88,8 @@ impl DiagCtxInner {
7188
/// Identifies a single step
7289
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
7390
pub struct CheckId {
74-
name: String,
75-
path: Option<PathBuf>,
91+
pub name: String,
92+
pub path: Option<PathBuf>,
7693
}
7794

7895
impl CheckId {
@@ -91,12 +108,28 @@ impl From<&'static str> for CheckId {
91108
}
92109
}
93110

111+
impl Display for CheckId {
112+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113+
write!(f, "{}", self.name)?;
114+
if let Some(path) = &self.path {
115+
write!(f, " ({})", path.display())?;
116+
}
117+
Ok(())
118+
}
119+
}
120+
94121
#[derive(PartialEq, Eq, Hash, Debug)]
95-
struct FinishedCheck {
122+
pub struct FinishedCheck {
96123
id: CheckId,
97124
bad: bool,
98125
}
99126

127+
impl FinishedCheck {
128+
pub fn id(&self) -> &CheckId {
129+
&self.id
130+
}
131+
}
132+
100133
/// Represents a single tidy check, identified by its `name`, running.
101134
pub struct RunningCheck {
102135
id: CheckId,
@@ -106,25 +139,25 @@ pub struct RunningCheck {
106139

107140
impl RunningCheck {
108141
/// Immediately output an error and mark the check as failed.
109-
pub fn error<T: Display>(&mut self, t: T) {
142+
pub fn error<T: Display>(&mut self, msg: T) {
110143
self.mark_as_bad();
111-
tidy_error(&t.to_string()).expect("failed to output error");
144+
output_message(&msg.to_string(), Some(&self.id), Some(COLOR_ERROR));
112145
}
113146

114147
/// Immediately output a warning.
115-
pub fn warning<T: Display>(&mut self, t: T) {
116-
eprintln!("WARNING: {t}");
148+
pub fn warning<T: Display>(&mut self, msg: T) {
149+
output_message(&msg.to_string(), Some(&self.id), Some(COLOR_WARNING));
117150
}
118151

119152
/// Output an informational message
120-
pub fn message<T: Display>(&mut self, t: T) {
121-
eprintln!("{t}");
153+
pub fn message<T: Display>(&mut self, msg: T) {
154+
output_message(&msg.to_string(), Some(&self.id), None);
122155
}
123156

124157
/// Output a message only if verbose output is enabled.
125-
pub fn verbose_msg<T: Display>(&mut self, t: T) {
158+
pub fn verbose_msg<T: Display>(&mut self, msg: T) {
126159
if self.is_verbose_enabled() {
127-
self.message(t);
160+
self.message(msg);
128161
}
129162
}
130163

@@ -149,17 +182,37 @@ impl Drop for RunningCheck {
149182
}
150183
}
151184

152-
fn tidy_error(args: &str) -> std::io::Result<()> {
185+
pub const COLOR_SUCCESS: Color = Color::Green;
186+
pub const COLOR_ERROR: Color = Color::Red;
187+
pub const COLOR_WARNING: Color = Color::Yellow;
188+
189+
/// Output a message to stderr.
190+
/// The message can be optionally scoped to a certain check, and it can also have a certain color.
191+
pub fn output_message(msg: &str, id: Option<&CheckId>, color: Option<Color>) {
153192
use std::io::Write;
154193

155-
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
194+
use termcolor::{ColorChoice, ColorSpec, StandardStream};
156195

157-
let mut stderr = StandardStream::stdout(ColorChoice::Auto);
158-
stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
196+
let mut stderr = StandardStream::stderr(ColorChoice::Auto);
197+
if let Some(color) = &color {
198+
stderr.set_color(ColorSpec::new().set_fg(Some(*color))).unwrap();
199+
}
159200

160-
write!(&mut stderr, "tidy error")?;
161-
stderr.set_color(&ColorSpec::new())?;
201+
match id {
202+
Some(id) => {
203+
write!(&mut stderr, "tidy [{}", id.name).unwrap();
204+
if let Some(path) = &id.path {
205+
write!(&mut stderr, " ({})", path.display()).unwrap();
206+
}
207+
write!(&mut stderr, "]").unwrap();
208+
}
209+
None => {
210+
write!(&mut stderr, "tidy").unwrap();
211+
}
212+
}
213+
if color.is_some() {
214+
stderr.set_color(&ColorSpec::new()).unwrap();
215+
}
162216

163-
writeln!(&mut stderr, ": {args}")?;
164-
Ok(())
217+
writeln!(&mut stderr, ": {msg}").unwrap();
165218
}

src/tools/tidy/src/extdeps.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fs;
44
use std::path::Path;
55

66
use crate::deps::WorkspaceInfo;
7-
use crate::diagnostics::{CheckId, DiagCtx};
7+
use crate::diagnostics::DiagCtx;
88

99
/// List of allowed sources for packages.
1010
const ALLOWED_SOURCES: &[&str] = &[
@@ -16,7 +16,7 @@ const ALLOWED_SOURCES: &[&str] = &[
1616
/// Checks for external package sources. `root` is the path to the directory that contains the
1717
/// workspace `Cargo.toml`.
1818
pub fn check(root: &Path, diag_ctx: DiagCtx) {
19-
let mut check = diag_ctx.start_check(CheckId::new("extdeps").path(root));
19+
let mut check = diag_ctx.start_check("extdeps");
2020

2121
for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES {
2222
if crate::deps::has_missing_submodule(root, submodules) {

src/tools/tidy/src/filenames.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
use std::path::Path;
1111
use std::process::Command;
1212

13-
use crate::diagnostics::{CheckId, DiagCtx};
13+
use crate::diagnostics::DiagCtx;
1414

1515
pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
16-
let mut check = diag_ctx.start_check(CheckId::new("filenames").path(root_path));
16+
let mut check = diag_ctx.start_check("filenames");
1717
let stat_output = Command::new("git")
1818
.arg("-C")
1919
.arg(root_path)

src/tools/tidy/src/main.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::str::FromStr;
1111
use std::thread::{self, ScopedJoinHandle, scope};
1212
use std::{env, process};
1313

14-
use tidy::diagnostics::DiagCtx;
14+
use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, DiagCtx, output_message};
1515
use tidy::*;
1616

1717
fn main() {
@@ -50,7 +50,7 @@ fn main() {
5050
let extra_checks =
5151
cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str);
5252

53-
let diag_ctx = DiagCtx::new(verbose);
53+
let diag_ctx = DiagCtx::new(&root_path, verbose);
5454
let ci_info = CiInfo::new(diag_ctx.clone());
5555

5656
let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
@@ -175,8 +175,22 @@ fn main() {
175175
);
176176
});
177177

178-
if diag_ctx.into_conclusion() {
179-
eprintln!("some tidy checks failed");
178+
let failed_checks = diag_ctx.into_failed_checks();
179+
if !failed_checks.is_empty() {
180+
let mut failed: Vec<String> =
181+
failed_checks.into_iter().map(|c| c.id().to_string()).collect();
182+
failed.sort();
183+
output_message(
184+
&format!(
185+
"The following check{} failed: {}",
186+
if failed.len() > 1 { "s" } else { "" },
187+
failed.join(", ")
188+
),
189+
None,
190+
Some(COLOR_ERROR),
191+
);
180192
process::exit(1);
193+
} else {
194+
output_message("All tidy checks succeeded", None, Some(COLOR_SUCCESS));
181195
}
182196
}

src/tools/tidy/src/tests_placement.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::path::Path;
22

3-
use crate::diagnostics::{CheckId, DiagCtx};
3+
use crate::diagnostics::DiagCtx;
44

55
const FORBIDDEN_PATH: &str = "src/test";
66
const ALLOWED_PATH: &str = "tests";
77

88
pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
9-
let mut check = diag_ctx.start_check(CheckId::new("tests-placement").path(root_path));
9+
let mut check = diag_ctx.start_check("tests_placement");
1010

1111
if root_path.join(FORBIDDEN_PATH).exists() {
1212
check.error(format!(

src/tools/tidy/src/triagebot.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use std::path::Path;
44

55
use toml::Value;
66

7-
use crate::diagnostics::{CheckId, DiagCtx};
7+
use crate::diagnostics::DiagCtx;
88

99
pub fn check(path: &Path, diag_ctx: DiagCtx) {
10-
let mut check = diag_ctx.start_check(CheckId::new("triagebot").path(path));
10+
let mut check = diag_ctx.start_check("triagebot");
1111
let triagebot_path = path.join("triagebot.toml");
1212

1313
// This check is mostly to catch broken path filters *within* `triagebot.toml`, and not enforce

0 commit comments

Comments
 (0)