Avoid no-op unlink+link dances in incr comp

This commit is contained in:
Ben Kimock 2024-07-28 16:54:14 -04:00
parent 9af8985e05
commit cae7c76d50
6 changed files with 50 additions and 21 deletions

View file

@ -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),
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);
}
}
match fs::hard_link(p, q) {
Ok(()) => Ok(LinkOrCopy::Link),
Err(_) => match fs::copy(p, q) {
Ok(_) => Ok(LinkOrCopy::Copy),
Err(e) => Err(e),
},
}
// Hard linking failed, fall back to copying.
fs::copy(p, q).map(|_| LinkOrCopy::Copy)
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]