diff --git a/src/cli/errors.rs b/src/cli/errors.rs index 66a80344ee..2921c805db 100644 --- a/src/cli/errors.rs +++ b/src/cli/errors.rs @@ -49,5 +49,12 @@ error_chain! { description("completion script for shell not yet supported for tool") display("{} does not currently support completions for {}", cmd, shell) } + TargetAllSpecifiedWithTargets(t: Vec) { + description( + "the `all` target, which installs all available targets, \ + cannot be combined with other targets" + ) + display("`rustup target add {}` includes `all`", t.join(" ")) + } } } diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 2a17c7c2e7..63a6da7047 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -275,7 +275,15 @@ pub fn cli() -> App<'static, 'static> { SubCommand::with_name("add") .about("Add a target to a Rust toolchain") .alias("install") - .arg(Arg::with_name("target").required(true).multiple(true)) + .arg( + Arg::with_name("target") + .required(true) + .multiple(true) + .help( + "List of targets to install; \ + \"all\" installs all available targets" + ) + ) .arg( Arg::with_name("toolchain") .help(TOOLCHAIN_ARG_HELP) @@ -851,9 +859,35 @@ fn target_list(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<()> { fn target_add(cfg: &Cfg, m: &ArgMatches<'_>) -> Result<()> { let toolchain = explicit_or_dir_toolchain(cfg, m)?; - for target in m.values_of("target").expect("") { - let new_component = Component::new("rust-std".to_string(), Some(TargetTriple::new(target))); + let mut targets: Vec = m + .values_of("target") + .expect("") + .map(ToString::to_string) + .collect(); + + if targets.contains(&"all".to_string()) { + if targets.len() != 1 { + return Err(ErrorKind::TargetAllSpecifiedWithTargets(targets).into()); + } + targets.clear(); + for component in toolchain.list_components()? { + if component.component.short_name_in_manifest() == "rust-std" + && component.available + && !component.installed + { + let target = component + .component + .target + .as_ref() + .expect("rust-std should have a target"); + targets.push(target.to_string()); + } + } + } + + for target in &targets { + let new_component = Component::new("rust-std".to_string(), Some(TargetTriple::new(target))); toolchain.add_component(new_component)?; } diff --git a/tests/cli-v2.rs b/tests/cli-v2.rs index 77e1c38e8d..a9435d8138 100644 --- a/tests/cli-v2.rs +++ b/tests/cli-v2.rs @@ -555,7 +555,7 @@ fn list_installed_targets() { } #[test] -fn add_target() { +fn add_target1() { setup(&|config| { expect_ok(config, &["rustup", "default", "nightly"]); expect_ok(config, &["rustup", "target", "add", clitools::CROSS_ARCH1]); @@ -568,6 +568,63 @@ fn add_target() { }); } +#[test] +fn add_target2() { + setup(&|config| { + expect_ok(config, &["rustup", "default", "nightly"]); + expect_ok(config, &["rustup", "target", "add", clitools::CROSS_ARCH2]); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH2 + ); + assert!(config.rustupdir.join(path).exists()); + }); +} + +#[test] +fn add_all_targets() { + setup(&|config| { + expect_ok(config, &["rustup", "default", "nightly"]); + expect_ok(config, &["rustup", "target", "add", "all"]); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(config.rustupdir.join(path).exists()); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH2 + ); + assert!(config.rustupdir.join(path).exists()); + }); +} + +#[test] +fn add_all_targets_fail() { + setup(&|config| { + expect_ok(config, &["rustup", "default", "nightly"]); + expect_err( + config, + &[ + "rustup", + "target", + "add", + clitools::CROSS_ARCH1, + "all", + clitools::CROSS_ARCH2, + ], + &format!( + "`rustup target add {} all {}` includes `all`", + clitools::CROSS_ARCH1, + clitools::CROSS_ARCH2 + ), + ); + }); +} + #[test] fn add_target_no_toolchain() { setup(&|config| {