Skip to content

Commit cc66815

Browse files
committed
chore: Cleanup cargo config queries
1 parent 3816d0a commit cc66815

File tree

8 files changed

+151
-130
lines changed

8 files changed

+151
-130
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! Read `.cargo/config.toml` as a JSON object
2+
use rustc_hash::FxHashMap;
3+
use toolchain::Tool;
4+
5+
use crate::{ManifestPath, Sysroot, utf8_stdout};
6+
7+
pub(crate) type CargoConfigFile = serde_json::Map<String, serde_json::Value>;
8+
9+
pub(crate) fn read(
10+
manifest: &ManifestPath,
11+
extra_env: &FxHashMap<String, Option<String>>,
12+
sysroot: &Sysroot,
13+
) -> Option<CargoConfigFile> {
14+
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
15+
cargo_config
16+
.args(["-Z", "unstable-options", "config", "get", "--format", "json"])
17+
.env("RUSTC_BOOTSTRAP", "1");
18+
if manifest.is_rust_manifest() {
19+
cargo_config.arg("-Zscript");
20+
}
21+
22+
tracing::debug!("Discovering cargo config by {:?}", cargo_config);
23+
let json: serde_json::Map<String, serde_json::Value> = utf8_stdout(&mut cargo_config)
24+
.inspect(|json| {
25+
tracing::debug!("Discovered cargo config: {:?}", json);
26+
})
27+
.inspect_err(|err| {
28+
tracing::debug!("Failed to discover cargo config: {:?}", err);
29+
})
30+
.ok()
31+
.and_then(|stdout| serde_json::from_str(&stdout).ok())?;
32+
33+
Some(json)
34+
}

crates/project-model/src/env.rs

Lines changed: 51 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use paths::{Utf8Path, Utf8PathBuf};
44
use rustc_hash::FxHashMap;
55
use toolchain::Tool;
66

7-
use crate::{ManifestPath, PackageData, Sysroot, TargetKind, utf8_stdout};
7+
use crate::{
8+
ManifestPath, PackageData, Sysroot, TargetKind, cargo_config_file::CargoConfigFile, utf8_stdout,
9+
};
810

911
/// Recreates the compile-time environment variables that Cargo sets.
1012
///
@@ -61,104 +63,68 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
6163
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
6264
}
6365

64-
pub(crate) fn cargo_config_env(
65-
manifest: &ManifestPath,
66-
extra_env: &FxHashMap<String, Option<String>>,
67-
sysroot: &Sysroot,
68-
) -> Env {
69-
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
70-
cargo_config
71-
.args(["-Z", "unstable-options", "config", "get", "env"])
72-
.env("RUSTC_BOOTSTRAP", "1");
73-
if manifest.is_rust_manifest() {
74-
cargo_config.arg("-Zscript");
75-
}
76-
// if successful we receive `env.key.value = "value" per entry
77-
tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
78-
utf8_stdout(&mut cargo_config)
79-
.map(|stdout| parse_output_cargo_config_env(manifest, &stdout))
80-
.inspect(|env| {
81-
tracing::debug!("Discovered cargo config env: {:?}", env);
82-
})
83-
.inspect_err(|err| {
84-
tracing::debug!("Failed to discover cargo config env: {:?}", err);
85-
})
86-
.unwrap_or_default()
87-
}
88-
89-
fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env {
66+
pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoConfigFile>) -> Env {
9067
let mut env = Env::default();
91-
let mut relatives = vec![];
92-
for (key, val) in
93-
stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = "))
94-
{
95-
let val = val.trim_matches('"').to_owned();
96-
if let Some((key, modifier)) = key.split_once('.') {
97-
match modifier {
98-
"relative" => relatives.push((key, val)),
99-
"value" => _ = env.insert(key, val),
100-
_ => {
101-
tracing::warn!(
102-
"Unknown modifier in cargo config env: {}, expected `relative` or `value`",
103-
modifier
104-
);
105-
continue;
106-
}
107-
}
108-
} else {
109-
env.insert(key, val);
110-
}
111-
}
68+
let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
69+
else {
70+
return env;
71+
};
72+
11273
// FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
11374
// But cargo does not provide this information.
11475
let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
115-
for (key, relative) in relatives {
116-
if relative != "true" {
76+
77+
for (key, entry) in env_json {
78+
let serde_json::Value::Object(entry) = entry else {
11779
continue;
118-
}
119-
if let Some(suffix) = env.get(key) {
120-
env.insert(key, base.join(suffix).to_string());
121-
}
122-
}
123-
env
124-
}
80+
};
81+
let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
82+
continue;
83+
};
12584

