Skip to content

Commit 4f6553a

Browse files
committed
Use canonical paths when parsing dep-info.
Instead of treating Windows differently, this just always uses canonical paths on all platforms. This fixes a problem where symlinks were not treated correctly on all platforms. Switching rm_rf to the remove_dir_all crate because deleting symbolic links on Windows is difficult.
1 parent ff532ec commit 4f6553a

File tree

5 files changed

+107
-28
lines changed

5 files changed

+107
-28
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ memchr = "2.1.3"
4747
num_cpus = "1.0"
4848
opener = "0.4"
4949
percent-encoding = "2.0"
50+
remove_dir_all = "0.5.2"
5051
rustfix = "0.4.4"
5152
same-file = "1"
5253
semver = { version = "0.9.0", features = ["serde"] }

src/cargo/core/compiler/fingerprint.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,18 +1572,18 @@ pub fn translate_dep_info(
15721572
.ok_or_else(|| internal("malformed dep-info format, no targets".to_string()))?
15731573
.1;
15741574

1575+
let target_root = target_root.canonicalize()?;
1576+
let pkg_root = pkg_root.canonicalize()?;
15751577
let mut new_contents = Vec::new();
15761578
for file in deps {
1577-
let file = if cfg!(windows) && file.starts_with("\\\\?\\") {
1578-
// Remove Windows extended-length prefix, since functions like
1579-
// strip_prefix won't work if you mix with traditional dos paths.
1580-
PathBuf::from(&file[4..])
1581-
} else {
1582-
rustc_cwd.join(file)
1583-
};
1584-
let (ty, path) = if let Ok(stripped) = file.strip_prefix(target_root) {
1579+
// The path may be absolute or relative, canonical or not. Make sure
1580+
// it is canonicalized so we are comparing the same kinds of paths.
1581+
let canon_file = rustc_cwd.join(file).canonicalize()?;
1582+
let abs_file = rustc_cwd.join(file);
1583+
1584+
let (ty, path) = if let Ok(stripped) = canon_file.strip_prefix(&target_root) {
15851585
(DepInfoPathType::TargetRootRelative, stripped)
1586-
} else if let Ok(stripped) = file.strip_prefix(pkg_root) {
1586+
} else if let Ok(stripped) = canon_file.strip_prefix(&pkg_root) {
15871587
if !allow_package {
15881588
continue;
15891589
}
@@ -1592,8 +1592,7 @@ pub fn translate_dep_info(
15921592
// It's definitely not target root relative, but this is an absolute path (since it was
15931593
// joined to rustc_cwd) and as such re-joining it later to the target root will have no
15941594
// effect.
1595-
assert!(file.is_absolute(), "{:?} is absolute", file);
1596-
(DepInfoPathType::TargetRootRelative, &*file)
1595+
(DepInfoPathType::TargetRootRelative, &*abs_file)
15971596
};
15981597
new_contents.push(ty as u8);
15991598
new_contents.extend(util::path2bytes(path)?);

tests/testsuite/dep_info.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
use crate::support::paths::{self, CargoPathExt};
12
use crate::support::registry::Package;
2-
use crate::support::{
3-
basic_bin_manifest, basic_manifest, main_file, paths, project, rustc_host, Project,
4-
};
3+
use crate::support::{basic_bin_manifest, basic_manifest, main_file, project, rustc_host, Project};
54
use filetime::FileTime;
65
use std::fs;
76
use std::path::Path;
@@ -460,3 +459,43 @@ fn reg_dep_source_not_tracked() {
460459
},
461460
);
462461
}
462+
463+
#[cargo_test]
464+
// Remove once https://github.com/rust-lang/rust/pull/61727 lands, and switch
465+
// to a `nightly` check.
466+
#[ignore]
467+
fn canonical_path() {
468+
if !crate::support::symlink_supported() {
469+
return;
470+
}
471+
Package::new("regdep", "0.1.0")
472+
.file("src/lib.rs", "pub fn f() {}")
473+
.publish();
474+
475+
let p = project()
476+
.file(
477+
"Cargo.toml",
478+
r#"
479+
[package]
480+
name = "foo"
481+
version = "0.1.0"
482+
483+
[dependencies]
484+
regdep = "0.1"
485+
"#,
486+
)
487+
.file("src/lib.rs", "pub fn f() { regdep::f(); }")
488+
.build();
489+
490+
let real = p.root().join("real_target");
491+
real.mkdir_p();
492+
p.symlink(real, "target");
493+
494+
p.cargo("build").run();
495+
496+
assert_deps_contains(
497+
&p,
498+
"target/debug/.fingerprint/foo-*/dep-lib-foo-*",
499+
&[(1, "src/lib.rs"), (2, "debug/deps/libregdep-*.rlib")],
500+
);
501+
}

tests/testsuite/support/mod.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,29 @@ impl Project {
448448
.write_all(contents.replace("#", "").as_bytes())
449449
.unwrap();
450450
}
451+
452+
pub fn symlink(&self, src: impl AsRef<Path>, dst: impl AsRef<Path>) {
453+
let src = self.root().join(src.as_ref());
454+
let dst = self.root().join(dst.as_ref());
455+
#[cfg(unix)]
456+
{
457+
if let Err(e) = os::unix::fs::symlink(&src, &dst) {
458+
panic!("failed to symlink {:?} to {:?}: {:?}", src, dst, e);
459+
}
460+
}
461+
#[cfg(windows)]
462+
{
463+
if src.is_dir() {
464+
if let Err(e) = os::windows::fs::symlink_dir(&src, &dst) {
465+
panic!("failed to symlink {:?} to {:?}: {:?}", src, dst, e);
466+
}
467+
} else {
468+
if let Err(e) = os::windows::fs::symlink_file(&src, &dst) {
469+
panic!("failed to symlink {:?} to {:?}: {:?}", src, dst, e);
470+
}
471+
}
472+
}
473+
}
451474
}
452475

453476
// Generates a project layout
@@ -1746,3 +1769,31 @@ pub fn clippy_is_available() -> bool {
17461769
true
17471770
}
17481771
}
1772+
1773+
#[cfg(windows)]
1774+
pub fn symlink_supported() -> bool {
1775+
let src = paths::root().join("symlink_src");
1776+
fs::write(&src, "").unwrap();
1777+
let dst = paths::root().join("symlink_dst");
1778+
let result = match os::windows::fs::symlink_file(&src, &dst) {
1779+
Ok(_) => {
1780+
fs::remove_file(&dst).unwrap();
1781+
true
1782+
}
1783+
Err(e) => {
1784+
eprintln!(
1785+
"symlinks not supported: {:?}\n\
1786+
Windows 10 users should enable developer mode.",
1787+
e
1788+
);
1789+
false
1790+
}
1791+
};
1792+
fs::remove_file(&src).unwrap();
1793+
return result;
1794+
}
1795+
1796+
#[cfg(not(windows))]
1797+
pub fn symlink_supported() -> bool {
1798+
true
1799+
}

tests/testsuite/support/paths.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,11 @@ impl CargoPathExt for Path {
113113
* care all that much for our tests
114114
*/
115115
fn rm_rf(&self) {
116-
if !self.exists() {
117-
return;
118-
}
119-
120-
for file in t!(fs::read_dir(self)) {
121-
let file = t!(file);
122-
if file.file_type().map(|m| m.is_dir()).unwrap_or(false) {
123-
file.path().rm_rf();
124-
} else {
125-
// On windows we can't remove a readonly file, and git will
126-
// often clone files as readonly. As a result, we have some
127-
// special logic to remove readonly files on windows.
128-
do_op(&file.path(), "remove file", |p| fs::remove_file(p));
116+
if self.exists() {
117+
if let Err(e) = remove_dir_all::remove_dir_all(self) {
118+
panic!("failed to remove {:?}: {:?}", self, e)
129119
}
130120
}
131-
do_op(self, "remove dir", |p| fs::remove_dir(p));
132121
}
133122

134123
fn mkdir_p(&self) {

0 commit comments

Comments
 (0)