]> git.lizzy.rs Git - rust.git/blobdiff - src/bootstrap/format.rs
Merge commit '4f3ab69ea0a0908260944443c739426cc384ae1a' into clippyup
[rust.git] / src / bootstrap / format.rs
index b99bf33f4829c75c6931bc34b9ef45ae9c95c77e..b49322e3c028fc07d5eb1a54702a08d97d7e59be 100644 (file)
@@ -1,7 +1,7 @@
 //! Runs rustfmt on the repository.
 
 use crate::builder::Builder;
-use crate::util::{output, t};
+use crate::util::{output, program_out_of_date, t};
 use ignore::WalkBuilder;
 use std::collections::VecDeque;
 use std::path::{Path, PathBuf};
@@ -44,6 +44,61 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F
     }
 }
 
+fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, PathBuf)> {
+    let stamp_file = build.out.join("rustfmt.stamp");
+
+    let mut cmd = Command::new(match build.initial_rustfmt() {
+        Some(p) => p,
+        None => return None,
+    });
+    cmd.arg("--version");
+    let output = match cmd.output() {
+        Ok(status) => status,
+        Err(_) => return None,
+    };
+    if !output.status.success() {
+        return None;
+    }
+    Some((String::from_utf8(output.stdout).unwrap(), stamp_file))
+}
+
+/// Return whether the format cache can be reused.
+fn verify_rustfmt_version(build: &Builder<'_>) -> bool {
+    let Some((version, stamp_file)) = get_rustfmt_version(build) else {return false;};
+    !program_out_of_date(&stamp_file, &version)
+}
+
+/// Updates the last rustfmt version used
+fn update_rustfmt_version(build: &Builder<'_>) {
+    let Some((version, stamp_file)) = get_rustfmt_version(build) else {return;};
+    t!(std::fs::write(stamp_file, version))
+}
+
+/// Returns the files modified between the `merge-base` of HEAD and
+/// rust-lang/master and what is now on the disk.
+///
+/// Returns `None` if all files should be formatted.
+fn get_modified_files(build: &Builder<'_>) -> Option<Vec<String>> {
+    let Ok(remote) = get_rust_lang_rust_remote() else {return None;};
+    if !verify_rustfmt_version(build) {
+        return None;
+    }
+    Some(
+        output(
+            build
+                .config
+                .git()
+                .arg("diff-index")
+                .arg("--name-only")
+                .arg("--merge-base")
+                .arg(&format!("{remote}/master")),
+        )
+        .lines()
+        .map(|s| s.trim().to_owned())
+        .collect(),
+    )
+}
+
 /// Finds the remote for rust-lang/rust.
 /// For example for these remotes it will return `upstream`.
 /// ```text
@@ -140,20 +195,11 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
                 ignore_fmt.add(&format!("!/{}", untracked_path)).expect(&untracked_path);
             }
             if !check && paths.is_empty() {
-                let remote = t!(get_rust_lang_rust_remote());
-                let base = output(
-                    build
-                        .config
-                        .git()
-                        .arg("merge-base")
-                        .arg("HEAD")
-                        .arg(format!("{remote}/master")),
-                );
-                let files =
-                    output(build.config.git().arg("diff").arg("--name-only").arg(base.trim()));
-                for file in files.lines() {
-                    println!("formatting modified file {file}");
-                    ignore_fmt.add(&format!("/{file}")).expect(file);
+                if let Some(files) = get_modified_files(build) {
+                    for file in files {
+                        println!("formatting modified file {file}");
+                        ignore_fmt.add(&format!("/{file}")).expect(&file);
+                    }
                 }
             }
         } else {
@@ -233,4 +279,7 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
     drop(tx);
 
     thread.join().unwrap();
+    if !check {
+        update_rustfmt_version(build);
+    }
 }