Skip to content

Commit 8e43074

Browse files
authored
fix(package): exclude target/package from backups (#16272)
### What does this PR try to resolve? The target directory is not excluded from backups when created with `cargo package` or `cargo publish`. This causes the `target/package` directory to be included in Time Machine backups on macOS and other backup systems that respect CACHEDIR.TAG files. The issue occurs because when `Filesystem::open()` creates parent directories in `src/cargo/util/flock.rs:336`, it uses `paths::create_dir_all()` instead of the backup-excluding version, bypassing the normal target directory creation logic. This commit adds a new `create_dir_with_backup_exclusion()` method to `Filesystem` and uses it to ensure the `target/package` directory is properly excluded from backups before creating files in it. Fixes #16238 ### How to test and review this PR? Run the following: cargo new foo cd foo cargo package --allow-dirty ls -al target/package/ Verify that `CACHEDIR.TAG` now exists in `target/package/`. All existing package tests pass.
2 parents 19678fc + b8dc393 commit 8e43074

File tree

4 files changed

+36
-1
lines changed

4 files changed

+36
-1
lines changed

src/cargo/ops/cargo_package/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ fn create_package(
161161
}
162162

163163
let filename = pkg.package_id().tarball_name();
164-
let dir = ws.build_dir().join("package");
164+
let build_dir = ws.build_dir();
165+
paths::create_dir_all_excluded_from_backups_atomic(build_dir.as_path_unlocked())?;
166+
let dir = build_dir.join("package");
165167
let mut dst = {
166168
let tmp = format!(".{}", filename);
167169
dir.open_rw_exclusive_create(&tmp, gctx, "package scratch space")?
@@ -225,6 +227,7 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Vec<Fi
225227
result.extend(packaged.into_iter().map(|(_, _, src)| src));
226228
} else {
227229
// Uplifting artifacts
230+
paths::create_dir_all_excluded_from_backups_atomic(target_dir.as_path_unlocked())?;
228231
let artifact_dir = target_dir.join("package");
229232
for (pkg, _, src) in packaged {
230233
let filename = pkg.package_id().tarball_name();

tests/testsuite/build_dir.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,15 @@ fn cargo_package_should_build_in_build_dir_and_output_to_target_dir() {
525525
[ROOT]/foo/build-dir/package/foo-0.0.1/Cargo.toml.orig
526526
[ROOT]/foo/build-dir/package/foo-0.0.1/src/main.rs
527527
[ROOT]/foo/build-dir/package/foo-0.0.1.crate
528+
[ROOT]/foo/build-dir/CACHEDIR.TAG
528529
529530
"#]]);
530531

531532
p.root()
532533
.join("target-dir")
533534
.assert_build_dir_layout(str![[r#"
534535
[ROOT]/foo/target-dir/package/foo-0.0.1.crate
536+
[ROOT]/foo/target-dir/CACHEDIR.TAG
535537
536538
"#]]);
537539
}

tests/testsuite/build_dir_legacy.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,13 +494,15 @@ fn cargo_package_should_build_in_build_dir_and_output_to_target_dir() {
494494
[ROOT]/foo/build-dir/package/foo-0.0.1/Cargo.toml.orig
495495
[ROOT]/foo/build-dir/package/foo-0.0.1/src/main.rs
496496
[ROOT]/foo/build-dir/package/foo-0.0.1.crate
497+
[ROOT]/foo/build-dir/CACHEDIR.TAG
497498
498499
"#]]);
499500

500501
p.root()
501502
.join("target-dir")
502503
.assert_build_dir_layout(str![[r#"
503504
[ROOT]/foo/target-dir/package/foo-0.0.1.crate
505+
[ROOT]/foo/target-dir/CACHEDIR.TAG
504506
505507
"#]]);
506508
}

tests/testsuite/package.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7804,3 +7804,31 @@ fn publish_to_alt_registry_warns() {
78047804
"#]])
78057805
.run();
78067806
}
7807+
7808+
#[cargo_test]
7809+
fn package_dir_not_excluded_from_backups() {
7810+
// This test documents the current behavior where target directory is NOT excluded from backups.
7811+
// After the fix, this test will be updated to verify that CACHEDIR.TAG exists.
7812+
let p = project().file("src/lib.rs", "").build();
7813+
7814+
p.cargo("package --allow-dirty")
7815+
.with_stderr_data(str![[r#"
7816+
[WARNING] manifest has no description, license, license-file, documentation, homepage or repository
7817+
|
7818+
= [NOTE] see https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info
7819+
[PACKAGING] foo v0.0.1 ([ROOT]/foo)
7820+
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
7821+
[VERIFYING] foo v0.0.1 ([ROOT]/foo)
7822+
[COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1)
7823+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
7824+
7825+
"#]])
7826+
.run();
7827+
7828+
// Verify CACHEDIR.TAG exists in target (which excludes target/ and all subdirectories)
7829+
let cachedir_tag = p.root().join("target/CACHEDIR.TAG");
7830+
assert!(
7831+
cachedir_tag.exists(),
7832+
"CACHEDIR.TAG should exist in target directory to exclude it from backups"
7833+
);
7834+
}

0 commit comments

Comments
 (0)