Skip to content

Commit

Permalink
Add support for [env] in rust-toolchain.toml
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Gjengset committed Oct 28, 2021
1 parent 59de864 commit 45d6158
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 26 deletions.
8 changes: 4 additions & 4 deletions src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1165,11 +1165,11 @@ fn show(cfg: &Cfg) -> Result<utils::ExitCode> {

match active_toolchain {
Ok(atc) => match atc {
(ref toolchain, Some(ref reason)) => {
(ref toolchain, _, Some(ref reason)) => {
writeln!(t, "{} ({})", toolchain.name(), reason)?;
writeln!(t, "{}", toolchain.rustc_version())?;
}
(ref toolchain, None) => {
(ref toolchain, _, None) => {
writeln!(t, "{} (default)", toolchain.name())?;
writeln!(t, "{}", toolchain.rustc_version())?;
}
Expand Down Expand Up @@ -1221,7 +1221,7 @@ fn show_active_toolchain(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<utils::ExitCod
return Err(e);
}
}
Ok((toolchain, reason)) => {
Ok((toolchain, _, reason)) => {
if let Some(reason) = reason {
writeln!(process().stdout(), "{} ({})", toolchain.name(), reason)?;
} else {
Expand Down Expand Up @@ -1376,7 +1376,7 @@ fn explicit_or_dir_toolchain<'a>(cfg: &'a Cfg, m: &ArgMatches<'_>) -> Result<Too
}

let cwd = utils::current_dir()?;
let (toolchain, _) = cfg.toolchain_for_dir(&cwd)?;
let (toolchain, _, _) = cfg.toolchain_for_dir(&cwd)?;

Ok(toolchain)
}
Expand Down
198 changes: 179 additions & 19 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::{self, Display};
use std::io;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -38,6 +39,8 @@ enum OverrideFileConfigError {
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
struct OverrideFile {
toolchain: ToolchainSection,
#[serde(default)]
env: HashMap<String, EnvOverride>,
}

impl OverrideFile {
Expand All @@ -64,6 +67,19 @@ impl ToolchainSection {
}
}

#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
enum EnvOverride {
Literal(String),
Table {
value: String,
#[serde(default)]
force: bool,
#[serde(default)]
relative: bool,
},
}

impl<T: Into<String>> From<T> for OverrideFile {
fn from(channel: T) -> Self {
let override_ = channel.into();
Expand All @@ -73,13 +89,15 @@ impl<T: Into<String>> From<T> for OverrideFile {
path: Some(PathBuf::from(override_)),
..Default::default()
},
env: Default::default(),
}
} else {
Self {
toolchain: ToolchainSection {
channel: Some(override_),
..Default::default()
},
env: Default::default(),
}
}
}
Expand Down Expand Up @@ -110,6 +128,7 @@ struct OverrideCfg<'a> {
components: Vec<String>,
targets: Vec<String>,
profile: Option<dist::Profile>,
env: HashMap<String, String>,
}

impl<'a> OverrideCfg<'a> {
Expand Down Expand Up @@ -150,6 +169,30 @@ impl<'a> OverrideCfg<'a> {
.as_deref()
.map(dist::Profile::from_str)
.transpose()?,
env: file
.env
.into_iter()
.filter_map(|(k, v)| {
let (v, force, relative) = match v {
EnvOverride::Literal(v) => (v, false, false),
EnvOverride::Table {
value,
force,
relative,
} => (value, force, relative),
};

if relative {
return Some(Err(anyhow!(
"Rustup does not yet support config-relative values"
)));
}
if !force && process().var_os(&k).is_some() {
return None;
}
Some(Ok((k, v)))
})
.collect::<Result<_, _>>()?,
})
}
}
Expand Down Expand Up @@ -492,7 +535,7 @@ impl Cfg {
}

pub fn which_binary(&self, path: &Path, binary: &str) -> Result<Option<PathBuf>> {
let (toolchain, _) = self.find_or_install_override_toolchain_or_default(path)?;
let (toolchain, _, _) = self.find_or_install_override_toolchain_or_default(path)?;
Ok(Some(toolchain.binary_file(binary)))
}

Expand Down Expand Up @@ -729,7 +772,11 @@ impl Cfg {
pub fn find_or_install_override_toolchain_or_default(
&self,
path: &Path,
) -> Result<(Toolchain<'_>, Option<OverrideReason>)> {
) -> Result<(
Toolchain<'_>,
HashMap<String, String>,
Option<OverrideReason>,
)> {
fn components_exist(
distributable: &DistributableToolchain<'_>,
components: &[&str],
Expand Down Expand Up @@ -773,14 +820,15 @@ impl Cfg {
}
}

if let Some((toolchain, components, targets, reason, profile)) =
if let Some((toolchain, components, targets, reason, profile, env)) =
match self.find_override_config(path)? {
Some((
OverrideCfg {
toolchain,
components,
targets,
profile,
env,
},
reason,
)) => {
Expand All @@ -790,13 +838,13 @@ impl Cfg {
None
};

toolchain
.or(default)
.map(|toolchain| (toolchain, components, targets, Some(reason), profile))
toolchain.or(default).map(|toolchain| {
(toolchain, components, targets, Some(reason), profile, env)
})
}
None => self
.find_default()?
.map(|toolchain| (toolchain, vec![], vec![], None, None)),
.map(|toolchain| (toolchain, vec![], vec![], None, None, Default::default())),
}
{
if toolchain.is_custom() {
Expand All @@ -816,7 +864,7 @@ impl Cfg {
}
}

Ok((toolchain, reason))
Ok((toolchain, env, reason))
} else {
// No override and no default set
Err(RustupError::ToolchainNotSelected.into())
Expand Down Expand Up @@ -905,21 +953,29 @@ impl Cfg {
pub fn toolchain_for_dir(
&self,
path: &Path,
) -> Result<(Toolchain<'_>, Option<OverrideReason>)> {
) -> Result<(
Toolchain<'_>,
HashMap<String, String>,
Option<OverrideReason>,
)> {
self.find_or_install_override_toolchain_or_default(path)
}

pub fn create_command_for_dir(&self, path: &Path, binary: &str) -> Result<Command> {
let (ref toolchain, _) = self.toolchain_for_dir(path)?;
let (ref toolchain, ref env, _) = self.toolchain_for_dir(path)?;

if let Some(cmd) = self.maybe_do_cargo_fallback(toolchain, binary)? {
let mut cmd = if let Some(cmd) = self.maybe_do_cargo_fallback(toolchain, binary)? {
Ok(cmd)
} else {
// NB this can only fail in race conditions since we used toolchain
// for dir.
let installed = toolchain.as_installed_common()?;
installed.create_command(binary)
}
}?;

cmd.envs(env);

Ok(cmd)
}

pub fn create_command_for_toolchain(
Expand Down Expand Up @@ -1043,7 +1099,8 @@ mod tests {
components: None,
targets: None,
profile: None,
}
},
env: Default::default(),
}
);
}
Expand All @@ -1070,7 +1127,8 @@ profile = "default"
"thumbv2-none-eabi".into()
]),
profile: Some("default".into()),
}
},
env: Default::default(),
}
);
}
Expand All @@ -1091,7 +1149,8 @@ channel = "nightly-2020-07-10"
components: None,
targets: None,
profile: None,
}
},
env: Default::default(),
}
);
}
Expand All @@ -1112,7 +1171,8 @@ path = "foobar"
components: None,
targets: None,
profile: None,
}
},
env: Default::default(),
}
);
}
Expand All @@ -1134,7 +1194,8 @@ components = []
components: Some(vec![]),
targets: None,
profile: None,
}
},
env: Default::default(),
}
);
}
Expand All @@ -1156,7 +1217,8 @@ targets = []
components: None,
targets: Some(vec![]),
profile: None,
}
},
env: Default::default(),
}
);
}
Expand All @@ -1177,7 +1239,8 @@ components = [ "rustfmt" ]
components: Some(vec!["rustfmt".into()]),
targets: None,
profile: None,
}
},
env: Default::default(),
}
);
}
Expand All @@ -1195,6 +1258,103 @@ components = [ "rustfmt" ]
));
}

