about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
author许杰友 Jieyou Xu (Joe) <jieyouxu@outlook.com>2024-08-22 15:54:52 +0000
committer许杰友 Jieyou Xu (Joe) <jieyouxu@outlook.com>2024-08-22 15:54:52 +0000
commite0900a1bb79a6c844fadb4b27fe8e0793d952476 (patch)
tree3d59b6e7234f6ac96476a5b3b908b3bd803fd3a9 /src
parent0f6e1ae67854c3c44726e8376144c16b465fe7c6 (diff)
downloadrust-e0900a1bb79a6c844fadb4b27fe8e0793d952476.tar.gz
rust-e0900a1bb79a6c844fadb4b27fe8e0793d952476.zip
Revert "bootstrap: fix clean's `remove_dir_all` implementation"
This reverts commit 1687c55168f3837506afcd2240a8a0b6eadcc1eb.
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/src/core/build_steps/clean.rs93
1 files changed, 78 insertions, 15 deletions
diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index bcbe490c36a..f608e5d715e 100644
--- a/src/bootstrap/src/core/build_steps/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -6,6 +6,7 @@
 //! directory unless the `--all` flag is present.
 
 use std::fs;
+use std::io::{self, ErrorKind};
 use std::path::Path;
 
 use crate::core::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
@@ -100,11 +101,11 @@ fn clean(build: &Build, all: bool, stage: Option<u32>) {
         return;
     }
 
-    remove_dir_recursive("tmp");
+    rm_rf("tmp".as_ref());
 
     // Clean the entire build directory
     if all {
-        remove_dir_recursive(&build.out);
+        rm_rf(&build.out);
         return;
     }
 
@@ -135,17 +136,17 @@ fn clean_specific_stage(build: &Build, stage: u32) {
             }
 
             let path = t!(entry.path().canonicalize());
-            remove_dir_recursive(&path);
+            rm_rf(&path);
         }
     }
 }
 
 fn clean_default(build: &Build) {
-    remove_dir_recursive(build.out.join("tmp"));
-    remove_dir_recursive(build.out.join("dist"));
-    remove_dir_recursive(build.out.join("bootstrap").join(".last-warned-change-id"));
-    remove_dir_recursive(build.out.join("bootstrap-shims-dump"));
-    remove_dir_recursive(build.out.join("rustfmt.stamp"));
+    rm_rf(&build.out.join("tmp"));
+    rm_rf(&build.out.join("dist"));
+    rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id"));
+    rm_rf(&build.out.join("bootstrap-shims-dump"));
+    rm_rf(&build.out.join("rustfmt.stamp"));
 
     let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect();
     // After cross-compilation, artifacts of the host architecture (which may differ from build.host)
@@ -165,16 +166,78 @@ fn clean_default(build: &Build) {
                 continue;
             }
             let path = t!(entry.path().canonicalize());
-            remove_dir_recursive(&path);
+            rm_rf(&path);
         }
     }
 }
 
-/// Wrapper for [`std::fs::remove_dir_all`] that panics on failure and prints the `path` we failed
-/// on.
-fn remove_dir_recursive<P: AsRef<Path>>(path: P) {
-    let path = path.as_ref();
-    if let Err(e) = fs::remove_dir_all(path) {
-        panic!("failed to `remove_dir_all` at `{}`: {e}", path.display());
+fn rm_rf(path: &Path) {
+    match path.symlink_metadata() {
+        Err(e) => {
+            if e.kind() == ErrorKind::NotFound {
+                return;
+            }
+            panic!("failed to get metadata for file {}: {}", path.display(), e);
+        }
+        Ok(metadata) => {
+            if metadata.file_type().is_file() || metadata.file_type().is_symlink() {
+                do_op(path, "remove file", |p| match fs::remove_file(p) {
+                    #[cfg(windows)]
+                    Err(e)
+                        if e.kind() == std::io::ErrorKind::PermissionDenied
+                            && p.file_name().and_then(std::ffi::OsStr::to_str)
+                                == Some("bootstrap.exe") =>
+                    {
+                        eprintln!("WARNING: failed to delete '{}'.", p.display());
+                        Ok(())
+                    }
+                    r => r,
+                });
+
+                return;
+            }
+
+            for file in t!(fs::read_dir(path)) {
+                rm_rf(&t!(file).path());
+            }
+
+            do_op(path, "remove dir", |p| match fs::remove_dir(p) {
+                // Check for dir not empty on Windows
+                // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized,
+                // match on `e.kind()` instead.
+                #[cfg(windows)]
+                Err(e) if e.raw_os_error() == Some(145) => Ok(()),
+                r => r,
+            });
+        }
+    };
+}
+
+fn do_op<F>(path: &Path, desc: &str, mut f: F)
+where
+    F: FnMut(&Path) -> io::Result<()>,
+{
+    match f(path) {
+        Ok(()) => {}
+        // On windows we can't remove a readonly file, and git will often clone files as readonly.
+        // As a result, we have some special logic to remove readonly files on windows.
+        // This is also the reason that we can't use things like fs::remove_dir_all().
+        #[cfg(windows)]
+        Err(ref e) if e.kind() == ErrorKind::PermissionDenied => {
+            let m = t!(path.symlink_metadata());
+            let mut p = m.permissions();
+            p.set_readonly(false);
+            t!(fs::set_permissions(path, p));
+            f(path).unwrap_or_else(|e| {
+                // Delete symlinked directories on Windows
+                if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() {
+                    return;
+                }
+                panic!("failed to {} {}: {}", desc, path.display(), e);
+            });
+        }
+        Err(e) => {
+            panic!("failed to {} {}: {}", desc, path.display(), e);
+        }
     }
 }