Auto merge of #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).
This commit is contained in:
bors 2022-09-18 17:43:46 +00:00
commit 11bf2e7ffb
18 changed files with 155 additions and 43 deletions

1
Cargo.lock generated
View File

@ -394,6 +394,7 @@ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"jod-thread", "jod-thread",
"paths", "paths",
"rustc-hash",
"serde", "serde",
"serde_json", "serde_json",
"stdx", "stdx",

View File

@ -13,6 +13,7 @@ doctest = false
crossbeam-channel = "0.5.5" crossbeam-channel = "0.5.5"
tracing = "0.1.35" tracing = "0.1.35"
cargo_metadata = "0.15.0" cargo_metadata = "0.15.0"
rustc-hash = "1.1.0"
serde = { version = "1.0.137", features = ["derive"] } serde = { version = "1.0.137", features = ["derive"] }
serde_json = "1.0.81" serde_json = "1.0.81"
jod-thread = "0.1.2" jod-thread = "0.1.2"

View File

@ -12,6 +12,7 @@ use std::{
use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use paths::AbsPathBuf; use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize; use serde::Deserialize;
use stdx::{process::streaming_output, JodChild}; use stdx::{process::streaming_output, JodChild};
@ -30,10 +31,12 @@ pub enum FlycheckConfig {
all_features: bool, all_features: bool,
features: Vec<String>, features: Vec<String>,
extra_args: Vec<String>, extra_args: Vec<String>,
extra_env: FxHashMap<String, String>,
}, },
CustomCommand { CustomCommand {
command: String, command: String,
args: Vec<String>, args: Vec<String>,
extra_env: FxHashMap<String, String>,
}, },
} }
@ -41,7 +44,7 @@ impl fmt::Display for FlycheckConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command), FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
FlycheckConfig::CustomCommand { command, args } => { FlycheckConfig::CustomCommand { command, args, .. } => {
write!(f, "{} {}", command, args.join(" ")) write!(f, "{} {}", command, args.join(" "))
} }
} }
@ -256,6 +259,7 @@ impl FlycheckActor {
all_features, all_features,
extra_args, extra_args,
features, features,
extra_env,
} => { } => {
let mut cmd = Command::new(toolchain::cargo()); let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command); cmd.arg(command);
@ -281,11 +285,13 @@ impl FlycheckActor {
} }
} }
cmd.args(extra_args); cmd.args(extra_args);
cmd.envs(extra_env);
cmd cmd
} }
FlycheckConfig::CustomCommand { command, args } => { FlycheckConfig::CustomCommand { command, args, extra_env } => {
let mut cmd = Command::new(command); let mut cmd = Command::new(command);
cmd.args(args); cmd.args(args);
cmd.envs(extra_env);
cmd cmd
} }
}; };

View File

@ -43,10 +43,12 @@ impl WorkspaceBuildScripts {
if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() { if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
let mut cmd = Command::new(program); let mut cmd = Command::new(program);
cmd.args(args); cmd.args(args);
cmd.envs(&config.extra_env);
return cmd; return cmd;
} }
let mut cmd = Command::new(toolchain::cargo()); let mut cmd = Command::new(toolchain::cargo());
cmd.envs(&config.extra_env);
cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]); cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

View File

