| //! Tidy checks source code in this repository. |
| //! |
| //! This program runs all of the various tidy checks for style, cleanliness, |
| //! etc. This is run by default on `./x.py test` and as part of the auto |
| //! builders. The tidy checks can be executed with `./x.py test tidy`. |
| |
| use tidy::*; |
| |
| use std::collections::VecDeque; |
| use std::env; |
| use std::num::NonZeroUsize; |
| use std::path::PathBuf; |
| use std::process; |
| use std::str::FromStr; |
| use std::sync::atomic::{AtomicBool, Ordering}; |
| use std::thread::{self, scope, ScopedJoinHandle}; |
| |
| fn main() { |
| // Running Cargo will read the libstd Cargo.toml |
| // which uses the unstable `public-dependency` feature. |
| // |
| // `setenv` might not be thread safe, so run it before using multiple threads. |
| env::set_var("RUSTC_BOOTSTRAP", "1"); |
| |
| let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into(); |
| let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into(); |
| let output_directory: PathBuf = |
| env::args_os().nth(3).expect("need path to output directory").into(); |
| let concurrency: NonZeroUsize = |
| FromStr::from_str(&env::args().nth(4).expect("need concurrency")) |
| .expect("concurrency must be a number"); |
| |
| let src_path = root_path.join("src"); |
| let tests_path = root_path.join("tests"); |
| let library_path = root_path.join("library"); |
| let compiler_path = root_path.join("compiler"); |
| let librustdoc_path = src_path.join("librustdoc"); |
| |
| let args: Vec<String> = env::args().skip(1).collect(); |
| let (cfg_args, pos_args) = match args.iter().position(|arg| arg == "--") { |
| Some(pos) => (&args[..pos], &args[pos + 1..]), |
| None => (&args[..], [].as_slice()), |
| }; |
| let verbose = cfg_args.iter().any(|s| *s == "--verbose"); |
| let bless = cfg_args.iter().any(|s| *s == "--bless"); |
| let extra_checks = |
| cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str); |
| |
| let bad = std::sync::Arc::new(AtomicBool::new(false)); |
| |
| let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| { |
| // poll all threads for completion before awaiting the oldest one |
| for i in (0..handles.len()).rev() { |
| if handles[i].is_finished() { |
| handles.swap_remove_back(i).unwrap().join().unwrap(); |
| } |
| } |
| |
| while handles.len() >= concurrency.get() { |
| handles.pop_front().unwrap().join().unwrap(); |
| } |
| }; |
| |
| scope(|s| { |
| let mut handles: VecDeque<ScopedJoinHandle<'_, ()>> = |
| VecDeque::with_capacity(concurrency.get()); |
| |
| macro_rules! check { |
| ($p:ident) => { |
| check!(@ $p, name=format!("{}", stringify!($p))); |
| }; |
| ($p:ident, $path:expr $(, $args:expr)* ) => { |
| let shortened = $path.strip_prefix(&root_path).unwrap(); |
| let name = if shortened == std::path::Path::new("") { |
| format!("{} (.)", stringify!($p)) |
| } else { |
| format!("{} ({})", stringify!($p), shortened.display()) |
| }; |
| check!(@ $p, name=name, $path $(,$args)*); |
| }; |
| (@ $p:ident, name=$name:expr $(, $args:expr)* ) => { |
| drain_handles(&mut handles); |
| |
| let handle = thread::Builder::new().name($name).spawn_scoped(s, || { |
| let mut flag = false; |
| $p::check($($args, )* &mut flag); |
| if (flag) { |
| bad.store(true, Ordering::Relaxed); |
| } |
| }).unwrap(); |
| handles.push_back(handle); |
| } |
| } |
| |
| check!(target_specific_tests, &tests_path); |
| |
| // Checks that are done on the cargo workspace. |
| check!(deps, &root_path, &cargo); |
| check!(extdeps, &root_path); |
| |
| // Checks over tests. |
| check!(tests_placement, &root_path); |
| check!(debug_artifacts, &tests_path); |
| check!(ui_tests, &tests_path); |
| check!(mir_opt_tests, &tests_path, bless); |
| check!(rustdoc_gui_tests, &tests_path); |
| check!(rustdoc_css_themes, &librustdoc_path); |
| |
| // Checks that only make sense for the compiler. |
| check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose); |
| check!(fluent_alphabetical, &compiler_path, bless); |
| |
| // Checks that only make sense for the std libs. |
| check!(pal, &library_path); |
| |
| // Checks that need to be done for both the compiler and std libraries. |
| check!(unit_tests, &src_path); |
| check!(unit_tests, &compiler_path); |
| check!(unit_tests, &library_path); |
| |
| if bins::check_filesystem_support(&[&root_path], &output_directory) { |
| check!(bins, &root_path); |
| } |
| |
| check!(style, &src_path); |
| check!(style, &tests_path); |
| check!(style, &compiler_path); |
| check!(style, &library_path); |
| |
| check!(edition, &src_path); |
| check!(edition, &compiler_path); |
| check!(edition, &library_path); |
| |
| check!(alphabetical, &src_path); |
| check!(alphabetical, &compiler_path); |
| check!(alphabetical, &library_path); |
| |
| check!(x_version, &root_path, &cargo); |
| |
| let collected = { |
| drain_handles(&mut handles); |
| |
| let mut flag = false; |
| let r = features::check( |
| &src_path, |
| &tests_path, |
| &compiler_path, |
| &library_path, |
| &mut flag, |
| verbose, |
| ); |
| if flag { |
| bad.store(true, Ordering::Relaxed); |
| } |
| r |
| }; |
| check!(unstable_book, &src_path, collected); |
| |
| check!(ext_tool_checks, &root_path, &output_directory, bless, extra_checks, pos_args); |
| }); |
| |
| if bad.load(Ordering::Relaxed) { |
| eprintln!("some tidy checks failed"); |
| process::exit(1); |
| } |
| } |