1use std::ffi::OsStr;
7use std::process::Command;
8
9use build_helper::ci::CiEnv;
10use build_helper::git::{GitConfig, get_closest_upstream_commit};
11use build_helper::stage0_parser::{Stage0Config, parse_stage0_file};
12use termcolor::WriteColor;
13
14macro_rules! static_regex {
15 ($re:literal) => {{
16 static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new();
17 RE.get_or_init(|| ::regex::Regex::new($re).unwrap())
18 }};
19}
20
21#[macro_export]
27macro_rules! t {
28 ($e:expr, $p:expr) => {
29 match $e {
30 Ok(e) => e,
31 Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
32 }
33 };
34
35 ($e:expr) => {
36 match $e {
37 Ok(e) => e,
38 Err(e) => panic!("{} failed with {}", stringify!($e), e),
39 }
40 };
41}
42
43macro_rules! tidy_error {
44 ($bad:expr, $($fmt:tt)*) => ({
45 $crate::tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
46 *$bad = true;
47 });
48}
49
50macro_rules! tidy_error_ext {
51 ($tidy_error:path, $bad:expr, $($fmt:tt)*) => ({
52 $tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
53 *$bad = true;
54 });
55}
56
57fn tidy_error(args: &str) -> std::io::Result<()> {
58 use std::io::Write;
59
60 use termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
61
62 let mut stderr = StandardStream::stdout(ColorChoice::Auto);
63 stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
64
65 write!(&mut stderr, "tidy error")?;
66 stderr.set_color(&ColorSpec::new())?;
67
68 writeln!(&mut stderr, ": {args}")?;
69 Ok(())
70}
71
72pub struct CiInfo {
73 pub git_merge_commit_email: String,
74 pub nightly_branch: String,
75 pub base_commit: Option<String>,
76 pub ci_env: CiEnv,
77}
78
79impl CiInfo {
80 pub fn new(bad: &mut bool) -> Self {
81 let stage0 = parse_stage0_file();
82 let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config;
83
84 let mut info = Self {
85 nightly_branch,
86 git_merge_commit_email,
87 ci_env: CiEnv::current(),
88 base_commit: None,
89 };
90 let base_commit = match get_closest_upstream_commit(None, &info.git_config(), info.ci_env) {
91 Ok(Some(commit)) => Some(commit),
92 Ok(None) => {
93 info.error_if_in_ci("no base commit found", bad);
94 None
95 }
96 Err(error) => {
97 info.error_if_in_ci(&format!("failed to retrieve base commit: {error}"), bad);
98 None
99 }
100 };
101 info.base_commit = base_commit;
102 info
103 }
104
105 pub fn git_config(&self) -> GitConfig<'_> {
106 GitConfig {
107 nightly_branch: &self.nightly_branch,
108 git_merge_commit_email: &self.git_merge_commit_email,
109 }
110 }
111
112 pub fn error_if_in_ci(&self, msg: &str, bad: &mut bool) {
113 if self.ci_env.is_running_in_ci() {
114 *bad = true;
115 eprintln!("tidy check error: {msg}");
116 } else {
117 eprintln!("tidy check warning: {msg}. Some checks will be skipped.");
118 }
119 }
120}
121
122pub fn git_diff<S: AsRef<OsStr>>(base_commit: &str, extra_arg: S) -> Option<String> {
123 let output = Command::new("git").arg("diff").arg(base_commit).arg(extra_arg).output().ok()?;
124 Some(String::from_utf8_lossy(&output.stdout).into())
125}
126
127pub mod alphabetical;
128pub mod bins;
129pub mod debug_artifacts;
130pub mod deps;
131pub mod edition;
132pub mod error_codes;
133pub mod ext_tool_checks;
134pub mod extdeps;
135pub mod features;
136pub mod fluent_alphabetical;
137pub mod fluent_period;
138mod fluent_used;
139pub mod gcc_submodule;
140pub(crate) mod iter_header;
141pub mod known_bug;
142pub mod mir_opt_tests;
143pub mod pal;
144pub mod rustdoc_css_themes;
145pub mod rustdoc_gui_tests;
146pub mod rustdoc_js;
147pub mod rustdoc_json;
148pub mod rustdoc_templates;
149pub mod style;
150pub mod target_policy;
151pub mod target_specific_tests;
152pub mod tests_placement;
153pub mod tests_revision_unpaired_stdout_stderr;
154pub mod triagebot;
155pub mod ui_tests;
156pub mod unit_tests;
157pub mod unknown_revision;
158pub mod unstable_book;
159pub mod walk;
160pub mod x_version;