Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21015 from ShoyuVanilla/cargo-config-origin
fix: Parse cargo config files with origins
Shoyu Vanilla (Flint) 5 months ago
parent c937fcc · parent 8d18080 · commit c9e3934
-rw-r--r--Cargo.lock154
-rw-r--r--Cargo.toml3
-rw-r--r--crates/proc-macro-srv/proc-macro-test/Cargo.toml2
-rw-r--r--crates/project-model/Cargo.toml1
-rw-r--r--crates/project-model/src/cargo_config_file.rs227
-rw-r--r--crates/project-model/src/env.rs103
-rw-r--r--crates/project-model/src/toolchain_info/target_tuple.rs48
-rw-r--r--crates/project-model/src/workspace.rs35
-rw-r--r--crates/rust-analyzer/Cargo.toml2
9 files changed, 319 insertions, 256 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5bc4d9f3df..c1dbe6a7a5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -187,69 +187,21 @@ dependencies = [
[[package]]
name = "cargo-platform"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "cargo-util-schemas"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e63d2780ac94487eb9f1fea7b0d56300abc9eb488800854ca217f102f5caccca"
-dependencies = [
- "semver",
- "serde",
- "serde-untagged",
- "serde-value",
- "thiserror 1.0.69",
- "toml",
- "unicode-xid",
- "url",
-]
-
-[[package]]
-name = "cargo-util-schemas"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
-dependencies = [
- "semver",
- "serde",
- "serde-untagged",
- "serde-value",
- "thiserror 2.0.16",
- "toml",
- "unicode-xid",
- "url",
-]
-
-[[package]]
-name = "cargo_metadata"
-version = "0.20.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f7835cfc6135093070e95eb2b53e5d9b5c403dc3a6be6040ee026270aa82502"
+checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4"
dependencies = [
- "camino",
- "cargo-platform",
- "cargo-util-schemas 0.2.0",
- "semver",
"serde",
- "serde_json",
- "thiserror 2.0.16",
]
[[package]]
name = "cargo_metadata"
-version = "0.21.0"
+version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868"
+checksum = "981a6f317983eec002839b90fae7411a85621410ae591a9cab2ecf5cb5744873"
dependencies = [
"camino",
"cargo-platform",
- "cargo-util-schemas 0.8.2",
"semver",
"serde",
"serde_json",
@@ -624,17 +576,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
-name = "erased-serde"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b"
-dependencies = [
- "serde",
- "serde_core",
- "typeid",
-]
-
-[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1717,15 +1658,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
-name = "ordered-float"
-version = "2.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
name = "parking_lot"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1937,7 +1869,7 @@ dependencies = [
name = "proc-macro-test"
version = "0.0.0"
dependencies = [
- "cargo_metadata 0.20.0",
+ "cargo_metadata",
]
[[package]]
@@ -1978,7 +1910,7 @@ version = "0.0.0"
dependencies = [
"anyhow",
"base-db",
- "cargo_metadata 0.21.0",
+ "cargo_metadata",
"cfg",
"expect-test",
"intern",
@@ -1993,6 +1925,7 @@ dependencies = [
"span",
"stdx",
"temp-dir",
+ "toml",
"toolchain",
"tracing",
"triomphe",
@@ -2343,7 +2276,7 @@ version = "0.0.0"
dependencies = [
"anyhow",
"base64",
- "cargo_metadata 0.21.0",
+ "cargo_metadata",
"cfg",
"crossbeam-channel",
"dhat",
@@ -2591,28 +2524,6 @@ dependencies = [
]
[[package]]
-name = "serde-untagged"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058"
-dependencies = [
- "erased-serde",
- "serde",
- "serde_core",
- "typeid",
-]
-
-[[package]]
-name = "serde-value"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
-dependencies = [
- "ordered-float",
- "serde",
-]
-
-[[package]]
name = "serde_core"
version = "1.0.226"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2659,11 +2570,11 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "0.6.9"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
dependencies = [
- "serde",
+ "serde_core",
]
[[package]]
@@ -3042,44 +2953,42 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.8.23"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
+checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
dependencies = [
- "serde",
+ "indexmap",
+ "serde_core",
"serde_spanned",
"toml_datetime",
- "toml_edit",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
]
[[package]]
name = "toml_datetime"
-version = "0.6.11"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
+checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
dependencies = [
- "serde",
+ "serde_core",
]
[[package]]
-name = "toml_edit"
-version = "0.22.27"
+name = "toml_parser"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
+checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
dependencies = [
- "indexmap",
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_write",
"winnow",
]
[[package]]
-name = "toml_write"
-version = "0.1.2"
+name = "toml_writer"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
+checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
[[package]]
name = "toolchain"
@@ -3181,12 +3090,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
-name = "typeid"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
-
-[[package]]
name = "unarray"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3654,9 +3557,6 @@ name = "winnow"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
-dependencies = [
- "memchr",
-]
[[package]]
name = "wit-bindgen"
diff --git a/Cargo.toml b/Cargo.toml
index df9442882c..35f2fe4a95 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -106,7 +106,7 @@ lsp-server = { version = "0.7.9" }
anyhow = "1.0.98"
arrayvec = "0.7.6"
bitflags = "2.9.1"
-cargo_metadata = "0.21.0"
+cargo_metadata = "0.23.0"
camino = "1.1.10"
crossbeam-channel = "0.5.15"
dissimilar = "1.0.10"
@@ -155,6 +155,7 @@ smallvec = { version = "1.15.1", features = [
smol_str = "0.3.2"
temp-dir = "0.1.16"
text-size = "1.1.1"
+toml = "0.9.8"
tracing = "0.1.41"
tracing-tree = "0.4.0"
tracing-subscriber = { version = "0.3.20", default-features = false, features = [
diff --git a/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/crates/proc-macro-srv/proc-macro-test/Cargo.toml
index bc04482273..78630ddb4d 100644
--- a/crates/proc-macro-srv/proc-macro-test/Cargo.toml
+++ b/crates/proc-macro-srv/proc-macro-test/Cargo.toml
@@ -10,4 +10,4 @@ license = "MIT OR Apache-2.0"
doctest = false
[build-dependencies]
-cargo_metadata = "0.20.0"
+cargo_metadata = "0.23.0"
diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml
index 0dbb309a62..ec44369fa9 100644
--- a/crates/project-model/Cargo.toml
+++ b/crates/project-model/Cargo.toml
@@ -21,6 +21,7 @@ serde_json.workspace = true
serde.workspace = true
serde_derive.workspace = true
temp-dir.workspace = true
+toml.workspace = true
tracing.workspace = true
triomphe.workspace = true
la-arena.workspace = true
diff --git a/crates/project-model/src/cargo_config_file.rs b/crates/project-model/src/cargo_config_file.rs
index a1e7ed0923..5d6e5fd648 100644
--- a/crates/project-model/src/cargo_config_file.rs
+++ b/crates/project-model/src/cargo_config_file.rs
@@ -1,37 +1,135 @@
-//! Read `.cargo/config.toml` as a JSON object
-use paths::{Utf8Path, Utf8PathBuf};
+//! Read `.cargo/config.toml` as a TOML table
+use paths::{AbsPath, Utf8Path, Utf8PathBuf};
use rustc_hash::FxHashMap;
+use toml::{
+ Spanned,
+ de::{DeTable, DeValue},
+};
use toolchain::Tool;
use crate::{ManifestPath, Sysroot, utf8_stdout};
-pub(crate) type CargoConfigFile = serde_json::Map<String, serde_json::Value>;
-
-pub(crate) fn read(
- manifest: &ManifestPath,
- extra_env: &FxHashMap<String, Option<String>>,
- sysroot: &Sysroot,
-) -> Option<CargoConfigFile> {
- let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
- cargo_config
- .args(["-Z", "unstable-options", "config", "get", "--format", "json"])
- .env("RUSTC_BOOTSTRAP", "1");
- if manifest.is_rust_manifest() {
- cargo_config.arg("-Zscript");
- }
-
- tracing::debug!("Discovering cargo config by {:?}", cargo_config);
- let json: serde_json::Map<String, serde_json::Value> = utf8_stdout(&mut cargo_config)
- .inspect(|json| {
- tracing::debug!("Discovered cargo config: {:?}", json);
- })
- .inspect_err(|err| {
- tracing::debug!("Failed to discover cargo config: {:?}", err);
- })
- .ok()
- .and_then(|stdout| serde_json::from_str(&stdout).ok())?;
-
- Some(json)
+#[derive(Clone)]
+pub struct CargoConfigFile(String);
+
+impl CargoConfigFile {
+ pub(crate) fn load(
+ manifest: &ManifestPath,
+ extra_env: &FxHashMap<String, Option<String>>,
+ sysroot: &Sysroot,
+ ) -> Option<Self> {
+ let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
+ cargo_config
+ .args(["-Z", "unstable-options", "config", "get", "--format", "toml", "--show-origin"])
+ .env("RUSTC_BOOTSTRAP", "1");
+ if manifest.is_rust_manifest() {
+ cargo_config.arg("-Zscript");
+ }
+
+ tracing::debug!("Discovering cargo config by {cargo_config:?}");
+ utf8_stdout(&mut cargo_config)
+ .inspect(|toml| {
+ tracing::debug!("Discovered cargo config: {toml:?}");
+ })
+ .inspect_err(|err| {
+ tracing::debug!("Failed to discover cargo config: {err:?}");
+ })
+ .ok()
+ .map(CargoConfigFile)
+ }
+
+ pub(crate) fn read<'a>(&'a self) -> Option<CargoConfigFileReader<'a>> {
+ CargoConfigFileReader::new(&self.0)
+ }
+
+ #[cfg(test)]
+ pub(crate) fn from_string_for_test(s: String) -> Self {
+ CargoConfigFile(s)
+ }
+}
+
+pub(crate) struct CargoConfigFileReader<'a> {
+ toml_str: &'a str,
+ line_ends: Vec<usize>,
+ table: Spanned<DeTable<'a>>,
+}
+
+impl<'a> CargoConfigFileReader<'a> {
+ fn new(toml_str: &'a str) -> Option<Self> {
+ let toml = DeTable::parse(toml_str)
+ .inspect_err(|err| tracing::debug!("Failed to parse cargo config into toml: {err:?}"))
+ .ok()?;
+ let mut last_line_end = 0;
+ let line_ends = toml_str
+ .lines()
+ .map(|l| {
+ last_line_end += l.len() + 1;
+ last_line_end
+ })
+ .collect();
+
+ Some(CargoConfigFileReader { toml_str, table: toml, line_ends })
+ }
+
+ pub(crate) fn get_spanned(
+ &self,
+ accessor: impl IntoIterator<Item = &'a str>,
+ ) -> Option<&Spanned<DeValue<'a>>> {
+ let mut keys = accessor.into_iter();
+ let mut val = self.table.get_ref().get(keys.next()?)?;
+ for key in keys {
+ let DeValue::Table(map) = val.get_ref() else { return None };
+ val = map.get(key)?;
+ }
+ Some(val)
+ }
+
+ pub(crate) fn get(&self, accessor: impl IntoIterator<Item = &'a str>) -> Option<&DeValue<'a>> {
+ self.get_spanned(accessor).map(|it| it.as_ref())
+ }
+
+ pub(crate) fn get_origin_root(&self, spanned: &Spanned<DeValue<'a>>) -> Option<&AbsPath> {
+ let span = spanned.span();
+
+ for &line_end in &self.line_ends {
+ if line_end < span.end {
+ continue;
+ }
+
+ let after_span = &self.toml_str[span.end..line_end];
+
+ // table.key = "value" # /parent/.cargo/config.toml
+ // | |
+ // span.end line_end
+ let origin_path = after_span
+ .strip_prefix([',']) // strip trailing comma
+ .unwrap_or(after_span)
+ .trim_start()
+ .strip_prefix(['#'])
+ .and_then(|path| {
+ let path = path.trim();
+ if path.starts_with("environment variable")
+ || path.starts_with("--config cli option")
+ {
+ None
+ } else {
+ Some(path)
+ }
+ });
+
+ return origin_path.and_then(|path| {
+ <&Utf8Path>::from(path)
+ .try_into()
+ .ok()
+ // Two levels up to the config file.
+ // See https://doc.rust-lang.org/cargo/reference/config.html#config-relative-paths
+ .and_then(AbsPath::parent)
+ .and_then(AbsPath::parent)
+ });
+ }
+
+ None
+ }
}
pub(crate) fn make_lockfile_copy(
@@ -54,3 +152,74 @@ pub(crate) fn make_lockfile_copy(
}
}
}
+
+#[test]
+fn cargo_config_file_reader_works() {
+ #[cfg(target_os = "windows")]
+ let root = "C://ROOT";
+
+ #[cfg(not(target_os = "windows"))]
+ let root = "/ROOT";
+
+ let toml = format!(
+ r##"
+alias.foo = "abc"
+alias.bar = "🙂" # {root}/home/.cargo/config.toml
+alias.sub-example = [
+ "sub", # {root}/foo/.cargo/config.toml
+ "example", # {root}/❤️💛💙/💝/.cargo/config.toml
+]
+build.rustflags = [
+ "--flag", # {root}/home/.cargo/config.toml
+ "env", # environment variable `CARGO_BUILD_RUSTFLAGS`
+ "cli", # --config cli option
+]
+env.CARGO_WORKSPACE_DIR.relative = true # {root}/home/.cargo/config.toml
+env.CARGO_WORKSPACE_DIR.value = "" # {root}/home/.cargo/config.toml
+"##
+ );
+
+ let reader = CargoConfigFileReader::new(&toml).unwrap();
+
+ let alias_foo = reader.get_spanned(["alias", "foo"]).unwrap();
+ assert_eq!(alias_foo.as_ref().as_str().unwrap(), "abc");
+ assert!(reader.get_origin_root(alias_foo).is_none());
+
+ let alias_bar = reader.get_spanned(["alias", "bar"]).unwrap();
+ assert_eq!(alias_bar.as_ref().as_str().unwrap(), "🙂");
+ assert_eq!(reader.get_origin_root(alias_bar).unwrap().as_str(), format!("{root}/home"));
+
+ let alias_sub_example = reader.get_spanned(["alias", "sub-example"]).unwrap();
+ assert!(reader.get_origin_root(alias_sub_example).is_none());
+ let alias_sub_example = alias_sub_example.as_ref().as_array().unwrap();
+
+ assert_eq!(alias_sub_example[0].get_ref().as_str().unwrap(), "sub");
+ assert_eq!(
+ reader.get_origin_root(&alias_sub_example[0]).unwrap().as_str(),
+ format!("{root}/foo")
+ );
+
+ assert_eq!(alias_sub_example[1].get_ref().as_str().unwrap(), "example");
+ assert_eq!(
+ reader.get_origin_root(&alias_sub_example[1]).unwrap().as_str(),
+ format!("{root}/❤️💛💙/💝")
+ );
+
+ let build_rustflags = reader.get(["build", "rustflags"]).unwrap().as_array().unwrap();
+ assert_eq!(
+ reader.get_origin_root(&build_rustflags[0]).unwrap().as_str(),
+ format!("{root}/home")
+ );
+ assert!(reader.get_origin_root(&build_rustflags[1]).is_none());
+ assert!(reader.get_origin_root(&build_rustflags[2]).is_none());
+
+ let env_cargo_workspace_dir =
+ reader.get(["env", "CARGO_WORKSPACE_DIR"]).unwrap().as_table().unwrap();
+ let env_relative = &env_cargo_workspace_dir["relative"];
+ assert!(env_relative.as_ref().as_bool().unwrap());
+ assert_eq!(reader.get_origin_root(env_relative).unwrap().as_str(), format!("{root}/home"));
+
+ let env_val = &env_cargo_workspace_dir["value"];
+ assert_eq!(env_val.as_ref().as_str().unwrap(), "");
+ assert_eq!(reader.get_origin_root(env_val).unwrap().as_str(), format!("{root}/home"));
+}
diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs
index 8089155adf..51c447945c 100644
--- a/crates/project-model/src/env.rs
+++ b/crates/project-model/src/env.rs
@@ -3,7 +3,7 @@ use base_db::Env;
use paths::Utf8Path;
use rustc_hash::FxHashMap;
-use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile};
+use crate::{PackageData, TargetKind, cargo_config_file::CargoConfigFile};
/// Recreates the compile-time environment variables that Cargo sets.
///
@@ -61,46 +61,48 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
}
pub(crate) fn cargo_config_env(
- manifest: &ManifestPath,
config: &Option<CargoConfigFile>,
extra_env: &FxHashMap<String, Option<String>>,
) -> Env {
+ use toml::de::*;
+
let mut env = Env::default();
env.extend(extra_env.iter().filter_map(|(k, v)| v.as_ref().map(|v| (k.clone(), v.clone()))));
- let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
- else {
+ let Some(config_reader) = config.as_ref().and_then(|c| c.read()) else {
+ return env;
+ };
+ let Some(env_toml) = config_reader.get(["env"]).and_then(|it| it.as_table()) else {
return env;
};
- // FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
- // But cargo does not provide this information.
- let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
-
- for (key, entry) in env_json {
- let value = match entry {
- serde_json::Value::String(s) => s.clone(),
- serde_json::Value::Object(entry) => {
+ for (key, entry) in env_toml {
+ let key = key.as_ref().as_ref();
+ let value = match entry.as_ref() {
+ DeValue::String(s) => String::from(s.clone()),
+ DeValue::Table(entry) => {
// Each entry MUST have a `value` key.
- let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
+ let Some(map) = entry.get("value").and_then(|v| v.as_ref().as_str()) else {
continue;
};
// If the entry already exists in the environment AND the `force` key is not set to
// true, then don't overwrite the value.
if extra_env.get(key).is_some_and(Option::is_some)
- && !entry.get("force").and_then(|v| v.as_bool()).unwrap_or(false)
+ && !entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false)
{
continue;
}
- if entry
- .get("relative")
- .and_then(|v| v.as_bool())
- .is_some_and(std::convert::identity)
- {
- base.join(value).to_string()
+ if let Some(base) = entry.get("relative").and_then(|v| {
+ if v.as_ref().as_bool().is_some_and(std::convert::identity) {
+ config_reader.get_origin_root(v)
+ } else {
+ None
+ }
+ }) {
+ base.join(map).to_string()
} else {
- value.to_owned()
+ map.to_owned()
}
}
_ => continue,
@@ -114,43 +116,30 @@ pub(crate) fn cargo_config_env(
#[test]
fn parse_output_cargo_config_env_works() {
+ use itertools::Itertools;
+
+ let cwd = paths::AbsPathBuf::try_from(
+ paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap(),
+ )
+ .unwrap();
+ let config_path = cwd.join(".cargo").join("config.toml");
let raw = r#"
-{
- "env": {
- "CARGO_WORKSPACE_DIR": {
- "relative": true,
- "value": ""
- },
- "INVALID": {
- "relative": "invalidbool",
- "value": "../relative"
- },
- "RELATIVE": {
- "relative": true,
- "value": "../relative"
- },
- "TEST": {
- "value": "test"
- },
- "FORCED": {
- "value": "test",
- "force": true
- },
- "UNFORCED": {
- "value": "test",
- "force": false
- },
- "OVERWRITTEN": {
- "value": "test"
- },
- "NOT_AN_OBJECT": "value"
- }
-}
+env.CARGO_WORKSPACE_DIR.relative = true
+env.CARGO_WORKSPACE_DIR.value = ""
+env.INVALID.relative = "invalidbool"
+env.INVALID.value = "../relative"
+env.RELATIVE.relative = true
+env.RELATIVE.value = "../relative"
+env.TEST.value = "test"
+env.FORCED.value = "test"
+env.FORCED.force = true
+env.UNFORCED.value = "test"
+env.UNFORCED.forced = false
+env.OVERWRITTEN.value = "test"
+env.NOT_AN_OBJECT = "value"
"#;
- let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
- let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
- let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
- let manifest = ManifestPath::try_from(manifest).unwrap();
+ let raw = raw.lines().map(|l| format!("{l} # {config_path}")).join("\n");
+ let config = CargoConfigFile::from_string_for_test(raw);
let extra_env = [
("FORCED", Some("ignored")),
("UNFORCED", Some("newvalue")),
@@ -160,7 +149,7 @@ fn parse_output_cargo_config_env_works() {
.iter()
.map(|(k, v)| (k.to_string(), v.map(ToString::to_string)))
.collect();
- let env = cargo_config_env(&manifest, &Some(config), &extra_env);
+ let env = cargo_config_env(&Some(config), &extra_env);
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
diff --git a/crates/project-model/src/toolchain_info/target_tuple.rs b/crates/project-model/src/toolchain_info/target_tuple.rs
index 9f12ededb6..12c64b5928 100644
--- a/crates/project-model/src/toolchain_info/target_tuple.rs
+++ b/crates/project-model/src/toolchain_info/target_tuple.rs
@@ -53,7 +53,7 @@ fn rustc_discover_host_tuple(
}
fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
- match parse_json_cargo_config_build_target(config) {
+ match parse_toml_cargo_config_build_target(config) {
Ok(v) => v,
Err(e) => {
tracing::debug!("Failed to discover cargo config build target {e:?}");
@@ -63,18 +63,44 @@ fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
}
// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
-fn parse_json_cargo_config_build_target(
+fn parse_toml_cargo_config_build_target(
config: &CargoConfigFile,
) -> anyhow::Result<Option<Vec<String>>> {
- let target = config.get("build").and_then(|v| v.as_object()).and_then(|m| m.get("target"));
- match target {
- Some(serde_json::Value::String(s)) => Ok(Some(vec![s.to_owned()])),
- Some(v) => serde_json::from_value(v.clone())
- .map(Option::Some)
- .context("Failed to parse `build.target` as an array of target"),
- // t`error: config value `build.target` is not set`, in which case we
- // don't wanna log the error
- None => Ok(None),
+ let Some(config_reader) = config.read() else {
+ return Ok(None);
+ };
+ let Some(target) = config_reader.get_spanned(["build", "target"]) else {
+ return Ok(None);
+ };
+
+ // if the target ends with `.json`, join it to the config file's parent dir.
+ // See https://github.com/rust-lang/cargo/blob/f7acf448fc127df9a77c52cc2bba027790ac4931/src/cargo/core/compiler/compile_kind.rs#L171-L192
+ let join_to_origin_if_json_path = |s: &str, spanned: &toml::Spanned<toml::de::DeValue<'_>>| {
+ if s.ends_with(".json") {
+ config_reader
+ .get_origin_root(spanned)
+ .map(|p| p.join(s).to_string())
+ .unwrap_or_else(|| s.to_owned())
+ } else {
+ s.to_owned()
+ }
+ };
+
+ let parse_err = "Failed to parse `build.target` as an array of target";
+
+ match target.as_ref() {
+ toml::de::DeValue::String(s) => {
+ Ok(Some(vec![join_to_origin_if_json_path(s.as_ref(), target)]))
+ }
+ toml::de::DeValue::Array(arr) => arr
+ .iter()
+ .map(|v| {
+ let s = v.as_ref().as_str().context(parse_err)?;
+ Ok(join_to_origin_if_json_path(s, v))
+ })
+ .collect::<anyhow::Result<_>>()
+ .map(Option::Some),
+ _ => Err(anyhow::anyhow!(parse_err)),
}
}
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index f01daa82b6..4d56668cf2 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -27,7 +27,7 @@ use crate::{
ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
WorkspaceBuildScripts,
build_dependencies::{BuildScriptOutput, ProcMacroDylibPath},
- cargo_config_file,
+ cargo_config_file::CargoConfigFile,
cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
project_json::{Crate, CrateArrayIdx},
@@ -268,7 +268,7 @@ impl ProjectWorkspace {
tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
progress("querying project metadata".to_owned());
- let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
+ let config_file = CargoConfigFile::load(cargo_toml, extra_env, &sysroot);
let config_file_ = config_file.clone();
let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
let targets =
@@ -391,7 +391,6 @@ impl ProjectWorkspace {
sysroot.load_workspace(
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
config,
- workspace_dir,
&targets,
toolchain.clone(),
)),
@@ -402,9 +401,7 @@ impl ProjectWorkspace {
.expect("failed to spawn thread");
let cargo_env = Builder::new()
.name("ProjectWorkspace::cargo_env".to_owned())
- .spawn_scoped(s, move || {
- cargo_config_env(cargo_toml, &config_file, &config.extra_env)
- })
+ .spawn_scoped(s, move || cargo_config_env(&config_file, &config.extra_env))
.expect("failed to spawn thread");
thread::Result::Ok((
rustc_cfg.join()?,
@@ -503,7 +500,6 @@ impl ProjectWorkspace {
sysroot.load_workspace(
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
config,
- project_json.project_root(),
&targets,
toolchain.clone(),
)),
@@ -548,7 +544,7 @@ impl ProjectWorkspace {
None => Sysroot::empty(),
};
- let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
+ let config_file = CargoConfigFile::load(detached_file, &config.extra_env, &sysroot);
let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
@@ -559,7 +555,6 @@ impl ProjectWorkspace {
let loaded_sysroot = sysroot.load_workspace(
&RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
config,
- dir,
&targets,
toolchain.clone(),
)),
@@ -585,8 +580,7 @@ impl ProjectWorkspace {
config.no_deps,
);
let cargo_script = fetch_metadata.exec(false, &|_| ()).ok().map(|(ws, error)| {
- let cargo_config_extra_env =
- cargo_config_env(detached_file, &config_file, &config.extra_env);
+ let cargo_config_extra_env = cargo_config_env(&config_file, &config.extra_env);
(
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
WorkspaceBuildScripts::default(),
@@ -1897,29 +1891,12 @@ fn add_dep_inner(graph: &mut CrateGraphBuilder, from: CrateBuilderId, dep: Depen
fn sysroot_metadata_config(
config: &CargoConfig,
- current_dir: &AbsPath,
targets: &[String],
toolchain_version: Option<Version>,
) -> CargoMetadataConfig {
- // We run `cargo metadata` on sysroot with sysroot dir as a working directory, but still pass
- // the `targets` from the cargo config evaluated from the workspace's `current_dir`.
- // So, we need to *canonicalize* those *might-be-relative-paths-to-custom-target-json-files*.
- //
- // See https://github.com/rust-lang/cargo/blob/f7acf448fc127df9a77c52cc2bba027790ac4931/src/cargo/core/compiler/compile_kind.rs#L171-L192
- let targets = targets
- .iter()
- .map(|target| {
- if target.ends_with(".json") {
- current_dir.join(target).to_string()
- } else {
- target.to_owned()
- }
- })
- .collect();
-
CargoMetadataConfig {
features: Default::default(),
- targets,
+ targets: targets.to_vec(),
extra_args: Default::default(),
extra_env: config.extra_env.clone(),
toolchain_version,
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index b9dfe1fd01..782ec55614 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -47,7 +47,7 @@ tracing.workspace = true
tracing-subscriber.workspace = true
tracing-tree.workspace = true
triomphe.workspace = true
-toml = "0.8.23"
+toml.workspace = true
nohash-hasher.workspace = true
walkdir = "2.5.0"
semver.workspace = true