Skip to content

Commit 45d6158

Browse files
author
Jon Gjengset
committed
Add support for [env] in rust-toolchain.toml
Fixes rust-lang#2883.
1 parent 59de864 commit 45d6158

File tree

5 files changed

+259
-26
lines changed

5 files changed

+259
-26
lines changed

src/cli/rustup_mode.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1165,11 +1165,11 @@ fn show(cfg: &Cfg) -> Result<utils::ExitCode> {
11651165

11661166
match active_toolchain {
11671167
Ok(atc) => match atc {
1168-
(ref toolchain, Some(ref reason)) => {
1168+
(ref toolchain, _, Some(ref reason)) => {
11691169
writeln!(t, "{} ({})", toolchain.name(), reason)?;
11701170
writeln!(t, "{}", toolchain.rustc_version())?;
11711171
}
1172-
(ref toolchain, None) => {
1172+
(ref toolchain, _, None) => {
11731173
writeln!(t, "{} (default)", toolchain.name())?;
11741174
writeln!(t, "{}", toolchain.rustc_version())?;
11751175
}
@@ -1221,7 +1221,7 @@ fn show_active_toolchain(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<utils::ExitCod
12211221
return Err(e);
12221222
}
12231223
}
1224-
Ok((toolchain, reason)) => {
1224+
Ok((toolchain, _, reason)) => {
12251225
if let Some(reason) = reason {
12261226
writeln!(process().stdout(), "{} ({})", toolchain.name(), reason)?;
12271227
} else {
@@ -1376,7 +1376,7 @@ fn explicit_or_dir_toolchain<'a>(cfg: &'a Cfg, m: &ArgMatches<'_>) -> Result<Too
13761376
}
13771377

13781378
let cwd = utils::current_dir()?;
1379-
let (toolchain, _) = cfg.toolchain_for_dir(&cwd)?;
1379+
let (toolchain, _, _) = cfg.toolchain_for_dir(&cwd)?;
13801380

13811381
Ok(toolchain)
13821382
}

src/config.rs

+179-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::borrow::Cow;
2+
use std::collections::HashMap;
23
use std::fmt::{self, Display};
34
use std::io;
45
use std::path::{Path, PathBuf};
@@ -38,6 +39,8 @@ enum OverrideFileConfigError {
3839
#[derive(Debug, Default, Deserialize, PartialEq, Eq)]
3940
struct OverrideFile {
4041
toolchain: ToolchainSection,
42+
#[serde(default)]
43+
env: HashMap<String, EnvOverride>,
4144
}
4245

4346
impl OverrideFile {
@@ -64,6 +67,19 @@ impl ToolchainSection {
6467
}
6568
}
6669

70+
#[derive(Debug, Deserialize, PartialEq, Eq)]
71+
#[serde(untagged)]
72+
enum EnvOverride {
73+
Literal(String),
74+
Table {
75+
value: String,
76+
#[serde(default)]
77+
force: bool,
78+
#[serde(default)]
79+
relative: bool,
80+
},
81+
}
82+
6783
impl<T: Into<String>> From<T> for OverrideFile {
6884
fn from(channel: T) -> Self {
6985
let override_ = channel.into();
@@ -73,13 +89,15 @@ impl<T: Into<String>> From<T> for OverrideFile {
7389
path: Some(PathBuf::from(override_)),
7490
..Default::default()
7591
},
92+
env: Default::default(),
7693
}
7794
} else {
7895
Self {
7996
toolchain: ToolchainSection {
8097
channel: Some(override_),
8198
..Default::default()
8299
},
100+
env: Default::default(),
83101
}
84102
}
85103
}
@@ -110,6 +128,7 @@ struct OverrideCfg<'a> {
110128
components: Vec<String>,
111129
targets: Vec<String>,
112130
profile: Option<dist::Profile>,
131+
env: HashMap<String, String>,
113132
}
114133

115134
impl<'a> OverrideCfg<'a> {
@@ -150,6 +169,30 @@ impl<'a> OverrideCfg<'a> {
150169
.as_deref()
151170
.map(dist::Profile::from_str)
152171
.transpose()?,
172+
env: file
173+
.env
174+
.into_iter()
175+
.filter_map(|(k, v)| {
176+
let (v, force, relative) = match v {
177+
EnvOverride::Literal(v) => (v, false, false),
178+
EnvOverride::Table {
179+
value,
180+
force,
181+
relative,
182+
} => (value, force, relative),
183+
};
184+
185+
if relative {
186+
return Some(Err(anyhow!(
187+
"Rustup does not yet support config-relative values"
188+
)));
189+
}
190+
if !force && process().var_os(&k).is_some() {
191+
return None;
192+
}
193+
Some(Ok((k, v)))
194+
})
195+
.collect::<Result<_, _>>()?,
153196
})
154197
}
155198
}
@@ -492,7 +535,7 @@ impl Cfg {
492535
}
493536

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

@@ -729,7 +772,11 @@ impl Cfg {
729772
pub fn find_or_install_override_toolchain_or_default(
730773
&self,
731774
path: &Path,
732-
) -> Result<(Toolchain<'_>, Option<OverrideReason>)> {
775+
) -> Result<(
776+
Toolchain<'_>,
777+
HashMap<String, String>,
778+
Option<OverrideReason>,
779+
)> {
733780
fn components_exist(
734781
distributable: &DistributableToolchain<'_>,
735782
components: &[&str],
@@ -773,14 +820,15 @@ impl Cfg {
773820
}
774821
}
775822

776-
if let Some((toolchain, components, targets, reason, profile)) =
823+
if let Some((toolchain, components, targets, reason, profile, env)) =
777824
match self.find_override_config(path)? {
778825
Some((
779826
OverrideCfg {
780827
toolchain,
781828
components,
782829
targets,
783830
profile,
831+
env,
784832
},
785833
reason,
786834
)) => {
@@ -790,13 +838,13 @@ impl Cfg {
790838
None
791839
};
792840

793-
toolchain
794-
.or(default)
795-
.map(|toolchain| (toolchain, components, targets, Some(reason), profile))
841+
toolchain.or(default).map(|toolchain| {
842+
(toolchain, components, targets, Some(reason), profile, env)
843+
})
796844
}
797845
None => self
798846
.find_default()?
799-
.map(|toolchain| (toolchain, vec![], vec![], None, None)),
847+
.map(|toolchain| (toolchain, vec![], vec![], None, None, Default::default())),
800848
}
801849
{
802850
if toolchain.is_custom() {
@@ -816,7 +864,7 @@ impl Cfg {
816864
}
817865
}
818866

819-
Ok((toolchain, reason))
867+
Ok((toolchain, env, reason))
820868
} else {
821869
// No override and no default set
822870
Err(RustupError::ToolchainNotSelected.into())
@@ -905,21 +953,29 @@ impl Cfg {
905953
pub fn toolchain_for_dir(
906954
&self,
907955
path: &Path,
908-
) -> Result<(Toolchain<'_>, Option<OverrideReason>)> {
956+
) -> Result<(
957+
Toolchain<'_>,
958+
HashMap<String, String>,
959+
Option<OverrideReason>,
960+
)> {
909961
self.find_or_install_override_toolchain_or_default(path)
910962
}
911963

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

915-
if let Some(cmd) = self.maybe_do_cargo_fallback(toolchain, binary)? {
967+
let mut cmd = if let Some(cmd) = self.maybe_do_cargo_fallback(toolchain, binary)? {
916968
Ok(cmd)
917969
} else {
918970
// NB this can only fail in race conditions since we used toolchain
919971
// for dir.
920972
let installed = toolchain.as_installed_common()?;
921973
installed.create_command(binary)
922-
}
974+
}?;
975+
976+
cmd.envs(env);
977+
978+
Ok(cmd)
923979
}
924980

925981
pub fn create_command_for_toolchain(
@@ -1043,7 +1099,8 @@ mod tests {
10431099
components: None,
10441100
targets: None,
10451101
profile: None,
1046-
}
1102+
},
1103+
env: Default::default(),
10471104
}
10481105
);
10491106
}
@@ -1070,7 +1127,8 @@ profile = "default"
10701127
"thumbv2-none-eabi".into()
10711128
]),
10721129
profile: Some("default".into()),
1073-
}
1130+
},
1131+
env: Default::default(),
10741132
}
10751133
);
10761134
}
@@ -1091,7 +1149,8 @@ channel = "nightly-2020-07-10"
10911149
components: None,
10921150
targets: None,
10931151
profile: None,
1094-
}
1152+
},
1153+
env: Default::default(),
10951154
}
10961155
);
10971156
}
@@ -1112,7 +1171,8 @@ path = "foobar"
11121171
components: None,
11131172
targets: None,
11141173
profile: None,
1115-
}
1174+
},
1175+
env: Default::default(),
11161176
}
11171177
);
11181178
}
@@ -1134,7 +1194,8 @@ components = []
11341194
components: Some(vec![]),
11351195
targets: None,
11361196
profile: None,
1137-
}
1197+
},
1198+
env: Default::default(),
11381199
}
11391200
);
11401201
}
@@ -1156,7 +1217,8 @@ targets = []
11561217
components: None,
11571218
targets: Some(vec![]),
11581219
profile: None,
1159-
}
1220+
},
1221+
env: Default::default(),
11601222
}
11611223
);
11621224
}
@@ -1177,7 +1239,8 @@ components = [ "rustfmt" ]
11771239
components: Some(vec!["rustfmt".into()]),
11781240
targets: None,
11791241
profile: None,
1180-
}
1242+
},
1243+
env: Default::default(),
11811244
}
11821245
);
11831246
}
@@ -1195,6 +1258,103 @@ components = [ "rustfmt" ]
11951258
));
11961259
}
11971260

