Skip to content

Commit 11bf2e7

Browse files
committed
Auto merge of rust-lang#13058 - dpaoliello:extraenv, r=Veykril
Add a new configuration settings to set env vars when running cargo, rustc, etc. commands: cargo.extraEnv and checkOnSave.extraEnv It can be extremely useful to be able to set environment variables when rust-analyzer is running various cargo or rustc commands (such as `cargo check`, `cargo --print cfg` or `cargo metadata`): users may want to set custom `RUSTFLAGS`, change `PATH` to use a custom toolchain or set a different `CARGO_HOME`. There is the existing `server.extraEnv` setting that allows env vars to be set when the rust-analyzer server is launched, but using this as the recommended mechanism to also configure cargo/rust has some drawbacks: - It convolutes configuring the rust-analyzer server with configuring cargo/rustc (one may want to change the `PATH` for cargo/rustc without affecting the rust-analyzer server). - The name `server.extraEnv` doesn't indicate that cargo/rustc will be affected but renaming it to `cargo.extraEnv` doesn't indicate that the rust-analyzer server would be affected. - To make the setting useful, it needs to be dynamically reloaded without requiring that the entire extension is reloaded. It might be possible to do this, but it would require the client communicating to the server what the overwritten env vars were at first launch, which isn't easy to do. This change adds two new configuration settings: `cargo.extraEnv` and `checkOnSave.extraEnv` that can be used to change the environment for the rust-analyzer server after launch (thus affecting any process that rust-analyzer invokes) and the `cargo check` command respectively. `cargo.extraEnv` supports dynamic changes by keeping track of the pre-change values of environment variables, thus it can undo changes made previously before applying the new configuration (and then requesting a workspace reload).
2 parents 932e63b + c407cc5 commit 11bf2e7

18 files changed

+155
-43
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/flycheck/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ doctest = false
1313
crossbeam-channel = "0.5.5"
1414
tracing = "0.1.35"
1515
cargo_metadata = "0.15.0"
16+
rustc-hash = "1.1.0"
1617
serde = { version = "1.0.137", features = ["derive"] }
1718
serde_json = "1.0.81"
1819
jod-thread = "0.1.2"

crates/flycheck/src/lib.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::{
1212

1313
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
1414
use paths::AbsPathBuf;
15+
use rustc_hash::FxHashMap;
1516
use serde::Deserialize;
1617
use stdx::{process::streaming_output, JodChild};
1718

@@ -30,18 +31,20 @@ pub enum FlycheckConfig {
3031
all_features: bool,
3132
features: Vec<String>,
3233
extra_args: Vec<String>,
34+
extra_env: FxHashMap<String, String>,
3335
},
3436
CustomCommand {
3537
command: String,
3638
args: Vec<String>,
39+
extra_env: FxHashMap<String, String>,
3740
},
3841
}
3942

