Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Output the previous version of a toolchain when it is updated #2143

Merged
merged 1 commit into from Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 13 additions & 61 deletions src/cli/common.rs
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
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
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
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