126-
pub(crate) fn cargo_config_build_target_dir(
127-
manifest: &ManifestPath,
128-
extra_env: &FxHashMap<String, Option<String>>,
129-
sysroot: &Sysroot,
130-
) -> Option<Utf8PathBuf> {
131-
let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
132-
cargo_config
133-
.args(["-Z", "unstable-options", "config", "get", "build.target-dir"])
134-
.env("RUSTC_BOOTSTRAP", "1");
135-
if manifest.is_rust_manifest() {
136-
cargo_config.arg("-Zscript");
85+
let value = if entry
86+
.get("relative")
87+
.and_then(|v| v.as_bool())
88+
.is_some_and(std::convert::identity)
89+
{
90+
base.join(value).to_string()
91+
} else {
92+
value.to_owned()
93+
};
94+
env.insert(key, value);
13795
}
138-
utf8_stdout(&mut cargo_config)
139-
.map(|stdout| {
140-
Utf8Path::new(stdout.trim_start_matches("build.target-dir = ").trim_matches('"'))
141-
.to_owned()
142-
})
143-
.ok()
96+
97+
env
14498
}
14599

146100
#[test]
147101
fn parse_output_cargo_config_env_works() {
148-
let stdout = r#"
149-
env.CARGO_WORKSPACE_DIR.relative = true
150-
env.CARGO_WORKSPACE_DIR.value = ""
151-
env.RELATIVE.relative = true
152-
env.RELATIVE.value = "../relative"
153-
env.INVALID.relative = invalidbool
154-
env.INVALID.value = "../relative"
155-
env.TEST.value = "test"
156-
"#
157-
.trim();
102+
let raw = r#"
103+
{
104+
"env": {
105+
"CARGO_WORKSPACE_DIR": {
106+
"relative": true,
107+
"value": ""
108+
},
109+
"INVALID": {
110+
"relative": "invalidbool",
111+
"value": "../relative"
112+
},
113+
"RELATIVE": {
114+
"relative": true,
115+
"value": "../relative"
116+
},
117+
"TEST": {
118+
"value": "test"
119+
}
120+
}
121+
}
122+
"#;
123+
let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
158124
let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
159125
let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
160126
let manifest = ManifestPath::try_from(manifest).unwrap();
161-
let env = parse_output_cargo_config_env(&manifest, stdout);
127+
let env = cargo_config_env(&manifest, &Some(config));
162128
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
163129
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
164130
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));

crates/project-model/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,20 @@ pub mod toolchain_info {
2424

2525
use std::path::Path;
2626

27-
use crate::{ManifestPath, Sysroot};
27+
use crate::{ManifestPath, Sysroot, cargo_config_file::CargoConfigFile};
2828

2929
#[derive(Copy, Clone)]
3030
pub enum QueryConfig<'a> {
3131
/// Directly invoke `rustc` to query the desired information.
3232
Rustc(&'a Sysroot, &'a Path),
3333
/// Attempt to use cargo to query the desired information, honoring cargo configurations.
3434
/// If this fails, falls back to invoking `rustc` directly.
35-
Cargo(&'a Sysroot, &'a ManifestPath),
35+
Cargo(&'a Sysroot, &'a ManifestPath, &'a Option<CargoConfigFile>),
3636
}
3737
}
3838

3939
mod build_dependencies;
40+
mod cargo_config_file;
4041
mod cargo_workspace;
4142
mod env;
4243
mod manifest_path;

