about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/bootstrap/builder.rs5
-rw-r--r--src/bootstrap/config.rs11
-rw-r--r--src/bootstrap/flags.rs26
-rw-r--r--src/bootstrap/format.rs28
-rw-r--r--src/bootstrap/lib.rs5
5 files changed, 70 insertions, 5 deletions
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 2093a49c6f9..bd0462fca6d 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -314,6 +314,7 @@ pub enum Kind {
     Check,
     Clippy,
     Fix,
+    Format,
     Test,
     Bench,
     Dist,
@@ -353,7 +354,7 @@ impl<'a> Builder<'a> {
                 tool::Miri,
                 native::Lld
             ),
-            Kind::Check | Kind::Clippy | Kind::Fix => describe!(
+            Kind::Check | Kind::Clippy | Kind::Fix | Kind::Format => describe!(
                 check::Std,
                 check::Rustc,
                 check::Rustdoc
@@ -514,7 +515,7 @@ impl<'a> Builder<'a> {
             Subcommand::Bench { ref paths, .. } => (Kind::Bench, &paths[..]),
             Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
             Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
-            Subcommand::Clean { .. } => panic!(),
+            Subcommand::Format { .. } | Subcommand::Clean { .. } => panic!(),
         };
 
         let builder = Builder {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 5f2ef01bd5c..3e67734e690 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -5,6 +5,7 @@
 
 use std::collections::{HashMap, HashSet};
 use std::env;
+use std::ffi::OsString;
 use std::fs;
 use std::path::{Path, PathBuf};
 use std::process;
@@ -149,6 +150,7 @@ pub struct Config {
     // These are either the stage0 downloaded binaries or the locally installed ones.
     pub initial_cargo: PathBuf,
     pub initial_rustc: PathBuf,
+    pub initial_rustfmt: Option<PathBuf>,
     pub out: PathBuf,
 }
 
@@ -348,12 +350,16 @@ struct TomlTarget {
 impl Config {
     fn path_from_python(var_key: &str) -> PathBuf {
         match env::var_os(var_key) {
-            // Do not trust paths from Python and normalize them slightly (#49785).
-            Some(var_val) => Path::new(&var_val).components().collect(),
+            Some(var_val) => Self::normalize_python_path(var_val),
             _ => panic!("expected '{}' to be set", var_key),
         }
     }
 
+    /// Normalizes paths from Python slightly. We don't trust paths from Python (#49785).
+    fn normalize_python_path(path: OsString) -> PathBuf {
+        Path::new(&path).components().collect()
+    }
+
     pub fn default_opts() -> Config {
         let mut config = Config::default();
         config.llvm_optimize = true;
@@ -380,6 +386,7 @@ impl Config {
 
         config.initial_rustc = Config::path_from_python("RUSTC");
         config.initial_cargo = Config::path_from_python("CARGO");
+        config.initial_rustfmt = env::var_os("RUSTFMT").map(Config::normalize_python_path);
 
         config
     }
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index 7b49cc0a929..b98e2c1bf24 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -53,6 +53,9 @@ pub enum Subcommand {
     Fix {
         paths: Vec<PathBuf>,
     },
+    Format {
+        check: bool,
+    },
     Doc {
         paths: Vec<PathBuf>,
     },
@@ -102,6 +105,7 @@ Subcommands:
     check       Compile either the compiler or libraries, using cargo check
     clippy      Run clippy
     fix         Run cargo fix
+    fmt         Run rustfmt
     test        Build and run some test suites
     bench       Build and run some benchmarks
     doc         Build documentation
@@ -160,6 +164,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`"
                 || (s == "check")
                 || (s == "clippy")
                 || (s == "fix")
+                || (s == "fmt")
                 || (s == "test")
                 || (s == "bench")
                 || (s == "doc")
@@ -222,6 +227,9 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`"
             "clean" => {
                 opts.optflag("", "all", "clean all build artifacts");
             }
+            "fmt" => {
+                opts.optflag("", "check", "check formatting instead of applying.");
+            }
             _ => {}
         };
 
@@ -323,6 +331,17 @@ Arguments:
         ./x.py fix src/libcore src/libproc_macro",
                 );
             }
+            "fmt" => {
+                subcommand_help.push_str(
+                    "\n
+Arguments:
+    This subcommand optionally accepts a `--check` flag which succeeds if formatting is correct and
+    fails if it is not. For example:
+
+        ./x.py fmt
+        ./x.py fmt --check",
+                );
+            }
             "test" => {
                 subcommand_help.push_str(
                     "\n
@@ -388,7 +407,7 @@ Arguments:
 
             let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
             extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
-        } else if subcommand.as_str() != "clean" {
+        } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
             extra_help.push_str(
                 format!(
                     "Run `./x.py {} -h -v` to see a list of available paths.",
@@ -439,6 +458,11 @@ Arguments:
                     all: matches.opt_present("all"),
                 }
             }
+            "fmt" => {
+                Subcommand::Format {
+                    check: matches.opt_present("check"),
+                }
+            }
             "dist" => Subcommand::Dist { paths },
             "install" => Subcommand::Install { paths },
             _ => {
diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs
new file mode 100644
index 00000000000..0c542594eff
--- /dev/null
+++ b/src/bootstrap/format.rs
@@ -0,0 +1,28 @@
+//! Runs rustfmt on the repository.
+
+use crate::{util, Build};
+use std::process::Command;
+
+pub fn format(build: &Build, check: bool) {
+    let target = &build.build;
+    let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
+        eprintln!("./x.py fmt is not supported on this channel");
+        std::process::exit(1);
+    }).clone();
+    let cargo_fmt_path = rustfmt_path.with_file_name(util::exe("cargo-fmt", &target));
+    assert!(cargo_fmt_path.is_file(), "{} not a file", cargo_fmt_path.display());
+
+    let mut cmd = Command::new(&cargo_fmt_path);
+    // cargo-fmt calls rustfmt as a bare command, so we need it to only find the correct one
+    cmd.env("PATH", cargo_fmt_path.parent().unwrap());
+    cmd.current_dir(&build.src);
+    cmd.arg("fmt");
+
+    if check {
+        cmd.arg("--");
+        cmd.arg("--check");
+    }
+
+    let status = cmd.status().expect("executing cargo-fmt");
+    assert!(status.success(), "cargo-fmt errored with status {:?}", status);
+}
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 080bef6853a..ff9a55afa29 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -147,6 +147,7 @@ mod builder;
 mod cache;
 mod tool;
 mod toolstate;
+mod format;
 
 #[cfg(windows)]
 mod job;
@@ -421,6 +422,10 @@ impl Build {
             job::setup(self);
         }
 
+        if let Subcommand::Format { check } = self.config.cmd {
+            return format::format(self, check);
+        }
+
         if let Subcommand::Clean { all } = self.config.cmd {
             return clean::clean(self, all);
         }