1 //! Runs rustfmt on the repository.
4 use build_helper::{output, t};
5 use ignore::WalkBuilder;
7 use std::process::Command;
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");
21 let cmd_debug = format!("{:?}", cmd);
22 let status = cmd.status().expect("executing rustfmt");
23 if !status.success() {
25 "Running `{}` failed.\nIf you're running `tidy`, \
26 try again with `--bless` flag. Or, you just want to format \
27 code, run `./x.py fmt` instead.",
30 std::process::exit(1);
34 #[derive(serde::Deserialize)]
35 struct RustfmtConfig {
39 pub fn format(build: &Build, check: bool) {
40 let mut builder = ignore::types::TypesBuilder::new();
41 builder.add_defaults();
42 builder.select("rust");
43 let matcher = builder.build().unwrap();
44 let rustfmt_config = build.src.join("rustfmt.toml");
45 if !rustfmt_config.exists() {
46 eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
47 eprintln!("This may happen in distributed tarballs.");
50 let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
51 let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
52 let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
53 for ignore in rustfmt_config.ignore {
54 ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
56 let untracked_paths_output = output(
57 Command::new("git").arg("status").arg("--porcelain").arg("--untracked-files=normal"),
59 let untracked_paths = untracked_paths_output
61 .filter(|entry| entry.starts_with("??"))
62 .map(|entry| entry.split(" ").nth(1).expect("every git status entry should list a path"));
63 for untracked_path in untracked_paths {
64 eprintln!("skip untracked path {} during rustfmt invocations", untracked_path);
65 ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path);
67 let ignore_fmt = ignore_fmt.build().unwrap();
69 let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
70 eprintln!("./x.py fmt is not supported on this channel");
71 std::process::exit(1);
73 let src = build.src.clone();
74 let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel();
76 let src = src.clone();
77 let rustfmt_path = rustfmt_path.clone();
78 Box::new(move |entry| {
79 let entry = t!(entry);
80 if entry.file_type().map_or(false, |t| t.is_file()) {
81 rustfmt(&src, &rustfmt_path, &entry.path(), check);
83 ignore::WalkState::Continue