Skip to content

Commit 40cd34d

Browse files
Merge pull request #20554 from dpaoliello/extraenv
Improvements for resolving the value of the `env!` macro
2 parents 57875bd + 554381f commit 40cd34d

File tree

3 files changed

+91
-39
lines changed

3 files changed

+91
-39
lines changed

crates/project-model/src/cargo_workspace.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ pub struct CargoWorkspace {
4949
is_virtual_workspace: bool,
5050
/// Whether this workspace represents the sysroot workspace.
5151
is_sysroot: bool,
52-
/// Environment variables set in the `.cargo/config` file.
53-
config_env: Env,
52+
/// Environment variables set in the `.cargo/config` file and the extraEnv
53+
/// configuration option.
54+
env: Env,
5455
requires_rustc_private: bool,
5556
}
5657

@@ -325,7 +326,7 @@ impl CargoWorkspace {
325326
pub fn new(
326327
mut meta: cargo_metadata::Metadata,
327328
ws_manifest_path: ManifestPath,
328-
cargo_config_env: Env,
329+
cargo_env: Env,
329330
is_sysroot: bool,
330331
) -> CargoWorkspace {
331332
let mut pkg_by_id = FxHashMap::default();
@@ -498,7 +499,7 @@ impl CargoWorkspace {
498499
is_virtual_workspace,
499500
requires_rustc_private,
500501
is_sysroot,
501-
config_env: cargo_config_env,
502+
env: cargo_env,
502503
}
503504
}
504505

@@ -589,7 +590,7 @@ impl CargoWorkspace {
589590
}
590591

591592
pub fn env(&self) -> &Env {
592-
&self.config_env
593+
&self.env
593594
}
594595

595596
pub fn is_sysroot(&self) -> bool {

crates/project-model/src/env.rs

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Cargo-like environment variables injection.
22
use base_db::Env;
33
use paths::Utf8Path;
4+
use rustc_hash::FxHashMap;
45
use toolchain::Tool;
56

67
use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile};
@@ -60,8 +61,14 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
6061
env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
6162
}
6263

63-
pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoConfigFile>) -> Env {
64+
pub(crate) fn cargo_config_env(
65+
manifest: &ManifestPath,
66+
config: &Option<CargoConfigFile>,
67+
extra_env: &FxHashMap<String, Option<String>>,
68+
) -> Env {
6469
let mut env = Env::default();
70+
env.extend(extra_env.iter().filter_map(|(k, v)| v.as_ref().map(|v| (k.clone(), v.clone()))));
71+
6572
let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
6673
else {
6774
return env;
@@ -72,22 +79,34 @@ pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoCon
7279
let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
7380

7481
for (key, entry) in env_json {
75-
let serde_json::Value::Object(entry) = entry else {
76-
continue;
77-
};
78-
let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
79-
continue;
80-
};
82+
let value = match entry {
83+
serde_json::Value::String(s) => s.clone(),
84+
serde_json::Value::Object(entry) => {
85+
// Each entry MUST have a `value` key.
86+
let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
87+
continue;
88+
};
89+
// If the entry already exists in the environment AND the `force` key is not set to
90+
// true, then don't overwrite the value.
91+
if extra_env.get(key).is_some_and(Option::is_some)
92+
&& !entry.get("force").and_then(|v| v.as_bool()).unwrap_or(false)
93+
{
94+
continue;
95+
}
8196

82-
let value = if entry
83-
.get("relative")
84-
.and_then(|v| v.as_bool())
85-
.is_some_and(std::convert::identity)
86-
{
87-
base.join(value).to_string()
88-
} else {
89-
value.to_owned()
97+
if entry
98+
.get("relative")
99+
.and_then(|v| v.as_bool())
100+
.is_some_and(std::convert::identity)
101+
{
102+
base.join(value).to_string()
103+
} else {
104+
value.to_owned()
105+
}
106+
}
107+
_ => continue,
90108
};
109+
91110
env.insert(key, value);
92111
}
93112

@@ -113,17 +132,42 @@ fn parse_output_cargo_config_env_works() {
113132
},
114133
"TEST": {
115134
"value": "test"
116-
}
135+
},
136+
"FORCED": {
137+
"value": "test",
138+
"force": true
139+
},
140+
"UNFORCED": {
141+
"value": "test",
142+
"force": false
143+
},
144+
"OVERWRITTEN": {
145+
"value": "test"
146+
},
147+
"NOT_AN_OBJECT": "value"
117148
}
118149
}
119150
"#;
120151
let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
121152
let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
122153
let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
123154
let manifest = ManifestPath::try_from(manifest).unwrap();
124-
let env = cargo_config_env(&manifest, &Some(config));
155+
let extra_env = [
156+
("FORCED", Some("ignored")),
157+
("UNFORCED", Some("newvalue")),
158+
("OVERWRITTEN", Some("newvalue")),
159+
("TEST", None),
160+
]
161+
.iter()
162+
.map(|(k, v)| (k.to_string(), v.map(ToString::to_string)))
163+
.collect();
164+
let env = cargo_config_env(&manifest, &Some(config), &extra_env);
125165
assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
126166
assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
127167
assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
128168
assert_eq!(env.get("TEST").as_deref(), Some("test"));
169+
assert_eq!(env.get("FORCED").as_deref(), Some("test"));
170+
assert_eq!(env.get("UNFORCED").as_deref(), Some("newvalue"));
171+
assert_eq!(env.get("OVERWRITTEN").as_deref(), Some("newvalue"));
172+
assert_eq!(env.get("NOT_AN_OBJECT").as_deref(), Some("value"));
129173
}