@ -2,6 +2,7 @@
use std::iter; use std::iter;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::from_utf8;
use std::{ops, process::Command}; use std::{ops, process::Command};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -98,6 +99,8 @@ pub struct CargoConfig {
pub wrap_rustc_in_build_scripts: bool, pub wrap_rustc_in_build_scripts: bool,
pub run_build_script_command: Option<Vec<String>>, pub run_build_script_command: Option<Vec<String>>,
pub extra_env: FxHashMap<String, String>,
} }
impl CargoConfig { impl CargoConfig {
@ -263,8 +266,8 @@ impl CargoWorkspace {
let target = config let target = config
.target .target
.clone() .clone()
.or_else(|| cargo_config_build_target(cargo_toml)) .or_else(|| cargo_config_build_target(cargo_toml, config))
.or_else(|| rustc_discover_host_triple(cargo_toml)); .or_else(|| rustc_discover_host_triple(cargo_toml, config));
let mut meta = MetadataCommand::new(); let mut meta = MetadataCommand::new();
meta.cargo_path(toolchain::cargo()); meta.cargo_path(toolchain::cargo());
@ -292,8 +295,27 @@ impl CargoWorkspace {
// unclear whether cargo itself supports it. // unclear whether cargo itself supports it.
progress("metadata".to_string()); progress("metadata".to_string());
let meta = fn exec_with_env(
meta.exec().with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?; command: &cargo_metadata::MetadataCommand,
extra_env: &FxHashMap<String, String>,
) -> Result<cargo_metadata::Metadata, cargo_metadata::Error> {
let mut command = command.cargo_command();
command.envs(extra_env);
let output = command.output()?;
if !output.status.success() {
return Err(cargo_metadata::Error::CargoMetadata {
stderr: String::from_utf8(output.stderr)?,
});
}
let stdout = from_utf8(&output.stdout)?
.lines()
.find(|line| line.starts_with('{'))
.ok_or(cargo_metadata::Error::NoJson)?;
cargo_metadata::MetadataCommand::parse(stdout)
}
let meta = exec_with_env(&meta, &config.extra_env)
.with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?;
Ok(meta) Ok(meta)
} }
@ -463,8 +485,9 @@ impl CargoWorkspace {
} }
} }
fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> { fn rustc_discover_host_triple(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<String> {
let mut rustc = Command::new(toolchain::rustc()); let mut rustc = Command::new(toolchain::rustc());
rustc.envs(&config.extra_env);
rustc.current_dir(cargo_toml.parent()).arg("-vV"); rustc.current_dir(cargo_toml.parent()).arg("-vV");
tracing::debug!("Discovering host platform by {:?}", rustc); tracing::debug!("Discovering host platform by {:?}", rustc);
match utf8_stdout(rustc) { match utf8_stdout(rustc) {
@ -486,8 +509,9 @@ fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
} }
} }
fn cargo_config_build_target(cargo_toml: &ManifestPath) -> Option<String> { fn cargo_config_build_target(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<String> {
let mut cargo_config = Command::new(toolchain::cargo()); let mut cargo_config = Command::new(toolchain::cargo());
cargo_config.envs(&config.extra_env);
cargo_config cargo_config
.current_dir(cargo_toml.parent()) .current_dir(cargo_toml.parent())
.args(&["-Z", "unstable-options", "config", "get", "build.target"]) .args(&["-Z", "unstable-options", "config", "get", "build.target"])

View File

@ -4,9 +4,13 @@ use std::process::Command;
use anyhow::Result; use anyhow::Result;
use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath}; use crate::{cfg_flag::CfgFlag, utf8_stdout, CargoConfig, ManifestPath};
pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Vec<CfgFlag> { pub(crate) fn get(
cargo_toml: Option<&ManifestPath>,
target: Option<&str>,
config: &CargoConfig,
) -> Vec<CfgFlag> {
let _p = profile::span("rustc_cfg::get"); let _p = profile::span("rustc_cfg::get");
let mut res = Vec::with_capacity(6 * 2 + 1); let mut res = Vec::with_capacity(6 * 2 + 1);
@ -18,7 +22,7 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
} }
} }
match get_rust_cfgs(cargo_toml, target) { match get_rust_cfgs(cargo_toml, target, config) {
Ok(rustc_cfgs) => { Ok(rustc_cfgs) => {
tracing::debug!( tracing::debug!(
"rustc cfgs found: {:?}", "rustc cfgs found: {:?}",
@ -35,9 +39,14 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
res res
} }
fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Result<String> { fn get_rust_cfgs(
cargo_toml: Option<&ManifestPath>,
target: Option<&str>,
config: &CargoConfig,
) -> Result<String> {
if let Some(cargo_toml) = cargo_toml { if let Some(cargo_toml) = cargo_toml {
let mut cargo_config = Command::new(toolchain::cargo()); let mut cargo_config = Command::new(toolchain::cargo());
cargo_config.envs(&config.extra_env);
cargo_config cargo_config
.current_dir(cargo_toml.parent()) .current_dir(cargo_toml.parent())
.args(&["-Z", "unstable-options", "rustc", "--print", "cfg"]) .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"])
@ -52,6 +61,7 @@ fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Res
} }
// using unstable cargo features failed, fall back to using plain rustc // using unstable cargo features failed, fall back to using plain rustc
let mut cmd = Command::new(toolchain::rustc()); let mut cmd = Command::new(toolchain::rustc());
cmd.envs(&config.extra_env);
cmd.args(&["--print", "cfg", "-O"]); cmd.args(&["--print", "cfg", "-O"]);
if let Some(target) = target { if let Some(target) = target {
cmd.args(&["--target", target]); cmd.args(&["--target", target]);

View File

@ -10,7 +10,7 @@ use anyhow::{format_err, Result};
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf}; use paths::{AbsPath, AbsPathBuf};
use crate::{utf8_stdout, ManifestPath}; use crate::{utf8_stdout, CargoConfig, ManifestPath};
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct Sysroot { pub struct Sysroot {
@ -67,18 +67,20 @@ impl Sysroot {
self.crates.iter().map(|(id, _data)| id) self.crates.iter().map(|(id, _data)| id)
} }
pub fn discover(dir: &AbsPath) -> Result<Sysroot> { pub fn discover(dir: &AbsPath, config: &CargoConfig) -> Result<Sysroot> {
tracing::debug!("Discovering sysroot for {}", dir.display()); tracing::debug!("Discovering sysroot for {}", dir.display());
let sysroot_dir = discover_sysroot_dir(dir)?; let sysroot_dir = discover_sysroot_dir(dir, config)?;
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?; let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir, config)?;
let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?;
Ok(res) Ok(res)
} }
pub fn discover_rustc(cargo_toml: &ManifestPath) -> Option<ManifestPath> { pub fn discover_rustc(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<ManifestPath> {
tracing::debug!("Discovering rustc source for {}", cargo_toml.display()); tracing::debug!("Discovering rustc source for {}", cargo_toml.display());
let current_dir = cargo_toml.parent(); let current_dir = cargo_toml.parent();
discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) discover_sysroot_dir(current_dir, config)
.ok()
.and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
} }
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> { pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
@ -144,8 +146,9 @@ impl Sysroot {
} }
} }
fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> { fn discover_sysroot_dir(current_dir: &AbsPath, config: &CargoConfig) -> Result<AbsPathBuf> {
let mut rustc = Command::new(toolchain::rustc()); let mut rustc = Command::new(toolchain::rustc());
rustc.envs(&config.extra_env);
rustc.current_dir(current_dir).args(&["--print", "sysroot"]); rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
tracing::debug!("Discovering sysroot by {:?}", rustc); tracing::debug!("Discovering sysroot by {:?}", rustc);
let stdout = utf8_stdout(rustc)?; let stdout = utf8_stdout(rustc)?;
@ -155,6 +158,7 @@ fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
fn discover_sysroot_src_dir( fn discover_sysroot_src_dir(
sysroot_path: &AbsPathBuf, sysroot_path: &AbsPathBuf,
current_dir: &AbsPath, current_dir: &AbsPath,
config: &CargoConfig,
) -> Result<AbsPathBuf> { ) -> Result<AbsPathBuf> {
if let Ok(path) = env::var("RUST_SRC_PATH") { if let Ok(path) = env::var("RUST_SRC_PATH") {
let path = AbsPathBuf::try_from(path.as_str()) let path = AbsPathBuf::try_from(path.as_str())
@ -170,6 +174,7 @@ fn discover_sysroot_src_dir(
get_rust_src(sysroot_path) get_rust_src(sysroot_path)
.or_else(|| { .or_else(|| {
let mut rustup = Command::new(toolchain::rustup()); let mut rustup = Command::new(toolchain::rustup());
rustup.envs(&config.extra_env);
rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
utf8_stdout(rustup).ok()?; utf8_stdout(rustup).ok()?;
get_rust_src(sysroot_path) get_rust_src(sysroot_path)

View File

@ -10,8 +10,8 @@ use paths::{AbsPath, AbsPathBuf};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use crate::{ use crate::{
CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, CargoConfig, CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace,
WorkspaceBuildScripts, Sysroot, WorkspaceBuildScripts,
}; };
fn load_cargo(file: &str) -> CrateGraph { fn load_cargo(file: &str) -> CrateGraph {
@ -92,13 +92,17 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
} }
fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph { fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
project_workspace.to_crate_graph(&mut |_, _| Ok(Vec::new()), &mut { project_workspace.to_crate_graph(
let mut counter = 0; &mut |_, _| Ok(Vec::new()),
move |_path| { &mut {
counter += 1; let mut counter = 0;
Some(FileId(counter)) move |_path| {
} counter += 1;
}) Some(FileId(counter))
}
},
&CargoConfig::default(),
)
} }
fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) { fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) {

View File

@ -156,11 +156,12 @@ impl ProjectWorkspace {
})?; })?;
let project_location = project_json.parent().to_path_buf(); let project_location = project_json.parent().to_path_buf();
let project_json = ProjectJson::new(&project_location, data); let project_json = ProjectJson::new(&project_location, data);
ProjectWorkspace::load_inline(project_json, config.target.as_deref())? ProjectWorkspace::load_inline(project_json, config.target.as_deref(), config)?
} }
ProjectManifest::CargoToml(cargo_toml) => { ProjectManifest::CargoToml(cargo_toml) => {
let cargo_version = utf8_stdout({ let cargo_version = utf8_stdout({
let mut cmd = Command::new(toolchain::cargo()); let mut cmd = Command::new(toolchain::cargo());
cmd.envs(&config.extra_env);
cmd.arg("--version"); cmd.arg("--version");
cmd cmd
})?; })?;
@ -186,7 +187,7 @@ impl ProjectWorkspace {
let sysroot = if config.no_sysroot { let sysroot = if config.no_sysroot {
None None
} else { } else {
Some(Sysroot::discover(cargo_toml.parent()).with_context(|| { Some(Sysroot::discover(cargo_toml.parent(), config).with_context(|| {
format!( format!(
"Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
cargo_toml.display() cargo_toml.display()
@ -196,7 +197,7 @@ impl ProjectWorkspace {
let rustc_dir = match &config.rustc_source { let rustc_dir = match &config.rustc_source {
Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(), Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml), Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml, config),
None => None, None => None,
}; };
@ -216,7 +217,7 @@ impl ProjectWorkspace {
None => None, None => None,
}; };
let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref()); let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), config);
let cfg_overrides = config.cfg_overrides(); let cfg_overrides = config.cfg_overrides();
ProjectWorkspace::Cargo { ProjectWorkspace::Cargo {
@ -237,6 +238,7 @@ impl ProjectWorkspace {
pub fn load_inline( pub fn load_inline(
project_json: ProjectJson, project_json: ProjectJson,
target: Option<&str>, target: Option<&str>,
config: &CargoConfig,
) -> Result<ProjectWorkspace> { ) -> Result<ProjectWorkspace> {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
(Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?), (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
@ -258,7 +260,7 @@ impl ProjectWorkspace {
(None, None) => None, (None, None) => None,
}; };
let rustc_cfg = rustc_cfg::get(None, target); let rustc_cfg = rustc_cfg::get(None, target, config);
Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
} }
@ -268,8 +270,9 @@ impl ProjectWorkspace {
.first() .first()
.and_then(|it| it.parent()) .and_then(|it| it.parent())
.ok_or_else(|| format_err!("No detached files to load"))?, .ok_or_else(|| format_err!("No detached files to load"))?,
&CargoConfig::default(),
)?; )?;
let rustc_cfg = rustc_cfg::get(None, None); let rustc_cfg = rustc_cfg::get(None, None, &CargoConfig::default());
Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
} }
@ -416,6 +419,7 @@ impl ProjectWorkspace {
&self, &self,
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
config: &CargoConfig,
) -> CrateGraph { ) -> CrateGraph {
let _p = profile::span("ProjectWorkspace::to_crate_graph"); let _p = profile::span("ProjectWorkspace::to_crate_graph");
@ -426,6 +430,7 @@ impl ProjectWorkspace {
load, load,
project, project,
sysroot, sysroot,
config,
), ),
ProjectWorkspace::Cargo { ProjectWorkspace::Cargo {
cargo, cargo,
@ -464,6 +469,7 @@ fn project_json_to_crate_graph(
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
project: &ProjectJson, project: &ProjectJson,
sysroot: &Option<Sysroot>, sysroot: &Option<Sysroot>,
config: &CargoConfig,
) -> CrateGraph { ) -> CrateGraph {
let mut crate_graph = CrateGraph::default(); let mut crate_graph = CrateGraph::default();
let sysroot_deps = sysroot let sysroot_deps = sysroot
@ -489,9 +495,9 @@ fn project_json_to_crate_graph(
}; };
let target_cfgs = match krate.target.as_deref() { let target_cfgs = match krate.target.as_deref() {
Some(target) => { Some(target) => cfg_cache
cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target))) .entry(target)
} .or_insert_with(|| rustc_cfg::get(None, Some(target), config)),
None => &rustc_cfg, None => &rustc_cfg,
}; };

View File

@ -80,7 +80,8 @@ impl flags::AnalysisStats {
Some(build_scripts_sw.elapsed()) Some(build_scripts_sw.elapsed())
}; };
let (host, vfs, _proc_macro) = load_workspace(workspace, &load_cargo_config)?; let (host, vfs, _proc_macro) =
load_workspace(workspace, &cargo_config, &load_cargo_config)?;
let db = host.raw_database(); let db = host.raw_database();
eprint!("{:<20} {}", "Database loaded:", db_load_sw.elapsed()); eprint!("{:<20} {}", "Database loaded:", db_load_sw.elapsed());
eprint!(" (metadata {}", metadata_time); eprint!(" (metadata {}", metadata_time);

View File

@ -38,7 +38,7 @@ pub fn load_workspace_at(
workspace.set_build_scripts(build_scripts) workspace.set_build_scripts(build_scripts)
} }
load_workspace(workspace, load_config) load_workspace(workspace, cargo_config, load_config)
} }
// Note: Since this function is used by external tools that use rust-analyzer as a library // Note: Since this function is used by external tools that use rust-analyzer as a library
@ -48,6 +48,7 @@ pub fn load_workspace_at(
// these tools need access to `ProjectWorkspace`, too, which `load_workspace_at` hides. // these tools need access to `ProjectWorkspace`, too, which `load_workspace_at` hides.
pub fn load_workspace( pub fn load_workspace(
ws: ProjectWorkspace, ws: ProjectWorkspace,
cargo_config: &CargoConfig,
load_config: &LoadCargoConfig, load_config: &LoadCargoConfig,
) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> { ) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
let (sender, receiver) = unbounded(); let (sender, receiver) = unbounded();
@ -75,6 +76,7 @@ pub fn load_workspace(
vfs.set_file_contents(path.clone(), contents); vfs.set_file_contents(path.clone(), contents);
vfs.file_id(&path) vfs.file_id(&path)
}, },
cargo_config,
); );
let project_folders = ProjectFolders::new(&[ws], &[]); let project_folders = ProjectFolders::new(&[ws], &[]);

View File

@ -299,7 +299,8 @@ impl flags::Lsif {
let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
let (host, vfs, _proc_macro) = load_workspace(workspace, &load_cargo_config)?; let (host, vfs, _proc_macro) =
load_workspace(workspace, &cargo_config, &load_cargo_config)?;
let db = host.raw_database(); let db = host.raw_database();
let analysis = host.analysis(); let analysis = host.analysis();

View File

@ -40,7 +40,7 @@ impl flags::Scip {
let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
let (host, vfs, _) = load_workspace(workspace, &load_cargo_config)?; let (host, vfs, _) = load_workspace(workspace, &cargo_config, &load_cargo_config)?;
let db = host.raw_database(); let db = host.raw_database();
let analysis = host.analysis(); let analysis = host.analysis();

View File

@ -84,6 +84,9 @@ config_data! {
/// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
/// avoid checking unnecessary things. /// avoid checking unnecessary things.
cargo_buildScripts_useRustcWrapper: bool = "true", cargo_buildScripts_useRustcWrapper: bool = "true",
/// Extra environment variables that will be set when running cargo, rustc
/// or other commands within the workspace. Useful for setting RUSTFLAGS.
cargo_extraEnv: FxHashMap<String, String> = "{}",
/// List of features to activate. /// List of features to activate.
/// ///
/// Set this to `"all"` to pass `--all-features` to cargo. /// Set this to `"all"` to pass `--all-features` to cargo.
@ -105,6 +108,8 @@ config_data! {
checkOnSave_enable: bool = "true", checkOnSave_enable: bool = "true",
/// Extra arguments for `cargo check`. /// Extra arguments for `cargo check`.
checkOnSave_extraArgs: Vec<String> = "[]", checkOnSave_extraArgs: Vec<String> = "[]",
/// Extra environment variables that will be set when running `cargo check`.
checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
/// List of features to activate. Defaults to /// List of features to activate. Defaults to
/// `#rust-analyzer.cargo.features#`. /// `#rust-analyzer.cargo.features#`.
/// ///
@ -956,6 +961,16 @@ impl Config {
} }
} }
pub fn extra_env(&self) -> &FxHashMap<String, String> {
&self.data.cargo_extraEnv
}
pub fn check_on_save_extra_env(&self) -> FxHashMap<String, String> {
let mut extra_env = self.data.cargo_extraEnv.clone();
extra_env.extend(self.data.checkOnSave_extraEnv.clone());
extra_env
}
pub fn lru_capacity(&self) -> Option<usize> { pub fn lru_capacity(&self) -> Option<usize> {
self.data.lru_capacity self.data.lru_capacity
} }
@ -1025,6 +1040,7 @@ impl Config {
unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()), unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
extra_env: self.data.cargo_extraEnv.clone(),
} }
} }
@ -1050,7 +1066,11 @@ impl Config {
Some(args) if !args.is_empty() => { Some(args) if !args.is_empty() => {
let mut args = args.clone(); let mut args = args.clone();
let command = args.remove(0); let command = args.remove(0);
FlycheckConfig::CustomCommand { command, args } FlycheckConfig::CustomCommand {
command,
args,
extra_env: self.check_on_save_extra_env(),
}
} }
Some(_) | None => FlycheckConfig::CargoCommand { Some(_) | None => FlycheckConfig::CargoCommand {
command: self.data.checkOnSave_command.clone(), command: self.data.checkOnSave_command.clone(),
@ -1078,6 +1098,7 @@ impl Config {
CargoFeatures::Listed(it) => it, CargoFeatures::Listed(it) => it,
}, },
extra_args: self.data.checkOnSave_extraArgs.clone(), extra_args: self.data.checkOnSave_extraArgs.clone(),
extra_env: self.check_on_save_extra_env(),
}, },
}; };
Some(flycheck_config) Some(flycheck_config)

View File

@ -1789,6 +1789,7 @@ fn run_rustfmt(
let mut command = match snap.config.rustfmt() { let mut command = match snap.config.rustfmt() {
RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
let mut cmd = process::Command::new(toolchain::rustfmt()); let mut cmd = process::Command::new(toolchain::rustfmt());
cmd.envs(snap.config.extra_env());
cmd.args(extra_args); cmd.args(extra_args);
// try to chdir to the file so we can respect `rustfmt.toml` // try to chdir to the file so we can respect `rustfmt.toml`
// FIXME: use `rustfmt --config-path` once // FIXME: use `rustfmt --config-path` once
@ -1846,6 +1847,7 @@ fn run_rustfmt(
} }
RustfmtConfig::CustomCommand { command, args } => { RustfmtConfig::CustomCommand { command, args } => {
let mut cmd = process::Command::new(command); let mut cmd = process::Command::new(command);
cmd.envs(snap.config.extra_env());
cmd.args(args); cmd.args(args);
cmd cmd
} }

View File

@ -143,6 +143,7 @@ impl GlobalState {
project_model::ProjectWorkspace::load_inline( project_model::ProjectWorkspace::load_inline(
it.clone(), it.clone(),
cargo_config.target.as_deref(), cargo_config.target.as_deref(),
&cargo_config,
) )
} }
}) })
@ -398,7 +399,11 @@ impl GlobalState {
dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(), dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
) )
}; };
crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load)); crate_graph.extend(ws.to_crate_graph(
&mut load_proc_macro,
&mut load,
&self.config.cargo(),
));
} }
crate_graph crate_graph
}; };

