Skip to content

Commit

Permalink
Output the previous version of a toolchain when it is updated
Browse files Browse the repository at this point in the history
This change modifies the cli output of `rustup update` to display the
previous version of rustc for toolchains that are updated:

    $ rustup update
    --snip--
      stable-x86_64-apple-darwin unchanged - rustc 1.39.0 (4560ea788 2019-11-04)
       nightly-x86_64-apple-darwin updated - rustc 1.41.0-nightly (7afe6d9d1 2019-12-03) (from rustc 1.40.0-nightly (22bc9e1d9 2019-09-30))
  • Loading branch information
RobbieClarken committed Dec 6, 2019
1 parent ae16683 commit 48736f5
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 81 deletions.
74 changes: 13 additions & 61 deletions src/cli/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ use rustup::utils::notify::NotificationLevel;
use rustup::utils::utils;
use rustup::{Cfg, Notification, Toolchain, UpdateStatus};
use std::fs;
use std::io::{BufRead, BufReader, ErrorKind, Write};
use std::io::{BufRead, ErrorKind, Write};
use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::Arc;
use std::time::Duration;
use std::{cmp, env, iter};
use term2::Terminal;
use wait_timeout::ChildExt;

pub const WARN_COMPLETE_PROFILE: &str = "downloading with complete profile isn't recommended unless you are a developer of the rust language";