crates/project-model/src/workspace.rs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -387,36 +387,36 @@ impl ProjectWorkspace {
387387
progress,
388388
)
389389
});
390-
let cargo_config_extra_env =
391-
s.spawn(move || cargo_config_env(cargo_toml, &config_file));
390+
let cargo_env =
391+
s.spawn(move || cargo_config_env(cargo_toml, &config_file, &config.extra_env));
392392
thread::Result::Ok((
393393
rustc_cfg.join()?,
394394
target_data.join()?,
395395
rustc_dir.join()?,
396396
loaded_sysroot.join()?,
397397
cargo_metadata.join()?,
398-
cargo_config_extra_env.join()?,
398+
cargo_env.join()?,
399399
))
400400
});
401401

402-
let (
403-
rustc_cfg,
404-
data_layout,
405-
mut rustc,
406-
loaded_sysroot,
407-
cargo_metadata,
408-
cargo_config_extra_env,
409-
) = match join {
410-
Ok(it) => it,
411-
Err(e) => std::panic::resume_unwind(e),
412-
};
402+
let (rustc_cfg, data_layout, mut rustc, loaded_sysroot, cargo_metadata, mut cargo_env) =
403+
match join {
404+
Ok(it) => it,
405+
Err(e) => std::panic::resume_unwind(e),
406+
};
407+
408+
for (key, value) in config.extra_env.iter() {
409+
if let Some(value) = value {
410+
cargo_env.insert(key.clone(), value.clone());
411+
}
412+
}
413413

414414
let (meta, error) = cargo_metadata.with_context(|| {
415415
format!(
416416
"Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
417417
)
418418
})?;
419-
let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env, false);
419+
let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_env, false);
420420
if let Some(loaded_sysroot) = loaded_sysroot {
421421
tracing::info!(src_root = ?sysroot.rust_lib_src_root(), root = %loaded_sysroot, "Loaded sysroot");
422422
sysroot.set_workspace(loaded_sysroot);
@@ -586,7 +586,8 @@ impl ProjectWorkspace {
586586
.unwrap_or_else(|| dir.join("target").into());
587587
let cargo_script =
588588
fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| {
589-
let cargo_config_extra_env = cargo_config_env(detached_file, &config_file);
589+
let cargo_config_extra_env =
590+
cargo_config_env(detached_file, &config_file, &config.extra_env);
590591
(
591592
CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
592593
WorkspaceBuildScripts::default(),
@@ -1089,7 +1090,13 @@ fn project_json_to_crate_graph(
10891090
},
10901091
file_id,
10911092
)| {
1092-
let env = env.clone().into_iter().collect();
1093+
let mut env = env.clone().into_iter().collect::<Env>();
1094+
// Override existing env vars with those from `extra_env`
1095+
env.extend(
1096+
extra_env
1097+
.iter()
1098+
.filter_map(|(k, v)| v.as_ref().map(|v| (k.clone(), v.clone()))),
1099+
);
10931100

10941101
let target_cfgs = match target.as_deref() {
10951102
Some(target) => cfg_cache.entry(target).or_insert_with(|| {

0 commit comments

Comments
 (0)