]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/format.rs
Rollup merge of #76607 - Mark-Simulacrum:tidy-bins, r=pnkfelix
[rust.git] / src / bootstrap / format.rs
1 //! Runs rustfmt on the repository.
2
3 use crate::Build;
4 use build_helper::{output, t};
5 use ignore::WalkBuilder;
6 use std::path::Path;
7 use std::process::{Command, Stdio};
8
9 fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
10     let mut cmd = Command::new(&rustfmt);
11     // avoid the submodule config paths from coming into play,
12     // we only allow a single global config for the workspace for now
13     cmd.arg("--config-path").arg(&src.canonicalize().unwrap());
14     cmd.arg("--edition").arg("2018");
15     cmd.arg("--unstable-features");
16     cmd.arg("--skip-children");
17     if check {
18         cmd.arg("--check");
19     }
20     cmd.arg(&path);
21     let cmd_debug = format!("{:?}", cmd);
22     let status = cmd.status().expect("executing rustfmt");
23     if !status.success() {
24         eprintln!(
25             "Running `{}` failed.\nIf you're running `tidy`, \
26             try again with `--bless`. Or, if you just want to format \
27             code, run `./x.py fmt` instead.",
28             cmd_debug,
29         );
30         std::process::exit(1);
31     }
32 }
33
34 #[derive(serde::Deserialize)]
35 struct RustfmtConfig {
36     ignore: Vec<String>,
37 }
38
39 pub fn format(build: &Build, check: bool) {
40     if build.config.dry_run {
41         return;
42     }
43     let mut builder = ignore::types::TypesBuilder::new();
44     builder.add_defaults();
45     builder.select("rust");
46     let matcher = builder.build().unwrap();
47     let rustfmt_config = build.src.join("rustfmt.toml");
48     if !rustfmt_config.exists() {
49         eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
50         eprintln!("This may happen in distributed tarballs.");
51         return;
52     }
53     let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
54     let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
55     let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
56     for ignore in rustfmt_config.ignore {
57         ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
58     }
59     let git_available = match Command::new("git")
60         .arg("--version")
61         .stdout(Stdio::null())
62         .stderr(Stdio::null())
63         .status()
64     {
65         Ok(status) => status.success(),
66         Err(_) => false,
67     };
68     if git_available {
69         let in_working_tree = match Command::new("git")
70             .arg("rev-parse")
71             .arg("--is-inside-work-tree")
72             .stdout(Stdio::null())
73             .stderr(Stdio::null())
74             .status()
75         {
76             Ok(status) => status.success(),
77             Err(_) => false,
78         };
79         if in_working_tree {
80             let untracked_paths_output = output(
81                 Command::new("git")
82                     .arg("status")
83                     .arg("--porcelain")
84                     .arg("--untracked-files=normal"),
85             );
86             let untracked_paths = untracked_paths_output
87                 .lines()
88                 .filter(|entry| entry.starts_with("??"))
89                 .map(|entry| {
90                     entry.split(' ').nth(1).expect("every git status entry should list a path")
91                 });
92             for untracked_path in untracked_paths {
93                 eprintln!("skip untracked path {} during rustfmt invocations", untracked_path);
94                 ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path);
95             }
96         } else {
97             eprintln!("Not in git tree. Skipping git-aware format checks");
98         }
99     } else {
100         eprintln!("Could not find usable git. Skipping git-aware format checks");
101     }
102     let ignore_fmt = ignore_fmt.build().unwrap();
103
104     let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
105         eprintln!("./x.py fmt is not supported on this channel");
106         std::process::exit(1);
107     });
108     let src = &build.src;
109     let walker = WalkBuilder::new(src).types(matcher).overrides(ignore_fmt).build_parallel();
110     walker.run(|| {
111         Box::new(move |entry| {
112             let entry = t!(entry);
113             if entry.file_type().map_or(false, |t| t.is_file()) {
114                 rustfmt(src, &rustfmt_path, &entry.path(), check);
115             }
116             ignore::WalkState::Continue
117         })
118     });
119 }