Expand Down Expand Up @@ -153,16 +150,18 @@ fn show_channel_updates(
) -> Result<()> {
let data = toolchains.into_iter().map(|(name, result)| {
let toolchain = cfg.get_toolchain(&name, false).expect("");
let version = rustc_version(&toolchain);
let version = toolchain.rustc_version();

let banner;
let color;
let mut previous_version: Option<String> = None;
match result {
Ok(UpdateStatus::Installed) => {
banner = "installed";
color = Some(term2::color::GREEN);
}
Ok(UpdateStatus::Updated) => {
Ok(UpdateStatus::Updated(v)) => {
previous_version = Some(v);
banner = "updated";
color = Some(term2::color::GREEN);
}
Expand All @@ -178,17 +177,17 @@ fn show_channel_updates(

let width = name.len() + 1 + banner.len();

(name, banner, width, color, version)
(name, banner, width, color, version, previous_version)
});

let mut t = term2::stdout();

let data: Vec<_> = data.collect();
let max_width = data
.iter()
.fold(0, |a, &(_, _, width, _, _)| cmp::max(a, width));
.fold(0, |a, &(_, _, width, _, _, _)| cmp::max(a, width));

for (name, banner, width, color, version) in data {
for (name, banner, width, color, version, previous_version) in data {
let padding = max_width - width;
let padding: String = iter::repeat(' ').take(padding).collect();
let _ = write!(t, " {}", padding);
Expand All @@ -199,7 +198,11 @@ fn show_channel_updates(
let _ = write!(t, "{} ", name);
let _ = write!(t, "{}", banner);
let _ = t.reset();
let _ = writeln!(t, " - {}", version);
let _ = write!(t, " - {}", version);
if let Some(previous_version) = previous_version {
let _ = write!(t, " (from {})", previous_version);
}
let _ = writeln!(t);
}
let _ = writeln!(t);

Expand Down Expand Up @@ -313,57 +316,6 @@ where
Ok(())
}

pub fn rustc_version(toolchain: &Toolchain<'_>) -> String {
if toolchain.exists() {
let rustc_path = toolchain.binary_file("rustc");
if utils::is_file(&rustc_path) {
let mut cmd = Command::new(&rustc_path);
cmd.arg("--version");
cmd.stdin(Stdio::null());
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
toolchain.set_ldpath(&mut cmd);

// some toolchains are faulty with some combinations of platforms and
// may fail to launch but also to timely terminate.
// (known cases include Rust 1.3.0 through 1.10.0 in recent macOS Sierra.)
// we guard against such cases by enforcing a reasonable timeout to read.
let mut line1 = None;
if let Ok(mut child) = cmd.spawn() {
let timeout = Duration::new(10, 0);
match child.wait_timeout(timeout) {
Ok(Some(status)) if status.success() => {
let out = child
.stdout
.expect("Child::stdout requested but not present");
let mut line = String::new();
if BufReader::new(out).read_line(&mut line).is_ok() {
let lineend = line.trim_end_matches(&['\r', '\n'][..]).len();
line.truncate(lineend);
line1 = Some(line);
}
}
Ok(None) => {
let _ = child.kill();
return String::from("(timeout reading rustc version)");
}
Ok(Some(_)) | Err(_) => {}
}
}

if let Some(line1) = line1 {
line1
} else {
String::from("(error reading rustc version)")
}
} else {
String::from("(rustc does not exist)")
}
} else {
String::from("(toolchain not installed)")
}
}

pub fn list_targets(toolchain: &Toolchain<'_>) -> Result<()> {
let mut t = term2::stdout();
for component in toolchain.list_components()? {
Expand Down
6 changes: 3 additions & 3 deletions src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ fn update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<()> {
None
};

if let Some(status) = status {
if let Some(status) = status.clone() {
println!();
common::show_channel_update(cfg, toolchain.name(), Ok(status))?;
}
Expand Down Expand Up @@ -1002,11 +1002,11 @@ fn show(cfg: &Cfg) -> Result<()> {
Ok(atc) => match atc {
Some((ref toolchain, Some(ref reason))) => {
writeln!(t, "{} ({})", toolchain.name(), reason)?;
writeln!(t, "{}", common::rustc_version(toolchain))?;
writeln!(t, "{}", toolchain.rustc_version())?;
}
Some((ref toolchain, None)) => {
writeln!(t, "{} (default)", toolchain.name())?;
writeln!(t, "{}", common::rustc_version(toolchain))?;
writeln!(t, "{}", toolchain.rustc_version())?;
}
None => {
writeln!(t, "no active toolchain")?;
Expand Down
77 changes: 67 additions & 10 deletions src/toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ use std::env;
use std::env::consts::EXE_SUFFIX;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::{Command, Stdio};
use std::str::FromStr;
use std::time::Duration;

use url::Url;
use wait_timeout::ChildExt;

/// A fully resolved reference to a toolchain which may or may not exist
pub struct Toolchain<'a> {
Expand All @@ -38,10 +41,10 @@ pub struct ComponentStatus {
pub available: bool,
}

#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub enum UpdateStatus {
Installed,
Updated,
Updated(String), // Stores the version of rustc *before* the update
Unchanged,
}

Expand Down Expand Up @@ -103,8 +106,12 @@ impl<'a> Toolchain<'a> {
}
fn install(&self, install_method: InstallMethod<'_>) -> Result<UpdateStatus> {
assert!(self.is_valid_install_method(install_method));
let exists = self.exists();
if exists {
let previous_version = if self.exists() {
Some(self.rustc_version())
} else {
None
};
if previous_version.is_some() {
(self.cfg.notify_handler)(Notification::UpdatingToolchain(&self.name));
} else {
(self.cfg.notify_handler)(Notification::InstallingToolchain(&self.name));
Expand All @@ -118,11 +125,10 @@ impl<'a> Toolchain<'a> {
(self.cfg.notify_handler)(Notification::InstalledToolchain(&self.name));
}

let status = match (updated, exists) {
(true, false) => UpdateStatus::Installed,
(true, true) => UpdateStatus::Updated,
(false, true) => UpdateStatus::Unchanged,
(false, false) => UpdateStatus::Unchanged,
let status = match (updated, previous_version) {
(true, None) => UpdateStatus::Installed,
(true, Some(v)) => UpdateStatus::Updated(v),
(false, _) => UpdateStatus::Unchanged,
};

Ok(status)
Expand Down Expand Up @@ -792,4 +798,55 @@ impl<'a> Toolchain<'a> {
path.push(name.to_owned() + env::consts::EXE_SUFFIX);
path
}

pub fn rustc_version(&self) -> String {
if self.exists() {
let rustc_path = self.binary_file("rustc");
if utils::is_file(&rustc_path) {
let mut cmd = Command::new(&rustc_path);
cmd.arg("--version");
cmd.stdin(Stdio::null());
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
self.set_ldpath(&mut cmd);

// some toolchains are faulty with some combinations of platforms and
// may fail to launch but also to timely terminate.
// (known cases include Rust 1.3.0 through 1.10.0 in recent macOS Sierra.)
// we guard against such cases by enforcing a reasonable timeout to read.
let mut line1 = None;
if let Ok(mut child) = cmd.spawn() {
let timeout = Duration::new(10, 0);
match child.wait_timeout(timeout) {
Ok(Some(status)) if status.success() => {
let out = child
.stdout
.expect("Child::stdout requested but not present");
let mut line = String::new();
if BufReader::new(out).read_line(&mut line).is_ok() {
let lineend = line.trim_end_matches(&['\r', '\n'][..]).len();
line.truncate(lineend);
line1 = Some(line);
}
}
Ok(None) => {
let _ = child.kill();
return String::from("(timeout reading rustc version)");
}
Ok(Some(_)) | Err(_) => {}
}
}

if let Some(line1) = line1 {
line1
} else {
String::from("(error reading rustc version)")
}
} else {
String::from("(rustc does not exist)")
}
} else {
String::from("(toolchain not installed)")
}
}
}
14 changes: 7 additions & 7 deletions tests/cli-rustup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn rustup_stable() {
&["rustup", "update", "--no-self-update"],
for_host!(
r"
stable-{0} updated - 1.1.0 (hash-stable-1.1.0)
stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
"
),
Expand Down Expand Up @@ -80,7 +80,7 @@ fn rustup_stable_quiet() {
&["rustup", "--quiet", "update", "--no-self-update"],
for_host!(
r"
stable-{0} updated - 1.1.0 (hash-stable-1.1.0)
stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
"
),
Expand Down Expand Up @@ -142,9 +142,9 @@ fn rustup_all_channels() {
&["rustup", "update", "--no-self-update"],
for_host!(
r"
stable-{0} updated - 1.1.0 (hash-stable-1.1.0)
beta-{0} updated - 1.2.0 (hash-beta-1.2.0)
nightly-{0} updated - 1.3.0 (hash-nightly-2)
stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
beta-{0} updated - 1.2.0 (hash-beta-1.2.0) (from 1.1.0 (hash-beta-1.1.0))
nightly-{0} updated - 1.3.0 (hash-nightly-2) (from 1.2.0 (hash-nightly-1))
"
),
Expand Down Expand Up @@ -212,9 +212,9 @@ fn rustup_some_channels_up_to_date() {
&["rustup", "update", "--no-self-update"],
for_host!(
r"
stable-{0} updated - 1.1.0 (hash-stable-1.1.0)
stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
beta-{0} unchanged - 1.2.0 (hash-beta-1.2.0)
nightly-{0} updated - 1.3.0 (hash-nightly-2)
nightly-{0} updated - 1.3.0 (hash-nightly-2) (from 1.2.0 (hash-nightly-1))
"
),
Expand Down

0 comments on commit 48736f5

Please sign in to comment.