Skip to content

Commit 8395087

Browse files
committed
Allow rustup-init to install the default toolchain from a toolchain file
1 parent 8e632bb commit 8395087

File tree

4 files changed

+158
-2
lines changed

4 files changed

+158
-2
lines changed

rustup-init.sh

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ OPTIONS:
4343
--default-host <default-host> Choose a default host triple
4444
--default-toolchain <default-toolchain> Choose a default toolchain to install
4545
--default-toolchain none Do not install any toolchains
46+
--from-file <rust-toolchain-file> Use the default toolchain specified by the rust-toolchain file
4647
--profile [minimal|default|complete] Choose a profile
4748
-c, --component <components>... Component name to also install
4849
-t, --target <targets>... Target name to also install

src/cli/setup_mode.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use clap::{App, AppSettings, Arg};
44
use super::common;
55
use super::self_update::{self, InstallOpts};
66
use crate::dist::dist::Profile;
7-
use crate::process;
87
use crate::utils::utils;
8+
use crate::{process, InstallFromToolchainFileCfg};
99

1010
pub fn main() -> Result<utils::ExitCode> {
1111
let args: Vec<_> = process().args().collect();
@@ -57,6 +57,12 @@ pub fn main() -> Result<utils::ExitCode> {
5757
.takes_value(true)
5858
.help("Choose a default toolchain to install"),
5959
)
60+
.arg(
61+
Arg::with_name("from-file")
62+
.long("from-file")
63+
.takes_value(true)
64+
.help("Use the default toolchain specified by the rust-toolchain file"),
65+
)
6066
.arg(
6167
Arg::with_name("profile")
6268
.long("profile")
@@ -103,11 +109,25 @@ pub fn main() -> Result<utils::ExitCode> {
103109
}
104110
Err(e) => return Err(e.into()),
105111
};
112+
113+
let toolchain_cfg = match matches
114+
.value_of("from-file")
115+
.map(InstallFromToolchainFileCfg::try_from_file)
116+
{
117+
Some(Ok(cfg)) => Some(cfg),
118+
Some(Err(e)) => return Err(e.into()),
119+
None => None,
120+
};
121+
106122
let no_prompt = matches.is_present("no-prompt");
107123
let verbose = matches.is_present("verbose");
108124
let quiet = matches.is_present("quiet");
109125
let default_host = matches.value_of("default-host").map(ToOwned::to_owned);
110-
let default_toolchain = matches.value_of("default-toolchain").map(ToOwned::to_owned);
126+
let default_toolchain = matches
127+
.value_of("default-toolchain")
128+
.map(ToOwned::to_owned)
129+
.or_else(|| toolchain_cfg.and_then(|cfg| cfg.channel()));
130+
111131
let profile = matches
112132
.value_of("profile")
113133
.expect("Unreachable: Clap should supply a default");

src/config.rs

+40
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::borrow::Cow;
2+
use std::ffi::OsStr;
23
use std::fmt::{self, Display};
34
use std::io;
45
use std::path::{Path, PathBuf};
@@ -997,6 +998,45 @@ enum ParseMode {
997998
Both,
998999
}
9991000

1001+
/// Configuration used with `rustup-init`, to install a default toolchain from a toolchain file.
1002+
#[derive(Debug, Default, PartialEq, Eq)]
1003+
pub struct InstallFromToolchainFileCfg {
1004+
toolchain_file: OverrideFile,
1005+
}
1006+
1007+
impl InstallFromToolchainFileCfg {
1008+
/// Try to create a new `InstallFromToolchainFileCfg`.
1009+
///
1010+
/// If the given toolchain file path (given through the `path` argument) ends in a `toml` file
1011+
/// extension, only the TOML parse mode will be used.
1012+
///
1013+
/// Fails if the toolchain file does not exist, is not a file, can not be read
1014+
/// or if the toolchain file contents cannot be parsed.
1015+
pub fn try_from_file(path: impl AsRef<Path>) -> Result<Self> {
1016+
let path = path.as_ref();
1017+
let parse_mode = Self::parse_mode(path);
1018+
let contents = utils::read_file("toolchain file", path)?;
1019+
1020+
let toolchain_file = Cfg::parse_override_file(&contents, parse_mode)?;
1021+
1022+
Ok(Self { toolchain_file })
1023+
}
1024+
1025+
/// The `channel` defined by a toolchain file corresponds to the `toolchain` field when installing
1026+
/// a toolchain with `rustup-init`.
1027+
pub fn channel(&self) -> Option<String> {
1028+
self.toolchain_file.toolchain.channel.to_owned()
1029+
}
1030+
1031+
/// Allow only the legacy format, if the file doesn't end in a toml file extension.
1032+
fn parse_mode(path: &Path) -> ParseMode {
1033+
path.extension()
1034+
.filter(|ext| ext == &OsStr::new("toml"))
1035+
.map(|_| ParseMode::OnlyToml)
1036+
.unwrap_or(ParseMode::Both)
1037+
}
1038+
}
1039+
10001040
#[cfg(test)]
10011041
mod tests {
10021042
use super::*;

tests/cli-self-upd.rs

+95
Original file line numberDiff line numberDiff line change
@@ -914,3 +914,98 @@ fn install_minimal_profile() {
914914
expect_component_not_executable(config, "cargo");
915915
});
916916
}
917+
918+
#[test]
919+
fn install_from_rust_toolchain_file_specific_version() {
920+
clitools::setup(Scenario::SimpleV2, &|config| {
921+
let cwd = config.current_dir();
922+
let toolchain_file = cwd.join("rust-toolchain.toml");
923+
924+
raw::write_file(
925+
&toolchain_file,
926+
r#"[toolchain]
927+
channel = "1.1.0""#,
928+
)
929+
.unwrap();
930+
931+
expect_ok(
932+
config,
933+
&["rustup-init", "-y", "--from-file", "rust-toolchain.toml"],
934+
);
935+
expect_stdout_ok(
936+
config,
937+
&["rustup", "toolchain", "list"],
938+
&format!("1.1.0-{} (default)", this_host_triple()),
939+
);
940+
})
941+
}
942+
943+
#[test]
944+
fn install_from_rust_toolchain_file_latest_stable() {
945+
clitools::setup(Scenario::SimpleV2, &|config| {
946+
let cwd = config.current_dir();
947+
let toolchain_file = cwd.join("rust-toolchain.toml");
948+
949+
raw::write_file(
950+
&toolchain_file,
951+
r#"[toolchain]
952+
channel = "stable""#,
953+
)
954+
.unwrap();
955+
956+
expect_ok(
957+
config,
958+
&["rustup-init", "-y", "--from-file", "rust-toolchain.toml"],
959+
);
960+
expect_stdout_ok(
961+
config,
962+
&["rustup", "toolchain", "list"],
963+
&format!("stable-{} (default)", this_host_triple()),
964+
);
965+
})
966+
}
967+
968+
#[test]
969+
fn install_from_rust_toolchain_file_toml_format_without_toml_extension() {
970+
clitools::setup(Scenario::SimpleV2, &|config| {
971+
let cwd = config.current_dir();
972+
let toolchain_file = cwd.join("rust-toolchain");
973+
974+
raw::write_file(
975+
&toolchain_file,
976+
r#"[toolchain]
977+
channel = "1.1.0""#,
978+
)
979+
.unwrap();
980+
981+
expect_ok(
982+
config,
983+
&["rustup-init", "-y", "--from-file", "rust-toolchain"],
984+
);
985+
expect_stdout_ok(
986+
config,
987+
&["rustup", "toolchain", "list"],
988+
&format!("1.1.0-{} (default)", this_host_triple()),
989+
);
990+
})
991+
}
992+
993+
#[test]
994+
fn install_from_rust_toolchain_file_legacy_format() {
995+
clitools::setup(Scenario::SimpleV2, &|config| {
996+
let cwd = config.current_dir();
997+
let toolchain_file = cwd.join("rust-toolchain");
998+
999+
raw::write_file(&toolchain_file, r#"nightly"#).unwrap();
1000+
1001+
expect_ok(
1002+
config,
1003+
&["rustup-init", "-y", "--from-file", "rust-toolchain"],
1004+
);
1005+
expect_stdout_ok(
1006+
config,
1007+
&["rustup", "toolchain", "list"],
1008+
&format!("nightly-{} (default)", this_host_triple()),
1009+
);
1010+
})
1011+
}

0 commit comments

Comments
 (0)