diff --git a/Cargo.lock b/Cargo.lock index 3646726838..1fba415d81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,6 +561,7 @@ dependencies = [ "toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wait-timeout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winreg 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index e89c0eb9a3..fb0afeb975 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ rustc-serialize = "0.3" sha2 = "0.1.2" markdown = "0.2" toml = "0.1.27" +wait-timeout = "0.1.5" [target."cfg(windows)".dependencies] winapi = "0.2.8" diff --git a/src/rustup-cli/common.rs b/src/rustup-cli/common.rs index 1abf345af4..01ecf1058c 100644 --- a/src/rustup-cli/common.rs +++ b/src/rustup-cli/common.rs @@ -6,13 +6,15 @@ use errors::*; use rustup_utils::utils; use rustup_utils::notify::NotificationLevel; use self_update; -use std::io::{Write, BufRead}; -use std::process::Command; +use std::io::{Write, BufRead, BufReader}; +use std::process::{Command, Stdio}; use std::path::Path; use std::{cmp, iter}; use std::sync::Arc; +use std::time::Duration; use std; use term2; +use wait_timeout::ChildExt; pub fn confirm(question: &str, default: bool) -> Result { print!("{} ", question); @@ -222,12 +224,34 @@ pub fn rustc_version(toolchain: &Toolchain) -> String { 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); - let out= cmd.output().ok(); - let out = out.into_iter().find(|o| o.status.success()); - let stdout = out.and_then(|o| String::from_utf8(o.stdout).ok()); - let line1 = stdout.and_then(|o| o.lines().next().map(|l| l.to_owned())); + // 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(1, 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_right_matches(&['\r', '\n'][..]).len(); + line.truncate(lineend); + line1 = Some(line); + } + } + Ok(None) => { + let _ = child.kill(); + } + Ok(Some(_)) | Err(_) => {} + } + } if let Some(line1) = line1 { line1.to_owned() diff --git a/src/rustup-cli/main.rs b/src/rustup-cli/main.rs index 8dc31f4d84..2ea6f16d9c 100644 --- a/src/rustup-cli/main.rs +++ b/src/rustup-cli/main.rs @@ -20,6 +20,7 @@ extern crate tempdir; extern crate sha2; extern crate markdown; extern crate toml; +extern crate wait_timeout; #[cfg(windows)] extern crate gcc;