diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs index 5a0a4c58280..667e3edd737 100644 --- a/crates/cargo-test-support/src/registry.rs +++ b/crates/cargo-test-support/src/registry.rs @@ -403,10 +403,17 @@ pub struct Dependency { optional: bool, } +/// Entry with data that corresponds to [`tar::EntryType`]. +#[non_exhaustive] +enum EntryData { + Regular(String), + Symlink(PathBuf), +} + /// A file to be created in a package. struct PackageFile { path: String, - contents: String, + contents: EntryData, /// The Unix mode for the file. Note that when extracted on Windows, this /// is mostly ignored since it doesn't have the same style of permissions. mode: u32, @@ -780,13 +787,24 @@ impl Package { pub fn file_with_mode(&mut self, path: &str, mode: u32, contents: &str) -> &mut Package { self.files.push(PackageFile { path: path.to_string(), - contents: contents.to_string(), + contents: EntryData::Regular(contents.into()), mode, extra: false, }); self } + /// Adds a symlink to a path to the package. + pub fn symlink(&mut self, dst: &str, src: &str) -> &mut Package { + self.files.push(PackageFile { + path: dst.to_string(), + contents: EntryData::Symlink(src.into()), + mode: DEFAULT_MODE, + extra: false, + }); + self + } + /// Adds an "extra" file that is not rooted within the package. /// /// Normal files are automatically placed within a directory named @@ -795,7 +813,7 @@ impl Package { pub fn extra_file(&mut self, path: &str, contents: &str) -> &mut Package { self.files.push(PackageFile { path: path.to_string(), - contents: contents.to_string(), + contents: EntryData::Regular(contents.to_string()), mode: DEFAULT_MODE, extra: true, }); @@ -1033,7 +1051,7 @@ impl Package { self.append_manifest(&mut a); } if self.files.is_empty() { - self.append(&mut a, "src/lib.rs", DEFAULT_MODE, ""); + self.append(&mut a, "src/lib.rs", DEFAULT_MODE, &EntryData::Regular("".into())); } else { for PackageFile { path, @@ -1107,10 +1125,10 @@ impl Package { manifest.push_str("[lib]\nproc-macro = true\n"); } - self.append(ar, "Cargo.toml", DEFAULT_MODE, &manifest); + self.append(ar, "Cargo.toml", DEFAULT_MODE, &EntryData::Regular(manifest.into())); } - fn append(&self, ar: &mut Builder, file: &str, mode: u32, contents: &str) { + fn append(&self, ar: &mut Builder, file: &str, mode: u32, contents: &EntryData) { self.append_raw( ar, &format!("{}-{}/{}", self.name, self.vers, file), @@ -1119,8 +1137,16 @@ impl Package { ); } - fn append_raw(&self, ar: &mut Builder, path: &str, mode: u32, contents: &str) { + fn append_raw(&self, ar: &mut Builder, path: &str, mode: u32, contents: &EntryData) { let mut header = Header::new_ustar(); + let contents = match contents { + EntryData::Regular(contents) => contents.as_str(), + EntryData::Symlink(src) => { + header.set_entry_type(tar::EntryType::Symlink); + t!(header.set_link_name(src)); + "" // Symlink has no contents. + } + }; header.set_size(contents.len() as u64); t!(header.set_path(path)); header.set_mode(mode); diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 7dbae00765f..4fcc4ffbec1 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -2583,6 +2583,47 @@ fn package_lock_inside_package_is_overwritten() { assert_eq!(ok.metadata().unwrap().len(), 2); } +#[cargo_test] +fn package_lock_as_a_symlink_inside_package_is_overwritten() { + let registry = registry::init(); + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = ">= 0.0.0" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.0.1") + .file("src/lib.rs", "pub fn f() {}") + .symlink(".cargo-ok", "src/lib.rs") + .publish(); + + p.cargo("build").run(); + + let id = SourceId::for_registry(registry.index_url()).unwrap(); + let hash = cargo::util::hex::short_hash(&id); + let pkg_root = cargo_home() + .join("registry") + .join("src") + .join(format!("-{}", hash)) + .join("bar-0.0.1"); + let ok = pkg_root.join(".cargo-ok"); + let librs = pkg_root.join("src/lib.rs"); + + // Is correctly overwritten and doesn't affect the file linked to + assert_eq!(ok.metadata().unwrap().len(), 2); + assert_eq!(fs::read_to_string(librs).unwrap(), "pub fn f() {}"); +} + #[cargo_test] fn ignores_unknown_index_version_http() { let _server = setup_http();