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

Check for unexpected cargo/rustc before install #705

Merged
merged 2 commits into from
Jun 24, 2017
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions src/rustup-cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use rustup_dist::dist;
use rustup_utils::utils;
use std::env;
use std::env::consts::EXE_SUFFIX;
use std::path::{Path, PathBuf};
use std::path::{Path, PathBuf, Component};
use std::process::{self, Command};
use std::fs;
use tempdir::TempDir;
Expand Down Expand Up @@ -215,11 +215,12 @@ fn canonical_cargo_home() -> Result<String> {

/// Installing is a simple matter of coping the running binary to
/// `CARGO_HOME`/bin, hardlinking the various Rust tools to it,
/// and and adding `CARGO_HOME`/bin to PATH.
/// and adding `CARGO_HOME`/bin to PATH.
pub fn install(no_prompt: bool, verbose: bool,
mut opts: InstallOpts) -> Result<()> {

try!(do_pre_install_sanity_checks());
try!(check_existence_of_rustc_or_cargo_in_path(no_prompt));
try!(do_anti_sudo_check(no_prompt));

if !try!(do_msvc_check(&opts)) {
Expand Down Expand Up @@ -331,8 +332,49 @@ pub fn install(no_prompt: bool, verbose: bool,
Ok(())
}

fn do_pre_install_sanity_checks() -> Result<()> {
fn rustc_or_cargo_exists_in_path() -> Result<()> {
// Ignore rustc and cargo if present in $HOME/.cargo/bin or a few other directories
fn ignore_paths(path: &PathBuf) -> bool {
!path.components().any(|c| c == Component::Normal(".cargo".as_ref())) &&
!path.components().any(|c| c == Component::Normal(".multirust".as_ref()))
}

if let Some(paths) = env::var_os("PATH") {
let paths = env::split_paths(&paths).filter(ignore_paths);

for path in paths {
let rustc = path.join(format!("rustc{}", EXE_SUFFIX));
let cargo = path.join(format!("cargo{}", EXE_SUFFIX));

if rustc.exists() || cargo.exists() {
return Err(path.to_str().unwrap().into());
}
}
}
Ok(())
}

fn check_existence_of_rustc_or_cargo_in_path(no_prompt: bool) -> Result<()> {
// Only the test runner should set this
let skip_check = env::var_os("RUSTUP_INIT_SKIP_PATH_CHECK");

// Ignore this check if called with no prompt (-y) or if the environment variable is set
if no_prompt || skip_check == Some("yes".into()) {
return Ok(());
}

if let Err(path) = rustc_or_cargo_exists_in_path() {
err!("it looks like you have an existing installation of Rust at:");
err!("{}", path);
err!("rustup cannot be installed alongside Rust. Please uninstall first");
err!("if this is what you want, restart the installation with `-y'");
Err("cannot install while Rust is installed".into())
} else {
Ok(())
}
}

fn do_pre_install_sanity_checks() -> Result<()> {
let multirust_manifest_path
= PathBuf::from("/usr/local/lib/rustlib/manifest-multirust");
let rustc_manifest_path
Expand Down Expand Up @@ -652,7 +694,7 @@ pub fn uninstall(no_prompt: bool) -> Result<()> {
.chain_err(|| ErrorKind::WindowsUninstallMadness));
process::exit(0);
}

let ref cargo_home = try!(utils::cargo_home());

if !cargo_home.join(&format!("bin/rustup{}", EXE_SUFFIX)).exists() {
Expand Down
4 changes: 4 additions & 0 deletions src/rustup-mock/src/clitools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ pub fn env(config: &Config, cmd: &mut Command) {

// Skip the MSVC warning check since it's environment dependent
cmd.env("RUSTUP_INIT_SKIP_MSVC_CHECK", "yes");

// The test environment may interfere with checking the PATH for the existence of rustc or
// cargo, so we disable that check globally
cmd.env("RUSTUP_INIT_SKIP_PATH_CHECK", "yes");
}

pub fn run(config: &Config, name: &str, args: &[&str], env: &[(&str, &str)]) -> SanitizedOutput {
Expand Down
60 changes: 57 additions & 3 deletions tests/cli-misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ extern crate rustup_dist;
extern crate rustup_utils;
extern crate rustup_mock;
extern crate time;
use rustup_mock::clitools::{self, Config, Scenario,
extern crate tempdir;

use rustup_mock::clitools::{self, Config, Scenario, SanitizedOutput,
expect_stdout_ok, expect_stderr_ok,
expect_ok, expect_err, expect_timeout_ok,
run, this_host_triple};
use rustup_utils::{raw, utils};

use time::Duration;
use std::ops::Sub;
use std::io::Write;
use std::ops::Add;
use std::ops::Sub;
use std::process::Stdio;
use std::time::Duration as StdDuration;
use std::env::consts::EXE_SUFFIX;
use tempdir::TempDir;
use time::Duration;

macro_rules! for_host { ($s: expr) => (&format!($s, this_host_triple())) }

Expand Down Expand Up @@ -429,3 +434,52 @@ fn rls_does_not_exist_in_toolchain() {
this_host_triple(), EXE_SUFFIX));
});
}

#[test]
fn install_stops_if_rustc_exists() {
let temp_dir = TempDir::new("fakebin").unwrap();
// Create fake executable
let ref fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
raw::append_file(fake_exe, "").unwrap();
let temp_dir_path = temp_dir.path().to_str().unwrap();

setup(&|config| {
let out = run(config, "rustup-init", &[],
&[("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", &temp_dir_path)]);
assert!(!out.ok);
assert!(out.stderr.contains("it looks like you have an existing installation of Rust at:"));
assert!(out.stderr.contains("if this is what you want, restart the installation with `-y'"));
});
}

#[test]
fn install_stops_if_cargo_exists() {
let temp_dir = TempDir::new("fakebin").unwrap();
// Create fake executable
let ref fake_exe = temp_dir.path().join(&format!("{}{}", "cargo", EXE_SUFFIX));
raw::append_file(fake_exe, "").unwrap();
let temp_dir_path = temp_dir.path().to_str().unwrap();

setup(&|config| {
let out = run(config, "rustup-init", &[],
&[("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", &temp_dir_path)]);
assert!(!out.ok);
assert!(out.stderr.contains("it looks like you have an existing installation of Rust at:"));
assert!(out.stderr.contains("if this is what you want, restart the installation with `-y'"));
});
}

#[test]
fn with_no_prompt_install_succeeds_if_rustc_exists() {
let temp_dir = TempDir::new("fakebin").unwrap();
// Create fake executable
let ref fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
raw::append_file(fake_exe, "").unwrap();
let temp_dir_path = temp_dir.path().to_str().unwrap();

setup(&|config| {
let out = run(config, "rustup-init", &["-y"],
&[("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", &temp_dir_path)]);
assert!(out.ok);
});
}