From edddcd9bebbc206ee752d003653157c52c363c79 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 27 Sep 2019 18:49:51 +0200 Subject: [PATCH] Support installation with additional components and targets Add support for "-c" and "-t" to `rustup toolchain install` and to `rustup-init` itself. Signed-off-by: Daniel Silverstone --- rustup-init.sh | 2 ++ src/cli/rustup_mode.rs | 26 +++++++++++++++++++++- src/cli/self_update.rs | 10 +++++++-- src/cli/setup_mode.rs | 28 ++++++++++++++++++++++++ src/config.rs | 6 +++--- src/dist/dist.rs | 49 ++++++++++++++++++++++++++++++++++++++++-- src/install.rs | 8 +++++++ src/toolchain.rs | 11 +++++++++- tests/cli-self-upd.rs | 29 +++++++++++++++++++++++++ tests/cli-v2.rs | 31 ++++++++++++++++++++++++++ 10 files changed, 191 insertions(+), 9 deletions(-) diff --git a/rustup-init.sh b/rustup-init.sh index f45fbb69361..5097ee5ec63 100755 --- a/rustup-init.sh +++ b/rustup-init.sh @@ -31,6 +31,8 @@ OPTIONS: --default-toolchain Choose a default toolchain to install --default-toolchain none Do not install any toolchains --profile [minimal|default|complete] Choose a profile + -c, --component ... Component name to also install + -t, --target ... Target name to also install EOF } diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 790abbba74c..4bd60084906 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -256,6 +256,22 @@ pub fn cli() -> App<'static, 'static> { .help("Don't perform self update when running the `rustup toolchain install` command") .long("no-self-update") .takes_value(false) + ) + .arg( + Arg::with_name("components") + .help("Add specific components on installation") + .long("component") + .short("c") + .takes_value(true) + .multiple(true) + ) + .arg( + Arg::with_name("targets") + .help("Add specific targets on installation") + .long("target") + .short("t") + .takes_value(true) + .multiple(true) ), ) .subcommand( @@ -741,7 +757,15 @@ fn update(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<()> { let toolchain = cfg.get_toolchain(name, false)?; let status = if !toolchain.is_custom() { - Some(toolchain.install_from_dist(m.is_present("force"))?) + let components: Vec<_> = m + .values_of("components") + .map(|v| v.collect()) + .unwrap_or_else(Vec::new); + let targets: Vec<_> = m + .values_of("targets") + .map(|v| v.collect()) + .unwrap_or_else(Vec::new); + Some(toolchain.install_from_dist(m.is_present("force"), &components, &targets)?) } else if !toolchain.exists() { return Err(ErrorKind::InvalidToolchainName(toolchain.name().to_string()).into()); } else { diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 3dee0883204..77d60a166e4 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -45,11 +45,13 @@ use std::fs; use std::path::{Component, Path, PathBuf}; use std::process::{self, Command}; -pub struct InstallOpts { +pub struct InstallOpts<'a> { pub default_host_triple: String, pub default_toolchain: String, pub profile: String, pub no_modify_path: bool, + pub components: &'a [&'a str], + pub targets: &'a [&'a str], } #[cfg(feature = "no-self-update")] @@ -283,6 +285,8 @@ pub fn install(no_prompt: bool, verbose: bool, quiet: bool, mut opts: InstallOpt &opts.default_toolchain, &opts.profile, &opts.default_host_triple, + opts.components, + opts.targets, verbose, quiet, )?; @@ -737,6 +741,8 @@ fn maybe_install_rust( toolchain_str: &str, profile_str: &str, default_host_triple: &str, + components: &[&str], + targets: &[&str], verbose: bool, quiet: bool, ) -> Result<()> { @@ -754,7 +760,7 @@ fn maybe_install_rust( // Set host triple first as it will affect resolution of toolchain_str cfg.set_default_host_triple(default_host_triple)?; let toolchain = cfg.get_toolchain(toolchain_str, false)?; - let status = toolchain.install_from_dist(false)?; + let status = toolchain.install_from_dist(false, components, targets)?; cfg.set_default(toolchain_str)?; println!(); common::show_channel_update(&cfg, toolchain_str, Ok(status))?; diff --git a/src/cli/setup_mode.rs b/src/cli/setup_mode.rs index 301409c25a9..16713d38812 100644 --- a/src/cli/setup_mode.rs +++ b/src/cli/setup_mode.rs @@ -61,6 +61,22 @@ pub fn main() -> Result<()> { .possible_values(Profile::names()) .default_value(Profile::default_name()), ) + .arg( + Arg::with_name("components") + .help("Component name to also install") + .long("component") + .short("c") + .takes_value(true) + .multiple(true), + ) + .arg( + Arg::with_name("targets") + .help("Target name to also install") + .long("target") + .short("target") + .takes_value(true) + .multiple(true), + ) .arg( Arg::with_name("no-modify-path") .long("no-modify-path") @@ -81,11 +97,23 @@ pub fn main() -> Result<()> { .expect("Unreachable: Clap should supply a default"); let no_modify_path = matches.is_present("no-modify-path"); + let components: Vec<_> = matches + .values_of("components") + .map(|v| v.collect()) + .unwrap_or_else(Vec::new); + + let targets: Vec<_> = matches + .values_of("targets") + .map(|v| v.collect()) + .unwrap_or_else(Vec::new); + let opts = InstallOpts { default_host_triple: default_host, default_toolchain: default_toolchain.to_owned(), profile: profile.to_owned(), no_modify_path, + components: &components, + targets: &targets, }; self_update::install(no_prompt, verbose, quiet, opts)?; diff --git a/src/config.rs b/src/config.rs index bb065f94698..68c1364e82b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -304,7 +304,7 @@ impl Cfg { ErrorKind::OverrideToolchainNotInstalled(name.to_string()) }) } else { - toolchain.install_from_dist(false)?; + toolchain.install_from_dist(false, &[], &[])?; Ok(Some((toolchain, reason))) } } @@ -413,7 +413,7 @@ impl Cfg { // Update toolchains and collect the results let channels = channels.map(|(n, t)| { let t = t.and_then(|t| { - let t = t.install_from_dist(force_update); + let t = t.install_from_dist(force_update, &[], &[]); if let Err(ref e) = t { (self.notify_handler)(Notification::NonFatalError(e)); } @@ -465,7 +465,7 @@ impl Cfg { ) -> Result { let toolchain = self.get_toolchain(toolchain, false)?; if install_if_missing && !toolchain.exists() { - toolchain.install_from_dist(false)?; + toolchain.install_from_dist(false, &[], &[])?; } if let Some(cmd) = self.maybe_do_cargo_fallback(&toolchain, binary)? { diff --git a/src/dist/dist.rs b/src/dist/dist.rs index b789a12b521..0946d609e46 100644 --- a/src/dist/dist.rs +++ b/src/dist/dist.rs @@ -565,6 +565,8 @@ pub fn update_from_dist<'a>( prefix: &InstallPrefix, force_update: bool, old_date: Option<&str>, + components: &[&str], + targets: &[&str], ) -> Result> { let fresh_install = !prefix.path().exists(); let hash_exists = update_hash.map(Path::exists).unwrap_or(false); @@ -584,6 +586,8 @@ pub fn update_from_dist<'a>( prefix, force_update, old_date, + components, + targets, ); // Don't leave behind an empty / broken installation directory @@ -603,6 +607,8 @@ fn update_from_dist_<'a>( prefix: &InstallPrefix, force_update: bool, old_date: Option<&str>, + components: &[&str], + targets: &[&str], ) -> Result> { let mut toolchain = toolchain.clone(); let mut fetched = String::new(); @@ -633,6 +639,8 @@ fn update_from_dist_<'a>( profile, prefix, force_update, + components, + targets, &mut fetched, ) { Ok(v) => break Ok(v), @@ -642,6 +650,10 @@ fn update_from_dist_<'a>( } if let ErrorKind::RequestedComponentsUnavailable(components, ..) = e.kind() { + if fetched.is_empty() { + // We failed to fetch, perhaps because the components are bad, stop now. + break Err(e); + } (download.notify_handler)(Notification::SkippingNightlyMissingComponent( components, )); @@ -701,6 +713,8 @@ fn try_update_from_dist_<'a>( profile: Option, prefix: &InstallPrefix, force_update: bool, + components: &[&str], + targets: &[&str], fetched: &mut String, ) -> Result> { let toolchain_str = toolchain.to_string(); @@ -708,7 +722,15 @@ fn try_update_from_dist_<'a>( // TODO: Add a notification about which manifest version is going to be used (download.notify_handler)(Notification::DownloadingManifest(&toolchain_str)); - match dl_v2_manifest(download, update_hash, toolchain) { + match dl_v2_manifest( + download, + if components.is_empty() && targets.is_empty() { + update_hash + } else { + None + }, + toolchain, + ) { Ok(Some((m, hash))) => { (download.notify_handler)(Notification::DownloadedManifest( &m.date, @@ -720,11 +742,34 @@ fn try_update_from_dist_<'a>( None => Vec::new(), }; - let changes = Changes { + let mut changes = Changes { explicit_add_components: profile_components, remove_components: Vec::new(), }; + for component in components { + let mut component = crate::dist::manifest::Component::new( + component.to_string(), + Some(toolchain.target.clone()), + false, + ); + if let Some(renamed) = m.rename_component(&component) { + component = renamed; + } + changes.explicit_add_components.push(component); + } + + for target in targets { + let triple = TargetTriple::new(target); + changes + .explicit_add_components + .push(crate::dist::manifest::Component::new( + "rust-std".to_string(), + Some(triple), + false, + )); + } + *fetched = m.date.clone(); return match manifestation.update( diff --git a/src/install.rs b/src/install.rs index fee576112c8..17ffef170b7 100644 --- a/src/install.rs +++ b/src/install.rs @@ -28,6 +28,10 @@ pub enum InstallMethod<'a> { bool, // currently installed date Option<&'a str>, + // Extra components to install from dist + &'a [&'a str], + // Extra targets to install from dist + &'a [&'a str], ), } @@ -64,6 +68,8 @@ impl<'a> InstallMethod<'a> { force_update, exists, old_date, + components, + targets, ) => { let prefix = &InstallPrefix::from(path.to_owned()); let maybe_new_hash = dist::update_from_dist( @@ -74,6 +80,8 @@ impl<'a> InstallMethod<'a> { prefix, force_update, old_date, + components, + targets, )?; if let Some(hash) = maybe_new_hash { diff --git a/src/toolchain.rs b/src/toolchain.rs index 2a8a5070bb2..225e4e7f44c 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -162,7 +162,12 @@ impl<'a> Toolchain<'a> { } } - pub fn install_from_dist(&self, force_update: bool) -> Result { + pub fn install_from_dist( + &self, + force_update: bool, + components: &[&str], + targets: &[&str], + ) -> Result { let update_hash = self.update_hash()?; let old_date = self.get_manifest().ok().and_then(|m| m.map(|m| m.date)); self.install(InstallMethod::Dist( @@ -173,6 +178,8 @@ impl<'a> Toolchain<'a> { force_update, self.exists(), old_date.as_ref().map(|s| &**s), + components, + targets, )) } @@ -186,6 +193,8 @@ impl<'a> Toolchain<'a> { false, false, None, + &[], + &[], )) } pub fn is_custom(&self) -> bool { diff --git a/tests/cli-self-upd.rs b/tests/cli-self-upd.rs index 95424347d27..b41bb96cbd8 100644 --- a/tests/cli-self-upd.rs +++ b/tests/cli-self-upd.rs @@ -1258,3 +1258,32 @@ fn update_installs_clippy_cargo_and() { assert!(cargo_clippy_path.exists()); }); } + +#[test] +fn install_with_components_and_targets() { + setup(&|config| { + expect_ok( + config, + &[ + "rustup-init", + "--default-toolchain", + "nightly", + "-y", + "-c", + "rls", + "-t", + clitools::CROSS_ARCH1, + ], + ); + expect_stdout_ok( + config, + &["rustup", "target", "list"], + &format!("{} (installed)", clitools::CROSS_ARCH1), + ); + expect_stdout_ok( + config, + &["rustup", "component", "list"], + &format!("rls-{} (installed)", this_host_triple()), + ); + }) +} diff --git a/tests/cli-v2.rs b/tests/cli-v2.rs index eb1d0233c8e..6fbfa07d116 100644 --- a/tests/cli-v2.rs +++ b/tests/cli-v2.rs @@ -1109,3 +1109,34 @@ fn target_list_ignores_unavailable_targets() { expect_not_stdout_ok(config, target_list, clitools::CROSS_ARCH1); }) } + +#[test] +fn install_with_component_and_target() { + setup(&|config| { + expect_ok(config, &["rustup", "default", "nightly"]); + expect_ok( + config, + &[ + "rustup", + "toolchain", + "install", + "nightly", + "-c", + "rls", + "-t", + clitools::CROSS_ARCH1, + "--no-self-update", + ], + ); + expect_stdout_ok( + config, + &["rustup", "component", "list"], + &format!("rls-{} (installed)", this_host_triple()), + ); + expect_stdout_ok( + config, + &["rustup", "target", "list"], + &format!("{} (installed)", clitools::CROSS_ARCH1), + ); + }) +}