#[test]
fn parse_toml_toolchain_file_env_literal() {
// XXX: It'd be nice if it was possible to specify [env] but _not_ [toolchain],
// but that seems to currently cause an "empty config" error.
let contents = r#"
[toolchain]
channel = "nightly-2020-07-10"
[env]
OPENSSL_DIR = "/opt/openssl"
"#;

let result = Cfg::parse_override_file(contents, ParseMode::Both);
assert_eq!(
result.unwrap(),
OverrideFile {
toolchain: ToolchainSection {
channel: Some("nightly-2020-07-10".into()),
path: None,
components: None,
targets: None,
profile: None,
},
env: HashMap::from([(
String::from("OPENSSL_DIR"),
EnvOverride::Literal(String::from("/opt/openssl"))
)]),
}
);
}

#[test]
fn parse_toml_toolchain_file_env_table() {
let contents = r#"
[toolchain]
channel = "nightly-2020-07-10"
[env]
TMPDIR = { value = "/home/tmp", force = true }
OPENSSL_DIR = { value = "vendor/openssl", relative = true }
"#;

let result = Cfg::parse_override_file(contents, ParseMode::Both);
assert_eq!(
result.unwrap(),
OverrideFile {
toolchain: ToolchainSection {
channel: Some("nightly-2020-07-10".into()),
path: None,
components: None,
targets: None,
profile: None,
},
env: HashMap::from([
(
String::from("TMPDIR"),
EnvOverride::Table {
value: String::from("/home/tmp"),
force: true,
relative: false
}
),
(
String::from("OPENSSL_DIR"),
EnvOverride::Table {
value: String::from("vendor/openssl"),
force: false,
relative: true
}
)
]),
}
);
}

#[test]
fn parse_empty_toml_toolchain_file_env() {
let contents = r#"
[toolchain]
channel = "nightly-2020-07-10"
[env]
"#;

let result = Cfg::parse_override_file(contents, ParseMode::Both);
assert_eq!(
result.unwrap(),
OverrideFile {
toolchain: ToolchainSection {
channel: Some("nightly-2020-07-10".into()),
path: None,
components: None,
targets: None,
profile: None,
},
env: Default::default(),
}
);
}

#[test]
fn parse_empty_toolchain_file() {
let contents = "";
Expand Down
Loading

0 comments on commit 45d6158

Please sign in to comment.