1261+
#[test]
1262+
fn parse_toml_toolchain_file_env_literal() {
1263+
// XXX: It'd be nice if it was possible to specify [env] but _not_ [toolchain],
1264+
// but that seems to currently cause an "empty config" error.
1265+
let contents = r#"
1266+
[toolchain]
1267+
channel = "nightly-2020-07-10"
1268+
[env]
1269+
OPENSSL_DIR = "/opt/openssl"
1270+
"#;
1271+
1272+
let result = Cfg::parse_override_file(contents, ParseMode::Both);
1273+
assert_eq!(
1274+
result.unwrap(),
1275+
OverrideFile {
1276+
toolchain: ToolchainSection {
1277+
channel: Some("nightly-2020-07-10".into()),
1278+
path: None,
1279+
components: None,
1280+
targets: None,
1281+
profile: None,
1282+
},
1283+
env: HashMap::from([(
1284+
String::from("OPENSSL_DIR"),
1285+
EnvOverride::Literal(String::from("/opt/openssl"))
1286+
)]),
1287+
}
1288+
);
1289+
}
1290+
1291+
#[test]
1292+
fn parse_toml_toolchain_file_env_table() {
1293+
let contents = r#"
1294+
[toolchain]
1295+
channel = "nightly-2020-07-10"
1296+
[env]
1297+
TMPDIR = { value = "/home/tmp", force = true }
1298+
OPENSSL_DIR = { value = "vendor/openssl", relative = true }
1299+
"#;
1300+
1301+
let result = Cfg::parse_override_file(contents, ParseMode::Both);
1302+
assert_eq!(
1303+
result.unwrap(),
1304+
OverrideFile {
1305+
toolchain: ToolchainSection {
1306+
channel: Some("nightly-2020-07-10".into()),
1307+
path: None,
1308+
components: None,
1309+
targets: None,
1310+
profile: None,
1311+
},
1312+
env: HashMap::from([
1313+
(
1314+
String::from("TMPDIR"),
1315+
EnvOverride::Table {
1316+
value: String::from("/home/tmp"),
1317+
force: true,
1318+
relative: false
1319+
}
1320+
),
1321+
(
1322+
String::from("OPENSSL_DIR"),
1323+
EnvOverride::Table {
1324+
value: String::from("vendor/openssl"),
1325+
force: false,
1326+
relative: true
1327+
}
1328+
)
1329+
]),
1330+
}
1331+
);
1332+
}
1333+
1334+
#[test]
1335+
fn parse_empty_toml_toolchain_file_env() {
1336+
let contents = r#"
1337+
[toolchain]
1338+
channel = "nightly-2020-07-10"
1339+
[env]
1340+
"#;
1341+
1342+
let result = Cfg::parse_override_file(contents, ParseMode::Both);
1343+
assert_eq!(
1344+
result.unwrap(),
1345+
OverrideFile {
1346+
toolchain: ToolchainSection {
1347+
channel: Some("nightly-2020-07-10".into()),
1348+
path: None,
1349+
components: None,
1350+
targets: None,
1351+
profile: None,
1352+
},
1353+
env: Default::default(),
1354+
}
1355+
);
1356+
}
1357+
11981358
#[test]
11991359
fn parse_empty_toolchain_file() {
12001360
let contents = "";

0 commit comments

Comments
 (0)