diff --git a/src/multirust-cli/cli.rs b/src/multirust-cli/cli.rs index f9c55e4113..29ae320064 100644 --- a/src/multirust-cli/cli.rs +++ b/src/multirust-cli/cli.rs @@ -188,7 +188,6 @@ r"Upgrades the ~/.multirust directory from previous versions. .subcommand( SubCommand::with_name("update") .about("Updates multirust.") - .arg(Arg::with_name("no-prompt").short("y").help("Disable confirmation prompt.")) ) ) .subcommand( diff --git a/src/multirust-cli/common.rs b/src/multirust-cli/common.rs index bf3aba448e..bb9e251b48 100644 --- a/src/multirust-cli/common.rs +++ b/src/multirust-cli/common.rs @@ -99,7 +99,6 @@ pub fn run_inner>(mut command: Command, } pub fn show_channel_version(cfg: &Cfg, name: &str) -> Result<()> { - println!(""); let mut t = term::stdout().unwrap(); if tty::stdout_isatty() { let _ = t.fg(term::color::BRIGHT_WHITE); } if tty::stdout_isatty() { let _ = t.bg(term::color::BLACK); } @@ -112,6 +111,48 @@ pub fn show_channel_version(cfg: &Cfg, name: &str) -> Result<()> { Ok(()) } +pub fn show_channel_update(cfg: &Cfg, name: &str, + updated: Result) -> Result<()> { + let tty = tty::stdout_isatty(); + let mut t = term::stdout().unwrap(); + match updated { + Ok(true) => { + if tty { let _ = t.fg(term::color::BRIGHT_GREEN); } + let _ = write!(t, "{} updated", name); + } + Ok(false) => { + if tty { let _ = t.fg(term::color::BRIGHT_WHITE); } + let _ = write!(t, "{} unchanged", name); + } + Err(_) => { + if tty { let _ = t.fg(term::color::BRIGHT_RED); } + let _ = write!(t, "{} update failed", name); + } + } + if tty {let _ = t.reset(); } + println!(":"); + println!(""); + try!(show_tool_versions(&try!(cfg.get_toolchain(&name, false)))); + println!(""); + Ok(()) +} + +pub fn update_all_channels(cfg: &Cfg) -> Result<()> { + let toolchains = try!(cfg.update_all_channels()); + + if toolchains.is_empty() { + info!("no updatable toolchains installed"); + return Ok(()); + } + + println!(""); + + for (name, result) in toolchains { + try!(show_channel_update(cfg, &name, result)); + } + Ok(()) +} + pub fn show_tool_versions(toolchain: &Toolchain) -> Result<()> { if toolchain.exists() { let rustc_path = toolchain.binary_file("rustc"); @@ -150,3 +191,49 @@ pub fn show_tool_versions(toolchain: &Toolchain) -> Result<()> { Ok(()) } +pub fn list_targets(toolchain: &Toolchain) -> Result<()> { + for component in try!(toolchain.list_components()) { + if component.component.pkg == "rust-std" { + if component.required { + println!("{} (default)", component.component.target); + } else if component.installed { + println!("{} (installed)", component.component.target); + } else { + println!("{}", component.component.target); + } + } + } + + Ok(()) +} + +pub fn list_toolchains(cfg: &Cfg) -> Result<()> { + let mut toolchains = try!(cfg.list_toolchains()); + + toolchains.sort(); + + if toolchains.is_empty() { + println!("no installed toolchains"); + } else { + for toolchain in toolchains { + println!("{}", &toolchain); + } + } + Ok(()) +} + +pub fn list_overrides(cfg: &Cfg) -> Result<()> { + let mut overrides = try!(cfg.override_db.list()); + + overrides.sort(); + + if overrides.is_empty() { + println!("no overrides"); + } else { + for o in overrides { + println!("{}", o); + } + } + Ok(()) +} + diff --git a/src/multirust-cli/log.rs b/src/multirust-cli/log.rs index 9a60eb822d..1555c8b8d0 100644 --- a/src/multirust-cli/log.rs +++ b/src/multirust-cli/log.rs @@ -36,7 +36,7 @@ pub fn err_fmt(args: fmt::Arguments) { pub fn info_fmt(args: fmt::Arguments) { let mut t = term::stderr().unwrap(); - if tty::stderr_isatty() { let _ = t.fg(term::color::BRIGHT_GREEN); } + if tty::stderr_isatty() { let _ = t.fg(term::color::BRIGHT_CYAN); } let _ = write!(t, "info: "); if tty::stderr_isatty() { let _ = t.reset(); } let _ = t.write_fmt(args); diff --git a/src/multirust-cli/main.rs b/src/multirust-cli/main.rs index 00edfcfc2d..c35463bc95 100644 --- a/src/multirust-cli/main.rs +++ b/src/multirust-cli/main.rs @@ -35,6 +35,7 @@ mod download_tracker; mod multirust_mode; mod proxy_mode; mod setup_mode; +mod rustup_mode; mod self_update; mod tty; mod job; @@ -64,10 +65,14 @@ fn run_multirust() -> Result<()> { .and_then(|a| a.file_stem()) .and_then(|a| a.to_str()); match name { + Some("rustup") => { + rustup_mode::main() + } Some("multirust") => { multirust_mode::main() } - Some(n) if n.starts_with("multirust-setup") => { + Some(n) if n.starts_with("multirust-setup")|| + n.starts_with("rustup-setup") => { // NB: The above check is only for the prefix of the file // name. Browsers rename duplicates to // e.g. multirust-setup(2), and this allows all variations diff --git a/src/multirust-cli/multirust_mode.rs b/src/multirust-cli/multirust_mode.rs index ea8c1c194d..efabb09f43 100644 --- a/src/multirust-cli/multirust_mode.rs +++ b/src/multirust-cli/multirust_mode.rs @@ -1,16 +1,13 @@ use clap::ArgMatches; use cli; -use common::{confirm, show_channel_version, +use common::{self, confirm, show_channel_version, set_globals, run_inner, - show_tool_versions}; + show_tool_versions, update_all_channels}; use multirust::*; use multirust_dist::manifest::Component; use self_update; use std::env; -use std::io::Write; -use std::iter; use std::path::{Path, PathBuf}; -use term; use job; pub fn main() -> Result<()> { @@ -39,8 +36,8 @@ pub fn main() -> Result<()> { ("override", Some(m)) => override_(&cfg, m), ("show-default", Some(_)) => show_default(&cfg), ("show-override", Some(_)) => show_override(&cfg), - ("list-overrides", Some(_)) => list_overrides(&cfg), - ("list-toolchains", Some(_)) => list_toolchains(&cfg), + ("list-overrides", Some(_)) => common::list_overrides(&cfg), + ("list-toolchains", Some(_)) => common::list_toolchains(&cfg), ("remove-override", Some(m)) => remove_override(&cfg, m), ("remove-toolchain", Some(m)) => remove_toolchain_args(&cfg, m), ("list-targets", Some(m)) => list_targets(&cfg, m), @@ -127,7 +124,7 @@ fn remove_toolchain_args(cfg: &Cfg, m: &ArgMatches) -> Result<()> { try!(get_toolchain(cfg, m, false)).remove() } -fn default_(cfg: &Cfg, m: &ArgMatches) -> Result<()> { +pub fn default_(cfg: &Cfg, m: &ArgMatches) -> Result<()> { let toolchain = try!(get_toolchain(cfg, m, true)); if !try!(common_install_args(&toolchain, m)) { if !toolchain.is_custom() { @@ -137,8 +134,8 @@ fn default_(cfg: &Cfg, m: &ArgMatches) -> Result<()> { } } - try!(toolchain.make_default()); + println!(""); try!(show_channel_version(cfg, toolchain.name())); Ok(()) @@ -149,11 +146,12 @@ fn update(cfg: &Cfg, m: &ArgMatches) -> Result<()> { let toolchain = try!(cfg.get_toolchain(name, true)); if !try!(common_install_args(&toolchain, m)) { if !toolchain.is_custom() { - try!(toolchain.install_from_dist()) + try!(toolchain.install_from_dist()); } else if !toolchain.exists() { return Err(Error::ToolchainNotInstalled(toolchain.name().to_string())); } } + println!(""); try!(show_channel_version(cfg, name)); } else { try!(update_all_channels(cfg)) @@ -170,6 +168,7 @@ fn override_(cfg: &Cfg, m: &ArgMatches) -> Result<()> { } try!(toolchain.make_override(&try!(utils::current_dir()))); + println!(""); try!(show_channel_version(cfg, toolchain.name())); Ok(()) } @@ -286,92 +285,10 @@ fn show_override(cfg: &Cfg) -> Result<()> { Ok(()) } -fn list_overrides(cfg: &Cfg) -> Result<()> { - let mut overrides = try!(cfg.override_db.list()); - - overrides.sort(); - - if overrides.is_empty() { - println!("no overrides"); - } else { - for o in overrides { - println!("{}", o); - } - } - Ok(()) -} - -fn list_toolchains(cfg: &Cfg) -> Result<()> { - let mut toolchains = try!(cfg.list_toolchains()); - - toolchains.sort(); - - if toolchains.is_empty() { - println!("no installed toolchains"); - } else { - for toolchain in toolchains { - println!("{}", &toolchain); - } - } - Ok(()) -} - -fn update_all_channels(cfg: &Cfg) -> Result<()> { - let toolchains = try!(cfg.update_all_channels()); - - let max_name_length = toolchains.iter().map(|&(ref n, _)| n.len()).max().unwrap_or(0); - let padding_str: String = iter::repeat(' ').take(max_name_length).collect(); - - println!(""); - let mut t = term::stdout().unwrap(); - for &(ref name, ref result) in &toolchains { - let _ = t.fg(term::color::BRIGHT_WHITE); - let _ = t.bg(term::color::BLACK); - let _ = write!(t, - "{}{}", - &padding_str[0..(max_name_length - name.len())], - name); - let _ = t.reset(); - let _ = write!(t, " update "); - if result.is_ok() { - let _ = t.fg(term::color::BRIGHT_GREEN); - let _ = writeln!(t, "succeeded"); - let _ = t.reset(); - } else { - let _ = t.fg(term::color::BRIGHT_RED); - let _ = writeln!(t, "FAILED"); - let _ = t.reset(); - } - } - println!(""); - - for (name, _) in toolchains { - let _ = t.fg(term::color::BRIGHT_WHITE); - let _ = t.bg(term::color::BLACK); - let _ = write!(t, "{}", name); - let _ = t.reset(); - let _ = writeln!(t, " revision:"); - println!(""); - try!(show_tool_versions(&try!(cfg.get_toolchain(&name, false)))); - println!(""); - } - Ok(()) -} - fn list_targets(cfg: &Cfg, m: &ArgMatches) -> Result<()> { let toolchain = m.value_of("toolchain").unwrap(); let toolchain = try!(cfg.get_toolchain(toolchain, false)); - for component in try!(toolchain.list_components()) { - if component.component.pkg == "rust-std" { - if component.required { - println!("{} (default)", component.component.target); - } else if component.installed { - println!("{} (installed)", component.component.target); - } else { - println!("{}", component.component.target); - } - } - } + try!(common::list_targets(&toolchain)); Ok(()) } diff --git a/src/multirust-cli/rustup_mode.rs b/src/multirust-cli/rustup_mode.rs new file mode 100644 index 0000000000..289ded3c53 --- /dev/null +++ b/src/multirust-cli/rustup_mode.rs @@ -0,0 +1,285 @@ +use clap::{App, Arg, AppSettings, SubCommand, ArgMatches}; +use common; +use multirust::{Result, Cfg, Error}; +use multirust_dist::manifest::Component; +use multirust_utils::utils; +use self_update; +use std::path::Path; + +pub fn main() -> Result<()> { + try!(::self_update::cleanup_self_updater()); + + let matches = cli().get_matches(); + let verbose = matches.is_present("verbose"); + let ref cfg = try!(common::set_globals(verbose)); + + match matches.subcommand() { + ("default", Some(m)) => try!(default_(cfg, m)), + ("update", Some(m)) => try!(update(cfg, m)), + ("run", Some(m)) => try!(run(cfg, m)), + ("target", Some(c)) => { + match c.subcommand() { + ("list", Some(_)) => try!(target_list(cfg)), + ("add", Some(m)) => try!(target_add(cfg, m)), + ("remove", Some(m)) => try!(target_remove(cfg, m)), + (_, _) => unreachable!(), + } + } + ("toolchain", Some(c)) => { + match c.subcommand() { + ("list", Some(_)) => try!(common::list_toolchains(cfg)), + ("link", Some(m)) => try!(toolchain_link(cfg, m)), + ("remove", Some(m)) => try!(toolchain_remove(cfg, m)), + (_, _) => unreachable!(), + } + } + ("override", Some(c)) => { + match c.subcommand() { + ("list", Some(_)) => try!(common::list_overrides(cfg)), + ("add", Some(m)) => try!(override_add(cfg, m)), + ("remove", Some(_)) => try!(override_remove(cfg)), + (_ ,_) => unreachable!(), + } + } + ("doc", Some(m)) => try!(doc(cfg, m)), + ("self", Some(c)) => { + match c.subcommand() { + ("update", Some(_)) => try!(self_update::update()), + ("uninstall", Some(m)) => try!(self_uninstall(m)), + ("upgrade-data", Some(_)) => try!(cfg.upgrade_data()), + (_ ,_) => unreachable!(), + } + } + (_, _) => { + try!(update_all_channels(cfg)); + } + } + + Ok(()) +} + +pub fn cli() -> App<'static, 'static> { + App::new("rustup") + .version("0.0.5") + .author("Diggory Blake") + .about("The Rust toolchain installer") + .setting(AppSettings::VersionlessSubcommands) + .setting(AppSettings::DeriveDisplayOrder) + .arg(Arg::with_name("verbose") + .short("v") + .long("verbose") + .help("Enable verbose output")) + .subcommand(SubCommand::with_name("default") + .about("Set the default toolchain") + .arg(Arg::with_name("toolchain") + .required(true))) + .subcommand(SubCommand::with_name("update") + .about("Install or update a toolchain from a Rust distribution channel") + .arg(Arg::with_name("toolchain") + .required(false))) + .subcommand(SubCommand::with_name("run") + .about("Run a command with an environment configured for a given toolchain") + .setting(AppSettings::TrailingVarArg) + .arg(Arg::with_name("toolchain") + .required(true)) + .arg(Arg::with_name("command") + .required(true).multiple(true))) + .subcommand(SubCommand::with_name("target") + .about("Modify a toolchain's supported targets") + .subcommand(SubCommand::with_name("list") + .about("List installed and available targets")) + .subcommand(SubCommand::with_name("add") + .about("Add a target to a Rust toolchain") + .arg(Arg::with_name("target") + .required(true))) + .subcommand(SubCommand::with_name("remove") + .about("Remove a target from a Rust toolchain") + .arg(Arg::with_name("target") + .required(true)) + .arg(Arg::with_name("toolchain")))) + .subcommand(SubCommand::with_name("toolchain") + .about("Modify the installed toolchains") + .subcommand(SubCommand::with_name("list") + .about("List installed toolchains")) + .subcommand(SubCommand::with_name("link") + .about("Create a custom toolchain by symlinking to a directory") + .arg(Arg::with_name("toolchain") + .required(true)) + .arg(Arg::with_name("path") + .required(true))) + .subcommand(SubCommand::with_name("remove") + .about("Uninstall a toolchain") + .arg(Arg::with_name("toolchain") + .required(true)))) + .subcommand(SubCommand::with_name("override") + .about("Modify directory toolchain overrides") + .subcommand(SubCommand::with_name("list") + .about("List directory toolchain overrides")) + .subcommand(SubCommand::with_name("add") + .about("Set the override toolchain for a directory") + .arg(Arg::with_name("toolchain") + .required(true))) + .subcommand(SubCommand::with_name("remove") + .about("Remove the override toolchain for a directory"))) + .subcommand(SubCommand::with_name("self") + .about("Modify the rustup installation") + .subcommand(SubCommand::with_name("update") + .about("Downloadand and install updates to rustup")) + .subcommand(SubCommand::with_name("uninstall") + .about("Uninstall rustup.") + .arg(Arg::with_name("no-prompt") + .short("y"))) + .subcommand(SubCommand::with_name("upgrade-data") + .about("Upgrade the internal data format."))) +} + +fn update_all_channels(cfg: &Cfg) -> Result<()> { + common::update_all_channels(cfg) +} + +fn default_(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let ref toolchain = m.value_of("toolchain").expect(""); + let ref toolchain = try!(cfg.get_toolchain(toolchain, false)); + + if !toolchain.is_custom() { + try!(toolchain.install_from_dist_if_not_installed()); + } else if !toolchain.exists() { + return Err(Error::ToolchainNotInstalled(toolchain.name().to_string())); + } + + try!(toolchain.make_default()); + println!(""); + try!(common::show_channel_version(cfg, toolchain.name())); + + Ok(()) +} + +fn update(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let toolchain = if let Some(name) = m.value_of("toolchain") { + try!(cfg.get_toolchain(name, false)) + } else { + let ref cwd = try!(utils::current_dir()); + let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd)); + toolchain + }; + + let updated = if !toolchain.is_custom() { + try!(toolchain.install_from_dist()) + } else if !toolchain.exists() { + return Err(Error::ToolchainNotInstalled(toolchain.name().to_string())); + } else { + false + }; + + println!(""); + try!(common::show_channel_update(cfg, toolchain.name(), Ok(updated))); + + Ok(()) +} + +fn run(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let ref toolchain = m.value_of("toolchain").expect(""); + let ref toolchain = try!(cfg.get_toolchain(toolchain, false)); + let args = m.values_of("command").unwrap(); + let args: Vec<_> = args.collect(); + let cmd = try!(toolchain.create_command(args[0])); + + common::run_inner(cmd, &args) +} + +fn target_list(cfg: &Cfg) -> Result<()> { + let ref cwd = try!(utils::current_dir()); + let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd)); + + common::list_targets(&toolchain) +} + +fn target_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let target = m.value_of("target").expect(""); + let ref cwd = try!(utils::current_dir()); + let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd)); + let new_component = Component { + pkg: "rust-std".to_string(), + target: target.to_string(), + }; + + toolchain.add_component(new_component) +} + +fn target_remove(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let target = m.value_of("target").expect(""); + let ref cwd = try!(utils::current_dir()); + let (toolchain, _) = try!(cfg.toolchain_for_dir(cwd)); + let new_component = Component { + pkg: "rust-std".to_string(), + target: target.to_string(), + }; + + toolchain.remove_component(new_component) +} + +fn toolchain_link(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let ref toolchain = m.value_of("toolchain").expect(""); + let ref path = m.value_of("path").expect(""); + let toolchain = try!(cfg.get_toolchain(toolchain, true)); + + toolchain.install_from_dir(Path::new(path), true) +} + +fn toolchain_remove(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let ref toolchain = m.value_of("toolchain").expect(""); + let toolchain = try!(cfg.get_toolchain(toolchain, false)); + + toolchain.remove() +} + +fn override_add(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let ref toolchain = m.value_of("toolchain").expect(""); + let toolchain = try!(cfg.get_toolchain(toolchain, false)); + + if !toolchain.is_custom() { + try!(toolchain.install_from_dist_if_not_installed()); + } else if !toolchain.exists() { + return Err(Error::ToolchainNotInstalled(toolchain.name().to_string())); + } + + try!(toolchain.make_override(&try!(utils::current_dir()))); + println!(""); + try!(common::show_channel_version(cfg, toolchain.name())); + + Ok(()) +} + +fn override_remove(cfg: &Cfg) -> Result<()> { + let ref path = try!(utils::current_dir()); + + let ref override_db = cfg.override_db; + let notify_handler = cfg.notify_handler.as_ref(); + + if try!(override_db.find(path, notify_handler)).is_none() { + info!("no override toolchain for '{}'", path.display()); + return Ok(()); + } + + try!(override_db.remove(path, &cfg.temp_cfg, notify_handler)); + info!("override toolchain for '{}' removed", path.display()); + Ok(()) +} + +fn doc(cfg: &Cfg, m: &ArgMatches) -> Result<()> { + let doc_url = if m.is_present("book") { + "book/index.html" + } else if m.is_present("std") { + "std/index.html" + } else { + "index.html" + }; + + cfg.open_docs_for_dir(&try!(utils::current_dir()), doc_url) +} + +fn self_uninstall(m: &ArgMatches) -> Result<()> { + let no_prompt = m.is_present("no-prompt"); + + self_update::uninstall(no_prompt) +} diff --git a/src/multirust-cli/self_update.rs b/src/multirust-cli/self_update.rs index 7570c09d0e..098a518abe 100644 --- a/src/multirust-cli/self_update.rs +++ b/src/multirust-cli/self_update.rs @@ -139,7 +139,7 @@ Continue? (y/N)" } static TOOLS: &'static [&'static str] - = &["rustc", "rustdoc", "cargo", "rust-lldb", "rust-gdb"]; + = &["rustup", "rustc", "rustdoc", "cargo", "rust-lldb", "rust-gdb"]; static UPDATE_ROOT: &'static str = "https://github.com/Diggsey/multirust-rs-binaries/raw/master"; @@ -296,6 +296,7 @@ fn maybe_install_rust_stable(verbose: bool) -> Result<()> { let stable = try!(cfg.get_toolchain("stable", false)); try!(stable.install_from_dist()); try!(cfg.set_default("stable")); + println!(""); try!(common::show_channel_version(cfg, "stable")); } else { info!("updating existing installation"); diff --git a/src/multirust-mock/src/clitools.rs b/src/multirust-mock/src/clitools.rs index 155efedf8a..efd07532df 100644 --- a/src/multirust-mock/src/clitools.rs +++ b/src/multirust-mock/src/clitools.rs @@ -61,11 +61,15 @@ pub fn setup(s: Scenario, f: &Fn(&Config)) { let setup_path = config.exedir.path().join(format!("multirust-setup{}", EXE_SUFFIX)); let rustc_path = config.exedir.path().join(format!("rustc{}", EXE_SUFFIX)); let cargo_path = config.exedir.path().join(format!("cargo{}", EXE_SUFFIX)); + let rustup_setup_path = config.exedir.path().join(format!("rustup-setup{}", EXE_SUFFIX)); + let rustup_path = config.exedir.path().join(format!("rustup{}", EXE_SUFFIX)); fs::copy(multirust_build_path, multirust_path).unwrap(); fs::hard_link(multirust_path, rustc_path).unwrap(); fs::hard_link(multirust_path, setup_path).unwrap(); fs::hard_link(multirust_path, cargo_path).unwrap(); + fs::hard_link(multirust_path, rustup_setup_path).unwrap(); + fs::hard_link(multirust_path, rustup_path).unwrap(); // Create some custom toolchains create_custom_toolchains(config.customdir.path()); diff --git a/src/multirust-utils/src/utils.rs b/src/multirust-utils/src/utils.rs index fbb16277ce..0a0e4205ba 100644 --- a/src/multirust-utils/src/utils.rs +++ b/src/multirust-utils/src/utils.rs @@ -427,7 +427,7 @@ pub fn cargo_home() -> Result { }; let cwd = try!(env::current_dir().map_err(|_| Error::CargoHome)); - let cargo_home = env_var.map(|home| { + let cargo_home = env_var.clone().map(|home| { cwd.join(home) }); let user_home = home_dir().map(|p| p.join(".cargo")); diff --git a/src/multirust/config.rs b/src/multirust/config.rs index 1f274a397f..574ccf6e8c 100644 --- a/src/multirust/config.rs +++ b/src/multirust/config.rs @@ -261,25 +261,51 @@ impl Cfg { } } - pub fn update_all_channels(&self) -> Result)>> { - let mut toolchains = try!(self.list_toolchains()); - toolchains.sort(); - - Ok(toolchains.into_iter() - .merge(["beta", "nightly", "stable"].into_iter().map(|s| (*s).to_owned())) - .dedup() - .filter(|name| { - dist::ToolchainDesc::from_str(&name).map(|d| d.is_tracking()).ok() == Some(true) - }) - .map(|name| { - let result = self.get_toolchain(&name, true) - .and_then(|t| t.install_from_dist()); - if let Err(ref e) = result { - self.notify_handler.call(Notification::NonFatalError(e)); - } - (name, result) - }) - .collect()) + pub fn update_all_channels(&self) -> Result)>> { + let toolchains = try!(self.list_toolchains()); + + let mut toolchains: Vec<(dist::ToolchainDesc, String)> = toolchains.into_iter() + .filter_map(|name| { + let desc = dist::ToolchainDesc::from_str(&name); + let tracked = desc.into_iter().filter(|d| d.is_tracking()).next(); + tracked.map(|d| (d, name)) + }).collect(); + + fn channel_sort_key(s: &str) -> String { + if s == "stable" { + String::from("0") + } else if s == "beta" { + String::from("1") + } else if s == "nightly" { + String::from("2") + } else { + format!("3{}", s) + } + } + + toolchains.sort_by(|&(ref a, _), &(ref b, _)| { + let a = format!("{}-{}-{}", + channel_sort_key(&a.channel), + a.date.as_ref().map(String::as_str).unwrap_or(""), + a.target_triple()); + let b = format!("{}-{}-{}", + channel_sort_key(&b.channel), + b.date.as_ref().map(String::as_str).unwrap_or(""), + b.target_triple()); + a.cmp(&b) + }); + + let updates = toolchains.into_iter() + .map(|(_, name)| { + let result = self.get_toolchain(&name, true) + .and_then(|t| t.install_from_dist()); + if let Err(ref e) = result { + self.notify_handler.call(Notification::NonFatalError(e)); + } + (name, result) + }).collect(); + + Ok(updates) } pub fn check_metadata_version(&self) -> Result<()> { diff --git a/src/multirust/toolchain.rs b/src/multirust/toolchain.rs index 82deea6606..5412a8b394 100644 --- a/src/multirust/toolchain.rs +++ b/src/multirust/toolchain.rs @@ -68,7 +68,7 @@ impl<'a> Toolchain<'a> { } Ok(try!(result)) } - fn install(&self, install_method: InstallMethod) -> Result<()> { + fn install(&self, install_method: InstallMethod) -> Result { assert!(self.is_valid_install_method(install_method)); if self.exists() { self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name)); @@ -87,13 +87,14 @@ impl<'a> Toolchain<'a> { self.cfg.notify_handler.call(Notification::InstalledToolchain(&self.name)); } - Ok(()) + Ok(updated) } fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> { assert!(self.is_valid_install_method(install_method)); self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name)); if !self.exists() { - self.install(install_method) + try!(self.install(install_method)); + Ok(()) } else { self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name)); Ok(()) @@ -123,7 +124,7 @@ impl<'a> Toolchain<'a> { } } - pub fn install_from_dist(&self) -> Result<()> { + pub fn install_from_dist(&self) -> Result { let update_hash = try!(self.update_hash()); self.install(InstallMethod::Dist(&self.name, update_hash.as_ref().map(|p| &**p), @@ -202,10 +203,12 @@ impl<'a> Toolchain<'a> { try!(self.ensure_custom()); if link { - self.install(InstallMethod::Link(&try!(utils::to_absolute(src)))) + try!(self.install(InstallMethod::Link(&try!(utils::to_absolute(src))))); } else { - self.install(InstallMethod::Copy(src)) + try!(self.install(InstallMethod::Copy(src))); } + + Ok(()) } pub fn create_command>(&self, binary: T) -> Result { diff --git a/tests/cli-rustup.rs b/tests/cli-rustup.rs new file mode 100644 index 0000000000..c9fda3da6b --- /dev/null +++ b/tests/cli-rustup.rs @@ -0,0 +1,273 @@ +//! Test cases for new rustup UI + +extern crate multirust_dist; +extern crate multirust_utils; +extern crate multirust_mock; +extern crate tempdir; + +use multirust_mock::clitools::{self, Config, Scenario, + expect_ok, expect_ok_ex, + expect_stdout_ok, + set_current_dist_date}; + +pub fn setup(f: &Fn(&Config)) { + clitools::setup(Scenario::ArchivesV2, &|config| { + f(config); + }); +} + +#[test] +fn rustup_stable() { + setup(&|config| { + set_current_dist_date(config, "2015-01-01"); + expect_ok(config, &["rustup-setup", "-y"]); + set_current_dist_date(config, "2015-01-02"); + expect_ok_ex(config, &["rustup"], +r" +stable updated: + +1.1.0 (hash-s-2) +1.1.0 (hash-s-2) + +", +r"info: updating existing install for 'stable' +info: downloading toolchain manifest +info: downloading component 'rust-std' +info: downloading component 'rustc' +info: downloading component 'cargo' +info: downloading component 'rust-docs' +info: installing component 'rust-std' +info: installing component 'rustc' +info: installing component 'cargo' +info: installing component 'rust-docs' +info: toolchain 'stable' installed +"); + }); +} + +#[test] +fn rustup_stable_no_change() { + setup(&|config| { + set_current_dist_date(config, "2015-01-01"); + expect_ok(config, &["rustup-setup", "-y"]); + expect_ok_ex(config, &["rustup"], +r" +stable unchanged: + +1.0.0 (hash-s-1) +1.0.0 (hash-s-1) + +", +r"info: updating existing install for 'stable' +info: downloading toolchain manifest +info: toolchain is already up to date +"); + }); +} + +#[test] +fn rustup_all_channels() { + setup(&|config| { + set_current_dist_date(config, "2015-01-01"); + expect_ok(config, &["rustup-setup", "-y"]); + expect_ok(config, &["multirust", "update", "beta"]); + expect_ok(config, &["multirust", "update", "nightly"]); + set_current_dist_date(config, "2015-01-02"); + expect_ok_ex(config, &["rustup"], +r" +stable updated: + +1.1.0 (hash-s-2) +1.1.0 (hash-s-2) + +beta updated: + +1.2.0 (hash-b-2) +1.2.0 (hash-b-2) + +nightly updated: + +1.3.0 (hash-n-2) +1.3.0 (hash-n-2) + +", +r"info: updating existing install for 'stable' +info: downloading toolchain manifest +info: downloading component 'rust-std' +info: downloading component 'rustc' +info: downloading component 'cargo' +info: downloading component 'rust-docs' +info: installing component 'rust-std' +info: installing component 'rustc' +info: installing component 'cargo' +info: installing component 'rust-docs' +info: toolchain 'stable' installed +info: updating existing install for 'beta' +info: downloading toolchain manifest +info: downloading component 'rust-std' +info: downloading component 'rustc' +info: downloading component 'cargo' +info: downloading component 'rust-docs' +info: installing component 'rust-std' +info: installing component 'rustc' +info: installing component 'cargo' +info: installing component 'rust-docs' +info: toolchain 'beta' installed +info: updating existing install for 'nightly' +info: downloading toolchain manifest +info: downloading component 'rust-std' +info: downloading component 'rustc' +info: downloading component 'cargo' +info: downloading component 'rust-docs' +info: installing component 'rust-std' +info: installing component 'rustc' +info: installing component 'cargo' +info: installing component 'rust-docs' +info: toolchain 'nightly' installed +"); + }) +} + +#[test] +fn rustup_some_channels_up_to_date() { + setup(&|config| { + set_current_dist_date(config, "2015-01-01"); + expect_ok(config, &["rustup-setup", "-y"]); + expect_ok(config, &["multirust", "update", "beta"]); + expect_ok(config, &["multirust", "update", "nightly"]); + set_current_dist_date(config, "2015-01-02"); + expect_ok(config, &["multirust", "update", "beta"]); + expect_ok_ex(config, &["rustup"], +r" +stable updated: + +1.1.0 (hash-s-2) +1.1.0 (hash-s-2) + +beta unchanged: + +1.2.0 (hash-b-2) +1.2.0 (hash-b-2) + +nightly updated: + +1.3.0 (hash-n-2) +1.3.0 (hash-n-2) + +", +r"info: updating existing install for 'stable' +info: downloading toolchain manifest +info: downloading component 'rust-std' +info: downloading component 'rustc' +info: downloading component 'cargo' +info: downloading component 'rust-docs' +info: installing component 'rust-std' +info: installing component 'rustc' +info: installing component 'cargo' +info: installing component 'rust-docs' +info: toolchain 'stable' installed +info: updating existing install for 'beta' +info: downloading toolchain manifest +info: toolchain is already up to date +info: updating existing install for 'nightly' +info: downloading toolchain manifest +info: downloading component 'rust-std' +info: downloading component 'rustc' +info: downloading component 'cargo' +info: downloading component 'rust-docs' +info: installing component 'rust-std' +info: installing component 'rustc' +info: installing component 'cargo' +info: installing component 'rust-docs' +info: toolchain 'nightly' installed +"); + }) +} + +#[test] +fn rustup_no_channels() { + setup(&|config| { + expect_ok(config, &["rustup-setup", "-y"]); + expect_ok(config, &["multirust", "remove-toolchain", "stable"]); + expect_ok_ex(config, &["rustup"], +r"", +r"info: no updatable toolchains installed +"); + }) +} + +#[test] +fn default() { + setup(&|config| { + expect_ok_ex(config, &["rustup", "default", "nightly"], +r" +nightly revision: + +1.3.0 (hash-n-2) +1.3.0 (hash-n-2) + +", +r"info: installing toolchain 'nightly' +info: downloading toolchain manifest +info: downloading component 'rust-std' +info: downloading component 'rustc' +info: downloading component 'cargo' +info: downloading component 'rust-docs' +info: installing component 'rust-std' +info: installing component 'rustc' +info: installing component 'cargo' +info: installing component 'rust-docs' +info: toolchain 'nightly' installed +info: default toolchain set to 'nightly' +"); + }); +} + +#[test] +fn add_target() { + setup(&|config| { + let path = format!("toolchains/nightly/lib/rustlib/{}/lib/libstd.rlib", + clitools::CROSS_ARCH1); + expect_ok(config, &["rustup", "default", "nightly"]); + expect_ok(config, &["rustup", "target", "add", + clitools::CROSS_ARCH1]); + assert!(config.homedir.path().join(path).exists()); + }); +} + +#[test] +fn remove_target() { + setup(&|config| { + let ref path = format!("toolchains/nightly/lib/rustlib/{}/lib/libstd.rlib", + clitools::CROSS_ARCH1); + expect_ok(config, &["rustup", "default", "nightly"]); + expect_ok(config, &["rustup", "target", "add", + clitools::CROSS_ARCH1]); + assert!(config.homedir.path().join(path).exists()); + expect_ok(config, &["rustup", "target", "remove", + clitools::CROSS_ARCH1]); + assert!(!config.homedir.path().join(path).exists()); + }); +} + +#[test] +fn list_targets() { + setup(&|config| { + expect_ok(config, &["rustup", "default", "nightly"]); + expect_stdout_ok(config, &["rustup", "target", "list"], + clitools::CROSS_ARCH1); + }); +} + +#[test] +fn link() { + setup(&|config| { + let path = config.customdir.path().join("custom-1"); + let path = path.to_string_lossy(); + expect_ok(config, &["rustup", "toolchain", "link", "custom", + &path]); + expect_ok(config, &["rustup", "default", "custom"]); + expect_stdout_ok(config, &["rustc", "--version"], + "hash-c-1"); + }); +} diff --git a/tests/cli-v1.rs b/tests/cli-v1.rs index b8de7817d9..cb4141925a 100644 --- a/tests/cli-v1.rs +++ b/tests/cli-v1.rs @@ -415,45 +415,6 @@ fn update_on_channel_when_date_has_changed() { }); } -#[test] -fn update_no_toolchain_means_update_all_toolchains() { - clitools::setup(Scenario::ArchivesV1, &|config| { - set_current_dist_date(config, "2015-01-01"); - expect_ok(config, &["multirust", "update"]); - - expect_stderr_ok(config, &["multirust", "default", "nightly"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-n-1"); - expect_stderr_ok(config, &["multirust", "default", "beta"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-b-1"); - expect_stderr_ok(config, &["multirust", "default", "stable"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-s-1"); - - set_current_dist_date(config, "2015-01-02"); - expect_stderr_ok(config, &["multirust", "update", "nightly"], - "updating existing"); - expect_ok(config, &["multirust", "update"]); - - expect_stderr_ok(config, &["multirust", "default", "nightly"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-n-2"); - expect_stderr_ok(config, &["multirust", "default", "beta"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-b-2"); - expect_stderr_ok(config, &["multirust", "default", "stable"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-s-2"); - }); -} - #[test] fn run_command() { setup(&|config| { diff --git a/tests/cli-v2.rs b/tests/cli-v2.rs index 2c556e7e79..e1a7370419 100644 --- a/tests/cli-v2.rs +++ b/tests/cli-v2.rs @@ -418,45 +418,6 @@ fn update_on_channel_when_date_has_changed() { }); } -#[test] -fn update_no_toolchain_means_update_all_toolchains() { - clitools::setup(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - expect_ok(config, &["multirust", "update"]); - - expect_stderr_ok(config, &["multirust", "default", "nightly"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-n-1"); - expect_stderr_ok(config, &["multirust", "default", "beta"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-b-1"); - expect_stderr_ok(config, &["multirust", "default", "stable"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-s-1"); - - set_current_dist_date(config, "2015-01-02"); - expect_stderr_ok(config, &["multirust", "update", "nightly"], - "updating existing"); - expect_ok(config, &["multirust", "update"]); - - expect_stderr_ok(config, &["multirust", "default", "nightly"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-n-2"); - expect_stderr_ok(config, &["multirust", "default", "beta"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-b-2"); - expect_stderr_ok(config, &["multirust", "default", "stable"], - "using existing"); - expect_stdout_ok(config, &["rustc", "--version"], - "hash-s-2"); - }); -} - #[test] fn run_command() { setup(&|config| {