4043
impl fmt::Display for FlycheckConfig {
4144
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4245
match self {
4346
FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
44-
FlycheckConfig::CustomCommand { command, args } => {
47+
FlycheckConfig::CustomCommand { command, args, .. } => {
4548
write!(f, "{} {}", command, args.join(" "))
4649
}
4750
}
@@ -256,6 +259,7 @@ impl FlycheckActor {
256259
all_features,
257260
extra_args,
258261
features,
262+
extra_env,
259263
} => {
260264
let mut cmd = Command::new(toolchain::cargo());
261265
cmd.arg(command);
@@ -281,11 +285,13 @@ impl FlycheckActor {
281285
}
282286
}
283287
cmd.args(extra_args);
288+
cmd.envs(extra_env);
284289
cmd
285290
}
286-
FlycheckConfig::CustomCommand { command, args } => {
291+
FlycheckConfig::CustomCommand { command, args, extra_env } => {
287292
let mut cmd = Command::new(command);
288293
cmd.args(args);
294+
cmd.envs(extra_env);
289295
cmd
290296
}
291297
};

crates/project-model/src/build_scripts.rs

+2
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ impl WorkspaceBuildScripts {
4343
if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
4444
let mut cmd = Command::new(program);
4545
cmd.args(args);
46+
cmd.envs(&config.extra_env);
4647
return cmd;
4748
}
4849

4950
let mut cmd = Command::new(toolchain::cargo());
51+
cmd.envs(&config.extra_env);
5052

5153
cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
5254

crates/project-model/src/cargo_workspace.rs

+30-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::iter;
44
use std::path::PathBuf;
5+
use std::str::from_utf8;
56
use std::{ops, process::Command};
67

78
use anyhow::{Context, Result};
@@ -98,6 +99,8 @@ pub struct CargoConfig {
9899
pub wrap_rustc_in_build_scripts: bool,
99100

100101
pub run_build_script_command: Option<Vec<String>>,
102+
103+
pub extra_env: FxHashMap<String, String>,
101104
}
102105

103106
impl CargoConfig {
@@ -263,8 +266,8 @@ impl CargoWorkspace {
263266
let target = config
264267
.target
265268
.clone()
266-
.or_else(|| cargo_config_build_target(cargo_toml))
267-
.or_else(|| rustc_discover_host_triple(cargo_toml));
269+
.or_else(|| cargo_config_build_target(cargo_toml, config))
270+
.or_else(|| rustc_discover_host_triple(cargo_toml, config));
268271

269272
let mut meta = MetadataCommand::new();
270273
meta.cargo_path(toolchain::cargo());
@@ -292,8 +295,27 @@ impl CargoWorkspace {
292295
// unclear whether cargo itself supports it.
293296
progress("metadata".to_string());
294297

295-
let meta =
296-
meta.exec().with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?;
298+
fn exec_with_env(
299+
command: &cargo_metadata::MetadataCommand,
300+
extra_env: &FxHashMap<String, String>,
301+
) -> Result<cargo_metadata::Metadata, cargo_metadata::Error> {
302+
let mut command = command.cargo_command();
303+
command.envs(extra_env);
304+
let output = command.output()?;
305+
if !output.status.success() {
306+
return Err(cargo_metadata::Error::CargoMetadata {
307+
stderr: String::from_utf8(output.stderr)?,
308+
});
309+
}
310+
let stdout = from_utf8(&output.stdout)?
311+
.lines()
312+
.find(|line| line.starts_with('{'))
313+
.ok_or(cargo_metadata::Error::NoJson)?;
314+
cargo_metadata::MetadataCommand::parse(stdout)
315+
}
316+
317+
let meta = exec_with_env(&meta, &config.extra_env)
318+
.with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?;
297319

298320
Ok(meta)
299321
}
@@ -463,8 +485,9 @@ impl CargoWorkspace {
463485
}
464486
}
465487

466-
fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
488+
fn rustc_discover_host_triple(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<String> {
467489
let mut rustc = Command::new(toolchain::rustc());
490+
rustc.envs(&config.extra_env);
468491
rustc.current_dir(cargo_toml.parent()).arg("-vV");
469492
tracing::debug!("Discovering host platform by {:?}", rustc);
470493
match utf8_stdout(rustc) {
@@ -486,8 +509,9 @@ fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
486509
}
487510
}
488511

489-
fn cargo_config_build_target(cargo_toml: &ManifestPath) -> Option<String> {
512+
fn cargo_config_build_target(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<String> {
490513
let mut cargo_config = Command::new(toolchain::cargo());
514+
cargo_config.envs(&config.extra_env);
491515
cargo_config
492516
.current_dir(cargo_toml.parent())
493517
.args(&["-Z", "unstable-options", "config", "get", "build.target"])

crates/project-model/src/rustc_cfg.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ use std::process::Command;
44

55
use anyhow::Result;
66

7-
use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath};
7+
use crate::{cfg_flag::CfgFlag, utf8_stdout, CargoConfig, ManifestPath};
88

9-
pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Vec<CfgFlag> {
9+
pub(crate) fn get(
10+
cargo_toml: Option<&ManifestPath>,
11+
target: Option<&str>,
12+
config: &CargoConfig,
13+
) -> Vec<CfgFlag> {
1014
let _p = profile::span("rustc_cfg::get");
1115
let mut res = Vec::with_capacity(6 * 2 + 1);
1216

@@ -18,7 +22,7 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
1822
}
1923
}
2024

21-
match get_rust_cfgs(cargo_toml, target) {
25+
match get_rust_cfgs(cargo_toml, target, config) {
2226
Ok(rustc_cfgs) => {
2327
tracing::debug!(
2428
"rustc cfgs found: {:?}",
@@ -35,9 +39,14 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
3539
res
3640
}
3741

38-
fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Result<String> {
42+
fn get_rust_cfgs(
43+
cargo_toml: Option<&ManifestPath>,
44+
target: Option<&str>,
45+
config: &CargoConfig,
46+
) -> Result<String> {
3947
if let Some(cargo_toml) = cargo_toml {
4048
let mut cargo_config = Command::new(toolchain::cargo());
49+
cargo_config.envs(&config.extra_env);
4150
cargo_config
4251
.current_dir(cargo_toml.parent())
4352
.args(&["-Z", "unstable-options", "rustc", "--print", "cfg"])
@@ -52,6 +61,7 @@ fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Res
5261
}
5362
// using unstable cargo features failed, fall back to using plain rustc
5463
let mut cmd = Command::new(toolchain::rustc());
64+
cmd.envs(&config.extra_env);
5565
cmd.args(&["--print", "cfg", "-O"]);
5666
if let Some(target) = target {
5767
cmd.args(&["--target", target]);

crates/project-model/src/sysroot.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use anyhow::{format_err, Result};
1010
use la_arena::{Arena, Idx};
1111
use paths::{AbsPath, AbsPathBuf};
1212

13-
use crate::{utf8_stdout, ManifestPath};
13+
use crate::{utf8_stdout, CargoConfig, ManifestPath};
1414

1515
#[derive(Debug, Clone, Eq, PartialEq)]
1616
pub struct Sysroot {
@@ -67,18 +67,20 @@ impl Sysroot {
6767
self.crates.iter().map(|(id, _data)| id)
6868
}
6969

70-
pub fn discover(dir: &AbsPath) -> Result<Sysroot> {
70+
pub fn discover(dir: &AbsPath, config: &CargoConfig) -> Result<Sysroot> {
7171
tracing::debug!("Discovering sysroot for {}", dir.display());
72-
let sysroot_dir = discover_sysroot_dir(dir)?;
73-
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?;
72+
let sysroot_dir = discover_sysroot_dir(dir, config)?;
73+
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir, config)?;
7474
let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?;
7575
Ok(res)
7676
}
7777

78-
pub fn discover_rustc(cargo_toml: &ManifestPath) -> Option<ManifestPath> {
78+
pub fn discover_rustc(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<ManifestPath> {
7979
tracing::debug!("Discovering rustc source for {}", cargo_toml.display());
8080
let current_dir = cargo_toml.parent();
81-
discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
81+
discover_sysroot_dir(current_dir, config)
82+
.ok()
83+
.and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
8284
}
8385

8486
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
@@ -144,8 +146,9 @@ impl Sysroot {
144146
}
145147
}
146148

147-
fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
149+
fn discover_sysroot_dir(current_dir: &AbsPath, config: &CargoConfig) -> Result<AbsPathBuf> {
148150
let mut rustc = Command::new(toolchain::rustc());
151+
rustc.envs(&config.extra_env);
149152
rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
150153
tracing::debug!("Discovering sysroot by {:?}", rustc);
151154
let stdout = utf8_stdout(rustc)?;
@@ -155,6 +158,7 @@ fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
155158
fn discover_sysroot_src_dir(
156159
sysroot_path: &AbsPathBuf,
157160
current_dir: &AbsPath,
161+
config: &CargoConfig,
158162
) -> Result<AbsPathBuf> {
159163
if let Ok(path) = env::var("RUST_SRC_PATH") {
160164
let path = AbsPathBuf::try_from(path.as_str())
@@ -170,6 +174,7 @@ fn discover_sysroot_src_dir(
170174
get_rust_src(sysroot_path)
171175
.or_else(|| {
172176
let mut rustup = Command::new(toolchain::rustup());
177+
rustup.envs(&config.extra_env);
173178
rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
174179
utf8_stdout(rustup).ok()?;
175180
get_rust_src(sysroot_path)

crates/project-model/src/tests.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use paths::{AbsPath, AbsPathBuf};
1010
use serde::de::DeserializeOwned;
1111

1212
use crate::{
13-
CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
14-
WorkspaceBuildScripts,
13+
CargoConfig, CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace,
14+
Sysroot, WorkspaceBuildScripts,
1515
};
1616

1717
fn load_cargo(file: &str) -> CrateGraph {
@@ -92,13 +92,17 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
9292
}
9393

9494
fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
95-
project_workspace.to_crate_graph(&mut |_, _| Ok(Vec::new()), &mut {
96-
let mut counter = 0;
97-
move |_path| {
98-
counter += 1;
99-
Some(FileId(counter))
100-
}
101-
})
95+
project_workspace.to_crate_graph(
96+
&mut |_, _| Ok(Vec::new()),
97+
&mut {
98+
let mut counter = 0;
99+
move |_path| {
100+
counter += 1;
101+
Some(FileId(counter))
102+
}
103+
},
104+
&CargoConfig::default(),
105+
)
102106
}
103107

104108
fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) {

0 commit comments

Comments
 (0)