Avoid no-op unlink+link dances in incr comp
This commit is contained in:
parent
9af8985e05
commit
cae7c76d50
6 changed files with 50 additions and 21 deletions
|
@ -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")))]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue