rust_tidy/
main.rs

1//! Tidy checks source code in this repository.
2//!
3//! This program runs all of the various tidy checks for style, cleanliness,
4//! etc. This is run by default on `./x.py test` and as part of the auto
5//! builders. The tidy checks can be executed with `./x.py test tidy`.
6
7use std::collections::VecDeque;
8use std::num::NonZeroUsize;
9use std::path::PathBuf;
10use std::str::FromStr;
11use std::sync::atomic::{AtomicBool, Ordering};
12use std::thread::{self, ScopedJoinHandle, scope};
13use std::{env, process};
14
15use tidy::*;
16
17fn main() {
18    // Enable nightly, because Cargo will read the libstd Cargo.toml
19    // which uses the unstable `public-dependency` feature.
20    // SAFETY: no other threads have been spawned
21    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 mut bad = false;
54    let ci_info = CiInfo::new(&mut bad);
55    let bad = std::sync::Arc::new(AtomicBool::new(bad));
56
57    let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
58        // poll all threads for completion before awaiting the oldest one
59        for i in (0..handles.len()).rev() {
60            if handles[i].is_finished() {
61                handles.swap_remove_back(i).unwrap().join().unwrap();
62            }
63        }
64
65        while handles.len() >= concurrency.get() {
66            handles.pop_front().unwrap().join().unwrap();
67        }
68    };
69
70    scope(|s| {
71        let mut handles: VecDeque<ScopedJoinHandle<'_, ()>> =
72            VecDeque::with_capacity(concurrency.get());
73
74        macro_rules! check {
75            ($p:ident) => {
76                check!(@ $p, name=format!("{}", stringify!($p)));
77            };
78            ($p:ident, $path:expr $(, $args:expr)* ) => {
79                let shortened = $path.strip_prefix(&root_path).unwrap();
80                let name = if shortened == std::path::Path::new("") {
81                    format!("{} (.)", stringify!($p))
82                } else {
83                    format!("{} ({})", stringify!($p), shortened.display())
84                };
85                check!(@ $p, name=name, $path $(,$args)*);
86            };
87            (@ $p:ident, name=$name:expr $(, $args:expr)* ) => {
88                drain_handles(&mut handles);
89
90                let handle = thread::Builder::new().name($name).spawn_scoped(s, || {
91                    let mut flag = false;
92                    $p::check($($args, )* &mut flag);
93                    if (flag) {
94                        bad.store(true, Ordering::Relaxed);
95                    }
96                }).unwrap();
97                handles.push_back(handle);
98            }
99        }
100
101        check!(target_specific_tests, &tests_path);
102
103        // Checks that are done on the cargo workspace.
104        check!(deps, &root_path, &cargo, bless);
105        check!(extdeps, &root_path);
106
107        // Checks over tests.
108        check!(tests_placement, &root_path);
109        check!(tests_revision_unpaired_stdout_stderr, &tests_path);
110        check!(debug_artifacts, &tests_path);
111        check!(ui_tests, &root_path, bless);
112        check!(mir_opt_tests, &tests_path, bless);
113        check!(rustdoc_gui_tests, &tests_path);
114        check!(rustdoc_css_themes, &librustdoc_path);
115        check!(rustdoc_templates, &librustdoc_path);
116        check!(rustdoc_json, &src_path, &ci_info);
117        check!(known_bug, &crashes_path);
118        check!(unknown_revision, &tests_path);
119
120        // Checks that only make sense for the compiler.
121        check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info);
122        check!(fluent_alphabetical, &compiler_path, bless);
123        check!(fluent_period, &compiler_path);
124        check!(fluent_lowercase, &compiler_path);
125        check!(target_policy, &root_path);
126        check!(gcc_submodule, &root_path, &compiler_path);
127
128        // Checks that only make sense for the std libs.
129        check!(pal, &library_path);
130
131        // Checks that need to be done for both the compiler and std libraries.
132        check!(unit_tests, &src_path, false);
133        check!(unit_tests, &compiler_path, false);
134        check!(unit_tests, &library_path, true);
135
136        if bins::check_filesystem_support(&[&root_path], &output_directory) {
137            check!(bins, &root_path);
138        }
139
140        check!(style, &src_path);
141        check!(style, &tests_path);
142        check!(style, &compiler_path);
143        check!(style, &library_path);
144
145        check!(edition, &src_path);
146        check!(edition, &compiler_path);
147        check!(edition, &library_path);
148
149        check!(alphabetical, &root_manifest);
150        check!(alphabetical, &src_path);
151        check!(alphabetical, &tests_path);
152        check!(alphabetical, &compiler_path);
153        check!(alphabetical, &library_path);
154
155        check!(x_version, &root_path, &cargo);
156
157        check!(triagebot, &root_path);
158
159        check!(filenames, &root_path);
160
161        let collected = {
162            drain_handles(&mut handles);
163
164            let mut flag = false;
165            let r = features::check(
166                &src_path,
167                &tests_path,
168                &compiler_path,
169                &library_path,
170                &mut flag,
171                verbose,
172            );
173            if flag {
174                bad.store(true, Ordering::Relaxed);
175            }
176            r
177        };
178        check!(unstable_book, &src_path, collected);
179
180        check!(
181            extra_checks,
182            &root_path,
183            &output_directory,
184            &ci_info,
185            &librustdoc_path,
186            &tools_path,
187            &npm,
188            &cargo,
189            bless,
190            extra_checks,
191            pos_args
192        );
193    });
194
195    if bad.load(Ordering::Relaxed) {
196        eprintln!("some tidy checks failed");
197        process::exit(1);
198    }
199}