diff --git a/crates/cargo-util/src/paths.rs b/crates/cargo-util/src/paths.rs index 5e153de04cc..4a917821b7e 100644 --- a/crates/cargo-util/src/paths.rs +++ b/crates/cargo-util/src/paths.rs @@ -413,11 +413,22 @@ fn _create_dir_all(p: &Path) -> Result<()> { Ok(()) } -/// Recursively remove all files and directories at the given directory. +/// Equivalent to [`std::fs::remove_dir_all`] with better error messages. /// /// This does *not* follow symlinks. pub fn remove_dir_all>(p: P) -> Result<()> { - _remove_dir_all(p.as_ref()) + _remove_dir_all(p.as_ref()).or_else(|prev_err| { + // `std::fs::remove_dir_all` is highly specialized for different platforms + // and may be more reliable than a simple walk. We try the walk first in + // order to report more detailed errors. + fs::remove_dir_all(p.as_ref()).with_context(|| { + format!( + "{:?}\n\nError: failed to remove directory `{}`", + prev_err, + p.as_ref().display(), + ) + }) + }) } fn _remove_dir_all(p: &Path) -> Result<()> { diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index de3139c9947..b9b33690e45 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -297,7 +297,12 @@ fn rm_rf(path: &Path, config: &Config, progress: &mut dyn CleaningProgressBar) - let entry = entry?; progress.on_clean()?; if entry.file_type().is_dir() { - paths::remove_dir(entry.path()).with_context(|| "could not remove build directory")?; + // The contents should have been removed by now, but sometimes a race condition is hit + // where other files have been added by the OS. `paths::remove_dir_all` also falls back + // to `std::fs::remove_dir_all`, which may be more reliable than a simple walk in + // platform-specific edge cases. + paths::remove_dir_all(entry.path()) + .with_context(|| "could not remove build directory")?; } else { paths::remove_file(entry.path()).with_context(|| "failed to remove build artifact")?; }