crates/project-model/src/toolchain_info/rustc_cfg.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fn rustc_print_cfg(
6363
) -> anyhow::Result<String> {
6464
const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
6565
let (sysroot, current_dir) = match config {
66-
QueryConfig::Cargo(sysroot, cargo_toml) => {
66+
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
6767
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
6868
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
6969
if let Some(target) = target {
@@ -109,7 +109,7 @@ mod tests {
109109
let sysroot = Sysroot::empty();
110110
let manifest_path =
111111
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
112-
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
112+
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
113113
assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
114114
}
115115

crates/project-model/src/toolchain_info/target_data_layout.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub fn get(
2020
})
2121
};
2222
let (sysroot, current_dir) = match config {
23-
QueryConfig::Cargo(sysroot, cargo_toml) => {
23+
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
2424
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
2525
cmd.env("RUSTC_BOOTSTRAP", "1");
2626
cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
@@ -66,7 +66,7 @@ mod tests {
6666
let sysroot = Sysroot::empty();
6767
let manifest_path =
6868
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
69-
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
69+
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
7070
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
7171
}
7272

crates/project-model/src/toolchain_info/target_tuple.rs

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use anyhow::Context;
55
use rustc_hash::FxHashMap;
66
use toolchain::Tool;
77

8-
use crate::{ManifestPath, Sysroot, toolchain_info::QueryConfig, utf8_stdout};
8+
use crate::{
9+
Sysroot, cargo_config_file::CargoConfigFile, toolchain_info::QueryConfig, utf8_stdout,
10+
};
911

1012
/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
1113
/// For rustc, runs `rustc --print -vV` to get the host target.
@@ -20,8 +22,8 @@ pub fn get(
2022
}
2123

2224
let (sysroot, current_dir) = match config {
23-
QueryConfig::Cargo(sysroot, cargo_toml) => {
24-
match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
25+
QueryConfig::Cargo(sysroot, cargo_toml, config_file) => {
26+
match config_file.as_ref().and_then(cargo_config_build_target) {
2527
Some(it) => return Ok(it),
2628
None => (sysroot, cargo_toml.parent().as_ref()),
2729
}
@@ -50,30 +52,30 @@ fn rustc_discover_host_tuple(
5052
}
5153
}
5254

53-
fn cargo_config_build_target(
54-
cargo_toml: &ManifestPath,
55-
extra_env: &FxHashMap<String, Option<String>>,
56-
sysroot: &Sysroot,
57-
) -> Option<Vec<String>> {
58-
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
59-
cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1");
60-
cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]);
61-
// if successful we receive `build.target = "target-tuple"`
62-
// or `build.target = ["<target 1>", ..]`
63-
// this might be `error: config value `build.target` is not set` in which case we
64-
// don't wanna log the error
65-
utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok()
55+
fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
56+
match parse_json_cargo_config_build_target(config) {
57+
Ok(v) => v,
58+
Err(e) => {
59+
tracing::debug!("Failed to discover cargo config build target {e:?}");
60+
None
61+
}
62+
}
6663
}
6764

6865
// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
69-
fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
70-
let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
71-
72-
if !trimmed.starts_with('[') {
73-
return Ok([trimmed.to_owned()].to_vec());
66+
fn parse_json_cargo_config_build_target(
67+
config: &CargoConfigFile,
68+
) -> anyhow::Result<Option<Vec<String>>> {
69+
let target = config.get("build").and_then(|v| v.as_object()).and_then(|m| m.get("target"));
70+
match target {
71+
Some(serde_json::Value::String(s)) => Ok(Some(vec![s.to_owned()])),
72+
Some(v) => serde_json::from_value(v.clone())
73+
.map(Option::Some)
74+
.context("Failed to parse `build.target` as an array of target"),
75+
// t`error: config value `build.target` is not set`, in which case we
76+
// don't wanna log the error
77+
None => Ok(None),
7478
}
75-
76-
serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
7779
}
7880

7981
#[cfg(test)]
@@ -90,7 +92,7 @@ mod tests {
9092
let sysroot = Sysroot::empty();
9193
let manifest_path =
9294
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
93-
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
95+
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
9496
assert!(get(cfg, None, &FxHashMap::default()).is_ok());
9597
}
9698

crates/project-model/src/toolchain_info/version.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub(crate) fn get(
1212
extra_env: &FxHashMap<String, Option<String>>,
1313
) -> Result<Option<Version>, anyhow::Error> {
1414
let (mut cmd, prefix) = match config {
15-
QueryConfig::Cargo(sysroot, cargo_toml) => {
15+
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
1616
(sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env), "cargo ")
1717
}
1818
QueryConfig::Rustc(sysroot, current_dir) => {
@@ -44,7 +44,7 @@ mod tests {
4444
let sysroot = Sysroot::empty();
4545
let manifest_path =
4646
ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
47-
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
47+
let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
4848
assert!(get(cfg, &FxHashMap::default()).is_ok());
4949
}
5050

0 commit comments

Comments
 (0)