Use `TempDir` for copied lockfiles
diff --git a/Cargo.lock b/Cargo.lock
index a699463..c19e847 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1679,6 +1679,7 @@
"serde_json",
"span",
"stdx",
+ "temp-dir",
"toolchain",
"tracing",
"triomphe",
@@ -2285,6 +2286,12 @@
]
[[package]]
+name = "temp-dir"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964"
+
+[[package]]
name = "tenthash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3103,6 +3110,7 @@
"proc-macro2",
"quote",
"stdx",
+ "time",
"ungrammar",
"write-json",
"xflags",
diff --git a/Cargo.toml b/Cargo.toml
index 700c116..87202e8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -156,6 +156,7 @@
"const_generics",
] }
smol_str = "0.3.2"
+temp-dir = "0.1.16"
text-size = "1.1.1"
tracing = "0.1.41"
tracing-tree = "0.4.0"
diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml
index 27fe9f7..0dbb309 100644
--- a/crates/project-model/Cargo.toml
+++ b/crates/project-model/Cargo.toml
@@ -20,6 +20,7 @@
serde_json.workspace = true
serde.workspace = true
serde_derive.workspace = true
+temp-dir.workspace = true
tracing.workspace = true
triomphe.workspace = true
la-arena.workspace = true
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index daadcd9..25abb19 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -552,6 +552,7 @@
pub(crate) struct FetchMetadata {
command: cargo_metadata::MetadataCommand,
+ manifest_path: ManifestPath,
lockfile_path: Option<Utf8PathBuf>,
kind: &'static str,
no_deps: bool,
@@ -655,7 +656,15 @@
}
.with_context(|| format!("Failed to run `{cargo_command:?}`"));
- Self { command, lockfile_path, kind: config.kind, no_deps, no_deps_result, other_options }
+ Self {
+ manifest_path: cargo_toml.clone(),
+ command,
+ lockfile_path,
+ kind: config.kind,
+ no_deps,
+ no_deps_result,
+ other_options,
+ }
}
pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> {
@@ -672,18 +681,47 @@
locked: bool,
progress: &dyn Fn(String),
) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
- let Self { mut command, lockfile_path, kind, no_deps, no_deps_result, mut other_options } =
- self;
+ let Self {
+ mut command,
+ manifest_path,
+ lockfile_path,
+ kind,
+ no_deps,
+ no_deps_result,
+ mut other_options,
+ } = self;
if no_deps {
return no_deps_result.map(|m| (m, None));
}
let mut using_lockfile_copy = false;
+ let mut _temp_dir_guard = None;
// The manifest is a rust file, so this means its a script manifest
if let Some(lockfile) = lockfile_path {
- let target_lockfile =
- target_dir.join("rust-analyzer").join("metadata").join(kind).join("Cargo.lock");
+ _temp_dir_guard = temp_dir::TempDir::with_prefix("rust-analyzer").ok();
+ let target_lockfile = _temp_dir_guard
+ .and_then(|tmp| tmp.path().join("Cargo.lock").try_into().ok())
+ .unwrap_or_else(|| {
+ // When multiple workspaces share the same target dir, they might overwrite into a
+ // single lockfile path.
+ // See https://github.com/rust-lang/rust-analyzer/issues/20189#issuecomment-3073520255
+ let manifest_path_hash = std::hash::BuildHasher::hash_one(
+ &std::hash::BuildHasherDefault::<rustc_hash::FxHasher>::default(),
+ &manifest_path,
+ );
+ let disambiguator = format!(
+ "{}_{manifest_path_hash}",
+ manifest_path.components().nth_back(1).map_or("", |c| c.as_str())
+ );
+
+ target_dir
+ .join("rust-analyzer")
+ .join("metadata")
+ .join(kind)
+ .join(disambiguator)
+ .join("Cargo.lock")
+ });
match std::fs::copy(&lockfile, &target_lockfile) {
Ok(_) => {
using_lockfile_copy = true;
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 677f29e..655da11 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -1905,7 +1905,8 @@
meta.manifest_path(manifest);
// `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve.
// So we can use it to get `target_directory` before copying lockfiles
- let mut other_options = vec!["--no-deps".to_owned()];
+ meta.no_deps();
+ let mut other_options = vec![];
if manifest.is_rust_manifest() {
meta.env("RUSTC_BOOTSTRAP", "1");
other_options.push("-Zscript".to_owned());