View File

@ -46,6 +46,12 @@ cargo check --quiet --workspace --message-format=json --all-targets
Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
avoid checking unnecessary things. avoid checking unnecessary things.
-- --
[[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`)::
+
--
Extra environment variables that will be set when running cargo, rustc
or other commands within the workspace. Useful for setting RUSTFLAGS.
--
[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
+ +
-- --
@ -93,6 +99,11 @@ Run specified `cargo check` command for diagnostics on save.
-- --
Extra arguments for `cargo check`. Extra arguments for `cargo check`.
-- --
[[rust-analyzer.checkOnSave.extraEnv]]rust-analyzer.checkOnSave.extraEnv (default: `{}`)::
+
--
Extra environment variables that will be set when running `cargo check`.
--
[[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: [[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`)::
+ +
-- --

View File

@ -437,6 +437,11 @@
"default": true, "default": true,
"type": "boolean" "type": "boolean"
}, },
"rust-analyzer.cargo.extraEnv": {
"markdownDescription": "Extra environment variables that will be set when running cargo, rustc\nor other commands within the workspace. Useful for setting RUSTFLAGS.",
"default": {},
"type": "object"
},
"rust-analyzer.cargo.features": { "rust-analyzer.cargo.features": {
"markdownDescription": "List of features to activate.\n\nSet this to `\"all\"` to pass `--all-features` to cargo.", "markdownDescription": "List of features to activate.\n\nSet this to `\"all\"` to pass `--all-features` to cargo.",
"default": [], "default": [],
@ -509,6 +514,11 @@
"type": "string" "type": "string"
} }
}, },
"rust-analyzer.checkOnSave.extraEnv": {
"markdownDescription": "Extra environment variables that will be set when running `cargo check`.",
"default": {},
"type": "object"
},
"rust-analyzer.checkOnSave.features": { "rust-analyzer.checkOnSave.features": {
"markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.\n\nSet to `\"all\"` to pass `--all-features` to Cargo.", "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.\n\nSet to `\"all\"` to pass `--all-features` to Cargo.",
"default": null, "default": null,