diff --git a/rls/src/build/cargo.rs b/rls/src/build/cargo.rs index 01f6bc8f303..6e9929adc01 100644 --- a/rls/src/build/cargo.rs +++ b/rls/src/build/cargo.rs @@ -260,6 +260,17 @@ fn run_cargo_ws( Arc::clone(&reached_primary), ); + // Cargo excludes target/ from backups since rust-lang/cargo@cf3bfc9/rust-lang/cargo#8378 but + // it does so if and only if the directory does not exist and it's about to create it. + // rls runs cargo internally with target directory set to target/rls/ so, if target/ doesn't + // exist yet and rls runs, target/ and target/rls/ will be created. While target/rls/ will be + // excluded from backups target/ won't be (as from our perspective it's not the target + // directory but its parent) and, when user runs "cargo build" themselves cargo will see + // target/ existing already and won't exclude it from backups. We can work around that by + // attempting to create a backup-excluded target/ ourelves using cargo paths:: machinery. + cargo::util::paths::create_dir_all_excluded_from_backups_atomic( + config.target_dir().unwrap().unwrap().as_path_unlocked().parent().unwrap(), + )?; let exec = Arc::new(exec) as Arc<dyn Executor>; match compile_with_exec(&ws, &compile_opts, &exec) { Ok(_) => { diff --git a/tests/client.rs b/tests/client.rs index e01a7fdeb67..18b2b75a6dd 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1,3 +1,4 @@ +use std::fs; use std::path::Path; use std::time::{Duration, Instant}; @@ -2227,3 +2228,30 @@ fn client_parse_error_on_malformed_input() { // to provide better fault tolerance. cmd.wait().unwrap(); } + +#[test] +fn client_cargo_target_directory_is_excluded_from_backups() { + // This is to make sure that if it's rls that crates target/ directory the directory is + // excluded from backups just as if it was created by cargo itself. See a comment in + // run_cargo_ws() or rust-lang/cargo@cf3bfc9/rust-lang/cargo#8378 for more information. + let p = project("backup_exclusion_workspace") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn main() { + println!("Hello world!"); + } + "#, + ) + .build(); + let root_path = p.root(); + let mut rls = p.spawn_rls_async(); + rls.request::<Initialize>(0, initialize_params(root_path)); + let _ = rls.wait_for_indexing(); + let cachedir_tag = p.root().join("target").join("CACHEDIR.TAG"); + assert!(cachedir_tag.is_file()); + assert!(fs::read_to_string(&cachedir_tag) + .unwrap() + .starts_with("Signature: 8a477f597d28d172789f06886806bc55")); +}