1use std::collections::VecDeque;
8use std::num::NonZeroUsize;
9use std::path::PathBuf;
10use std::str::FromStr;
11use std::thread::{self, ScopedJoinHandle, scope};
12use std::{env, process};
13
14use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, DiagCtx, output_message};
15use tidy::*;
16
17fn main() {
18 unsafe {
22 env::set_var("RUSTC_BOOTSTRAP", "1");
23 }
24
25 let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into();
26 let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into();
27 let output_directory: PathBuf =
28 env::args_os().nth(3).expect("need path to output directory").into();
29 let concurrency: NonZeroUsize =
30 FromStr::from_str(&env::args().nth(4).expect("need concurrency"))
31 .expect("concurrency must be a number");
32 let npm: PathBuf = env::args_os().nth(5).expect("need name/path of npm command").into();
33
34 let root_manifest = root_path.join("Cargo.toml");
35 let src_path = root_path.join("src");
36 let tests_path = root_path.join("tests");
37 let library_path = root_path.join("library");
38 let compiler_path = root_path.join("compiler");
39 let librustdoc_path = src_path.join("librustdoc");
40 let tools_path = src_path.join("tools");
41 let crashes_path = tests_path.join("crashes");
42
43 let args: Vec<String> = env::args().skip(1).collect();
44 let (cfg_args, pos_args) = match args.iter().position(|arg| arg == "--") {
45 Some(pos) => (&args[..pos], &args[pos + 1..]),
46 None => (&args[..], [].as_slice()),
47 };
48 let verbose = cfg_args.iter().any(|s| *s == "--verbose");
49 let bless = cfg_args.iter().any(|s| *s == "--bless");
50 let extra_checks =
51 cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str);
52
53 let diag_ctx = DiagCtx::new(&root_path, verbose);
54 let ci_info = CiInfo::new(diag_ctx.clone());
55
56 let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
57 for i in (0..handles.len()).rev() {
59 if handles[i].is_finished() {
60 handles.swap_remove_back(i).unwrap().join().unwrap();
61 }
62 }
63
64 while handles.len() >= concurrency.get() {
65 handles.pop_front().unwrap().join().unwrap();
66 }
67 };
68
69 scope(|s| {
70 let mut handles: VecDeque<ScopedJoinHandle<'_, ()>> =
71 VecDeque::with_capacity(concurrency.get());
72
73 macro_rules! check {
74 ($p:ident) => {
75 check!(@ $p, name=format!("{}", stringify!($p)));
76 };
77 ($p:ident, $path:expr $(, $args:expr)* ) => {
78 let shortened = $path.strip_prefix(&root_path).unwrap();
79 let name = if shortened == std::path::Path::new("") {
80 format!("{} (.)", stringify!($p))
81 } else {
82 format!("{} ({})", stringify!($p), shortened.display())
83 };
84 check!(@ $p, name=name, $path $(,$args)*);
85 };
86 (@ $p:ident, name=$name:expr $(, $args:expr)* ) => {
87 drain_handles(&mut handles);
88
89 let diag_ctx = diag_ctx.clone();
90 let handle = thread::Builder::new().name($name).spawn_scoped(s, || {
91 $p::check($($args, )* diag_ctx);
92 }).unwrap();
93 handles.push_back(handle);
94 }
95 }
96
97 check!(target_specific_tests, &tests_path);
98
99 check!(deps, &root_path, &cargo, bless);
101 check!(extdeps, &root_path);
102
103 check!(tests_placement, &root_path);
105 check!(tests_revision_unpaired_stdout_stderr, &tests_path);
106 check!(debug_artifacts, &tests_path);
107 check!(ui_tests, &root_path, bless);
108 check!(mir_opt_tests, &tests_path, bless);
109 check!(rustdoc_gui_tests, &tests_path);
110 check!(rustdoc_css_themes, &librustdoc_path);
111 check!(rustdoc_templates, &librustdoc_path);
112 check!(rustdoc_json, &src_path, &ci_info);
113 check!(known_bug, &crashes_path);
114 check!(unknown_revision, &tests_path);
115
116 check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], &ci_info);
118 check!(fluent_alphabetical, &compiler_path, bless);
119 check!(fluent_period, &compiler_path);
120 check!(fluent_lowercase, &compiler_path);
121 check!(target_policy, &root_path);
122 check!(gcc_submodule, &root_path, &compiler_path);
123
124 check!(pal, &library_path);
126
127 check!(unit_tests, &src_path, false);
129 check!(unit_tests, &compiler_path, false);
130 check!(unit_tests, &library_path, true);
131
132 if bins::check_filesystem_support(&[&root_path], &output_directory) {
133 check!(bins, &root_path);
134 }
135
136 check!(style, &src_path);
137 check!(style, &tests_path);
138 check!(style, &compiler_path);
139 check!(style, &library_path);
140
141 check!(edition, &src_path);
142 check!(edition, &compiler_path);
143 check!(edition, &library_path);
144
145 check!(alphabetical, &root_manifest);
146 check!(alphabetical, &src_path);
147 check!(alphabetical, &tests_path);
148 check!(alphabetical, &compiler_path);
149 check!(alphabetical, &library_path);
150
151 check!(x_version, &root_path, &cargo);
152
153 check!(triagebot, &root_path);
154 check!(filenames, &root_path);
155
156 let collected = {
157 drain_handles(&mut handles);
158
159 features::check(&src_path, &tests_path, &compiler_path, &library_path, diag_ctx.clone())
160 };
161 check!(unstable_book, &src_path, collected);
162
163 check!(
164 extra_checks,
165 &root_path,
166 &output_directory,
167 &ci_info,
168 &librustdoc_path,
169 &tools_path,
170 &npm,
171 &cargo,
172 bless,
173 extra_checks,
174 pos_args
175 );
176 });
177
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 );
192 process::exit(1);
193 } else {
194 output_message("All tidy checks succeeded", None, Some(COLOR_SUCCESS));
195 }
196}