about summary refs log tree commit diff
path: root/compiler/rustc_fs_util/src
diff options
context:
space:
mode:
authorBen Kimock <kimockb@gmail.com>2024-07-28 16:54:14 -0400
committerBen Kimock <kimockb@gmail.com>2025-02-24 19:46:48 -0500
commitcae7c76d509386ea29a90c4001bb8dbc59c79d22 (patch)
tree809697abba06a0f819405dbbeb601ee5b7201f37 /compiler/rustc_fs_util/src
parent9af8985e059071ea2e0566969a4f140eca73fca9 (diff)
downloadrust-cae7c76d509386ea29a90c4001bb8dbc59c79d22.tar.gz
rust-cae7c76d509386ea29a90c4001bb8dbc59c79d22.zip
Avoid no-op unlink+link dances in incr comp
Diffstat (limited to 'compiler/rustc_fs_util/src')
-rw-r--r--compiler/rustc_fs_util/src/lib.rs32
1 files changed, 19 insertions, 13 deletions
diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs
index 4e9d21c900d..0df1b243d69 100644
--- a/compiler/rustc_fs_util/src/lib.rs
+++ b/compiler/rustc_fs_util/src/lib.rs
@@ -55,25 +55,31 @@ pub enum LinkOrCopy {
     Copy,
 }
 
-/// Copies `p` into `q`, preferring to use hard-linking if possible. If
-/// `q` already exists, it is removed first.
+/// Copies `p` into `q`, preferring to use hard-linking if possible.
 /// The result indicates which of the two operations has been performed.
 pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> {
+    // Creating a hard-link will fail if the destination path already exists. We could defensively
+    // call remove_file in this function, but that pessimizes callers who can avoid such calls.
+    // Incremental compilation calls this function a lot, and is able to avoid calls that
+    // would fail the first hard_link attempt.
+
     let p = p.as_ref();
     let q = q.as_ref();
-    match fs::remove_file(q) {
-        Ok(()) => (),
-        Err(err) if err.kind() == io::ErrorKind::NotFound => (),
-        Err(err) => return Err(err),
-    }
 
-    match fs::hard_link(p, q) {
-        Ok(()) => Ok(LinkOrCopy::Link),
-        Err(_) => match fs::copy(p, q) {
-            Ok(_) => Ok(LinkOrCopy::Copy),
-            Err(e) => Err(e),
-        },
+    let err = match fs::hard_link(p, q) {
+        Ok(()) => return Ok(LinkOrCopy::Link),
+        Err(err) => err,
+    };
+
+    if err.kind() == io::ErrorKind::AlreadyExists {
+        fs::remove_file(q)?;
+        if fs::hard_link(p, q).is_ok() {
+            return Ok(LinkOrCopy::Link);
+        }
     }
+
+    // Hard linking failed, fall back to copying.
+    fs::copy(p, q).map(|_| LinkOrCopy::Copy)
 }
 
 #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]