1use std::ffi::OsStr;
7use std::num::NonZeroUsize;
8use std::process::Command;
9
10macro_rules! static_regex {
11 ($re:literal) => {{
12 static RE: ::std::sync::LazyLock<::regex::Regex> =
13 ::std::sync::LazyLock::new(|| ::regex::Regex::new($re).unwrap());
14 &*RE
15 }};
16}
17
18#[macro_export]
24macro_rules! t {
25 ($e:expr, $p:expr) => {
26 match $e {
27 Ok(e) => e,
28 Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
29 }
30 };
31
32 ($e:expr) => {
33 match $e {
34 Ok(e) => e,
35 Err(e) => panic!("{} failed with {}", stringify!($e), e),
36 }
37 };
38}
39
40pub fn git_diff<S: AsRef<OsStr>>(base_commit: &str, extra_arg: S) -> Option<String> {
41 let output = Command::new("git").arg("diff").arg(base_commit).arg(extra_arg).output().ok()?;
42 Some(String::from_utf8_lossy(&output.stdout).into())
43}
44
45pub fn files_modified_batch_filter<T>(
51 base_commit: &Option<String>,
52 is_ci: bool,
53 items: &mut Vec<T>,
54 pred: impl Fn(&T, &str) -> bool,
55) {
56 if is_ci {
57 return;
59 }
60 let Some(base_commit) = base_commit else {
61 eprintln!("No base commit, assuming all files are modified");
62 return;
63 };
64 match crate::git_diff(base_commit, "--name-status") {
65 Some(output) => {
66 let modified_files: Vec<_> = output
67 .lines()
68 .filter_map(|ln| {
69 let (status, name) = ln
70 .trim_end()
71 .split_once('\t')
72 .expect("bad format from `git diff --name-status`");
73 if status == "M" { Some(name) } else { None }
74 })
75 .collect();
76 items.retain(|item| {
77 for modified_file in &modified_files {
78 if pred(item, modified_file) {
79 return true;
81 }
82 }
83 false
85 });
86 }
87 None => {
88 eprintln!("warning: failed to run `git diff` to check for changes");
89 eprintln!("warning: assuming all files are modified");
90 }
91 }
92}
93
94pub fn files_modified(
96 base_commit: &Option<String>,
97 is_ci: bool,
98 pred: impl Fn(&str) -> bool,
99) -> bool {
100 let mut v = vec![()];
101 files_modified_batch_filter(base_commit, is_ci, &mut v, |_, p| pred(p));
102 !v.is_empty()
103}
104
105pub mod alphabetical;
106pub mod arg_parser;
107pub mod bins;
108pub mod codegen;
109pub mod debug_artifacts;
110pub mod deps;
111pub mod diagnostics;
112pub mod edition;
113pub mod error_codes;
114pub mod extdeps;
115pub mod extra_checks;
116pub mod features;
117pub mod filenames;
118pub mod gcc_submodule;
119pub(crate) mod iter_header;
120pub mod known_bug;
121pub mod mir_opt_tests;
122pub mod pal;
123pub mod rustdoc_css_themes;
124pub mod rustdoc_gui_tests;
125pub mod rustdoc_html_tests;
126pub mod rustdoc_json;
127pub mod rustdoc_templates;
128pub mod style;
129pub mod target_policy;
130pub mod target_specific_tests;
131pub mod tests_placement;
132pub mod tests_revision_unpaired_stdout_stderr;
133pub mod triagebot;
134pub mod ui_tests;
135pub mod unit_tests;
136pub mod unknown_revision;
137pub mod unstable_book;
138pub mod walk;
139pub mod x_version;
140
141pub struct Semaphore {
142 sem: std::sync::Mutex<usize>,
143 cond: std::sync::Condvar,
144}
145
146impl Semaphore {
147 pub fn new(count: NonZeroUsize) -> Self {
148 Self { sem: std::sync::Mutex::new(count.get()), cond: std::sync::Condvar::new() }
149 }
150
151 pub fn acquire(&self) -> Guard<'_> {
152 let mut count = self.sem.lock().unwrap();
153 while *count == 0 {
154 count = self.cond.wait(count).unwrap();
155 }
156 *count -= 1;
157 Guard { sem: self }
158 }
159
160 fn release(&self) {
161 let mut count = self.sem.lock().unwrap();
162 *count += 1;
163 self.cond.notify_one();
164 }
165}
166
167pub struct Guard<'a> {
168 sem: &'a Semaphore,
169}
170
171impl Drop for Guard<'_> {
172 fn drop(&mut self) {
173 self.sem.release();
174 }
175}