Skip to content

Commit

Permalink
Allow rustup-init to install the default toolchain from a toolchain file
Browse files Browse the repository at this point in the history
  • Loading branch information
foresterre committed May 4, 2021
1 parent 8e632bb commit 9e20396
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 2 deletions.
1 change: 1 addition & 0 deletions rustup-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ OPTIONS:
--default-host <default-host> Choose a default host triple
--default-toolchain <default-toolchain> Choose a default toolchain to install
--default-toolchain none Do not install any toolchains
--from-file <rust-toolchain-file> Use the default toolchain specified by the rust-toolchain file
--profile [minimal|default|complete] Choose a profile
-c, --component <components>... Component name to also install
-t, --target <targets>... Target name to also install
Expand Down
24 changes: 22 additions & 2 deletions src/cli/setup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use clap::{App, AppSettings, Arg};
use super::common;
use super::self_update::{self, InstallOpts};
use crate::dist::dist::Profile;
use crate::process;
use crate::utils::utils;
use crate::{process, InstallFromToolchainFileCfg};

pub fn main() -> Result<utils::ExitCode> {
let args: Vec<_> = process().args().collect();
Expand Down Expand Up @@ -57,6 +57,12 @@ pub fn main() -> Result<utils::ExitCode> {
.takes_value(true)
.help("Choose a default toolchain to install"),
)
.arg(
Arg::with_name("from-file")
.long("from-file")
.takes_value(true)
.help("Use the default toolchain specified by the rust-toolchain file")
)
.arg(
Arg::with_name("profile")
.long("profile")
Expand Down Expand Up @@ -103,11 +109,25 @@ pub fn main() -> Result<utils::ExitCode> {
}
Err(e) => return Err(e.into()),
};

let toolchain_cfg = match matches
.value_of("from-file")
.map(InstallFromToolchainFileCfg::try_from_file)
{
Some(Ok(cfg)) => Some(cfg),
Some(Err(e)) => return Err(e.into()),
None => None,
};

let no_prompt = matches.is_present("no-prompt");
let verbose = matches.is_present("verbose");
let quiet = matches.is_present("quiet");
let default_host = matches.value_of("default-host").map(ToOwned::to_owned);
let default_toolchain = matches.value_of("default-toolchain").map(ToOwned::to_owned);
let default_toolchain = matches
.value_of("default-toolchain")
.map(ToOwned::to_owned)
.or_else(|| toolchain_cfg.and_then(|cfg| cfg.channel()));

let profile = matches
.value_of("profile")
.expect("Unreachable: Clap should supply a default");
Expand Down
40 changes: 40 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::ffi::OsStr;
use std::fmt::{self, Display};
use std::io;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -997,6 +998,45 @@ enum ParseMode {
Both,
}

/// Configuration used with `rustup-init`, to install a default toolchain from a toolchain file.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct InstallFromToolchainFileCfg {
toolchain_file: OverrideFile,
}

impl InstallFromToolchainFileCfg {
/// Try to create a new `InstallFromToolchainFileCfg`.
///
/// If the given toolchain file path (given through the `path` argument) ends in a `toml` file
/// extension, only the TOML parse mode will be used.
///
/// Fails if the toolchain file does not exist, is not a file, can not be read
/// or if the toolchain file contents cannot be parsed.
pub fn try_from_file(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
let parse_mode = Self::parse_mode(path);
let contents = utils::read_file("toolchain file", path)?;

let toolchain_file = Cfg::parse_override_file(&contents, parse_mode)?;

Ok(Self { toolchain_file })
}

/// The `channel` defined by a toolchain file corresponds to the `toolchain` field when installing
/// a toolchain with `rustup-init`.
pub fn channel(&self) -> Option<String> {
self.toolchain_file.toolchain.channel.to_owned()
}

/// Allow only the legacy format, if the file doesn't end in a toml file extension.
fn parse_mode(path: &Path) -> ParseMode {
path.extension()
.filter(|ext| ext == &OsStr::new("toml"))
.map(|_| ParseMode::OnlyToml)
.unwrap_or(ParseMode::Both)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
25 changes: 25 additions & 0 deletions tests/cli-self-upd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,3 +914,28 @@ fn install_minimal_profile() {
expect_component_not_executable(config, "cargo");
});
}

#[test]
fn install_from_rust_toolchain_file() {
clitools::setup(Scenario::SimpleV2, &|config| {
let cwd = config.current_dir();
let toolchain_file = cwd.join("rust-toolchain.toml");

raw::write_file(
&toolchain_file,
r#"[toolchain]
channel = "1.1.0""#,
)
.unwrap();

expect_ok(
config,
&["rustup-init", "-y", "--from-file", "rust-toolchain.toml"],
);
expect_stdout_ok(
config,
&["rustup", "toolchain", "list"],
&format!("1.1.0-{} (default)", this_host_triple()),
);
})
}

0 comments on commit 9e20396

Please sign in to comment.