diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 14e2f60adad..5567537f095 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -486,7 +486,12 @@ fn hardlink_or_copy(src: &Path, dst: &Path) -> CargoResult<()> { if is_same_file(src, dst).unwrap_or(false) { return Ok(()); } - if dst.exists() { + + // NB: we can't use dst.exists(), as if dst is a broken symlink, + // dst.exists() will return false. This is problematic, as we still need to + // unlink dst in this case. symlink_metadata(dst).is_ok() will tell us + // whether dst exists *without* following symlinks, which is what we want. + if fs::symlink_metadata(dst).is_ok() { paths::remove_file(&dst)?; } diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index b351c2cf102..9a280ff3461 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -4167,6 +4167,34 @@ fn uplift_dsym_of_bin_on_mac() { assert!(!p.target_debug_dir().join("d.dSYM").exists()); } +#[cargo_test] +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn uplift_dsym_of_bin_on_mac_when_broken_link_exists() { + let p = project() + .file("src/main.rs", "fn main() { panic!(); }") + .build(); + let dsym = p.target_debug_dir().join("foo.dSYM"); + + p.cargo("build").run(); + assert!(dsym.is_dir()); + + // Simulate the situation where the underlying dSYM bundle goes missing + // but the uplifted symlink to it remains. This would previously cause + // builds to permanently fail until the bad symlink was manually removed. + dsym.rm_rf(); + p.symlink( + p.target_debug_dir() + .join("deps") + .join("foo-baaaaaadbaaaaaad.dSYM"), + &dsym, + ); + assert!(dsym.is_symlink()); + assert!(!dsym.exists()); + + p.cargo("build").run(); + assert!(dsym.is_dir()); +} + #[cargo_test] #[cfg(all(target_os = "windows", target_env = "msvc"))] fn uplift_pdb_of_bin_on_windows() {