Skip to content

Commit 84616ef

Browse files
authored
Merge pull request #705 from meqif/check-for-unexpected-cargo-rustc-before-install
Check for unexpected cargo/rustc before install
2 parents e8c7e21 + 774d0c0 commit 84616ef

File tree

3 files changed

+107
-7
lines changed

3 files changed

+107
-7
lines changed

src/rustup-cli/self_update.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use rustup_dist::dist;
3636
use rustup_utils::utils;
3737
use std::env;
3838
use std::env::consts::EXE_SUFFIX;
39-
use std::path::{Path, PathBuf};
39+
use std::path::{Path, PathBuf, Component};
4040
use std::process::{self, Command};
4141
use std::fs;
4242
use tempdir::TempDir;
@@ -215,11 +215,12 @@ fn canonical_cargo_home() -> Result<String> {
215215

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

222222
try!(do_pre_install_sanity_checks());
223+
try!(check_existence_of_rustc_or_cargo_in_path(no_prompt));
223224
try!(do_anti_sudo_check(no_prompt));
224225

225226
if !try!(do_msvc_check(&opts)) {
@@ -331,8 +332,49 @@ pub fn install(no_prompt: bool, verbose: bool,
331332
Ok(())
332333
}
333334

334-
fn do_pre_install_sanity_checks() -> Result<()> {
335+
fn rustc_or_cargo_exists_in_path() -> Result<()> {
336+
// Ignore rustc and cargo if present in $HOME/.cargo/bin or a few other directories
337+
fn ignore_paths(path: &PathBuf) -> bool {
338+
!path.components().any(|c| c == Component::Normal(".cargo".as_ref())) &&
339+
!path.components().any(|c| c == Component::Normal(".multirust".as_ref()))
340+
}
341+
342+
if let Some(paths) = env::var_os("PATH") {
343+
let paths = env::split_paths(&paths).filter(ignore_paths);
344+
345+
for path in paths {
346+
let rustc = path.join(format!("rustc{}", EXE_SUFFIX));
347+
let cargo = path.join(format!("cargo{}", EXE_SUFFIX));
348+
349+
if rustc.exists() || cargo.exists() {
350+
return Err(path.to_str().unwrap().into());
351+
}
352+
}
353+
}
354+
Ok(())
355+
}
356+
357+
fn check_existence_of_rustc_or_cargo_in_path(no_prompt: bool) -> Result<()> {
358+
// Only the test runner should set this
359+
let skip_check = env::var_os("RUSTUP_INIT_SKIP_PATH_CHECK");
335360

361+
// Ignore this check if called with no prompt (-y) or if the environment variable is set
362+
if no_prompt || skip_check == Some("yes".into()) {
363+
return Ok(());
364+
}
365+
366+
if let Err(path) = rustc_or_cargo_exists_in_path() {
367+
err!("it looks like you have an existing installation of Rust at:");
368+
err!("{}", path);
369+
err!("rustup cannot be installed alongside Rust. Please uninstall first");
370+
err!("if this is what you want, restart the installation with `-y'");
371+
Err("cannot install while Rust is installed".into())
372+
} else {
373+
Ok(())
374+
}
375+
}
376+
377+
fn do_pre_install_sanity_checks() -> Result<()> {
336378
let multirust_manifest_path
337379
= PathBuf::from("/usr/local/lib/rustlib/manifest-multirust");
338380
let rustc_manifest_path
@@ -652,7 +694,7 @@ pub fn uninstall(no_prompt: bool) -> Result<()> {
652694
.chain_err(|| ErrorKind::WindowsUninstallMadness));
653695
process::exit(0);
654696
}
655-
697+
656698
let ref cargo_home = try!(utils::cargo_home());
657699

658700
if !cargo_home.join(&format!("bin/rustup{}", EXE_SUFFIX)).exists() {

src/rustup-mock/src/clitools.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ pub fn env(config: &Config, cmd: &mut Command) {
313313

314314
// Skip the MSVC warning check since it's environment dependent
315315
cmd.env("RUSTUP_INIT_SKIP_MSVC_CHECK", "yes");
316+
317+
// The test environment may interfere with checking the PATH for the existence of rustc or
318+
// cargo, so we disable that check globally
319+
cmd.env("RUSTUP_INIT_SKIP_PATH_CHECK", "yes");
316320
}
317321

318322
pub fn run(config: &Config, name: &str, args: &[&str], env: &[(&str, &str)]) -> SanitizedOutput {

tests/cli-misc.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@ extern crate rustup_dist;
55
extern crate rustup_utils;
66
extern crate rustup_mock;
77
extern crate time;
8-
use rustup_mock::clitools::{self, Config, Scenario,
8+
extern crate tempdir;
9+
10+
use rustup_mock::clitools::{self, Config, Scenario, SanitizedOutput,
911
expect_stdout_ok, expect_stderr_ok,
1012
expect_ok, expect_err, expect_timeout_ok,
1113
run, this_host_triple};
1214
use rustup_utils::{raw, utils};
1315

14-
use time::Duration;
15-
use std::ops::Sub;
16+
use std::io::Write;
1617
use std::ops::Add;
18+
use std::ops::Sub;
19+
use std::process::Stdio;
1720
use std::time::Duration as StdDuration;
1821
use std::env::consts::EXE_SUFFIX;
22+
use tempdir::TempDir;
23+
use time::Duration;
1924

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

@@ -429,3 +434,52 @@ fn rls_does_not_exist_in_toolchain() {
429434
this_host_triple(), EXE_SUFFIX));
430435
});
431436
}
437+
438+
#[test]
439+
fn install_stops_if_rustc_exists() {
440+
let temp_dir = TempDir::new("fakebin").unwrap();
441+
// Create fake executable
442+
let ref fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
443+
raw::append_file(fake_exe, "").unwrap();
444+
let temp_dir_path = temp_dir.path().to_str().unwrap();
445+
446+
setup(&|config| {
447+
let out = run(config, "rustup-init", &[],
448+
&[("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", &temp_dir_path)]);
449+
assert!(!out.ok);
450+
assert!(out.stderr.contains("it looks like you have an existing installation of Rust at:"));
451+
assert!(out.stderr.contains("if this is what you want, restart the installation with `-y'"));
452+
});
453+
}
454+
455+
#[test]
456+
fn install_stops_if_cargo_exists() {
457+
let temp_dir = TempDir::new("fakebin").unwrap();
458+
// Create fake executable
459+
let ref fake_exe = temp_dir.path().join(&format!("{}{}", "cargo", EXE_SUFFIX));
460+
raw::append_file(fake_exe, "").unwrap();
461+
let temp_dir_path = temp_dir.path().to_str().unwrap();
462+
463+
setup(&|config| {
464+
let out = run(config, "rustup-init", &[],
465+
&[("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", &temp_dir_path)]);
466+
assert!(!out.ok);
467+
assert!(out.stderr.contains("it looks like you have an existing installation of Rust at:"));
468+
assert!(out.stderr.contains("if this is what you want, restart the installation with `-y'"));
469+
});
470+
}
471+
472+
#[test]
473+
fn with_no_prompt_install_succeeds_if_rustc_exists() {
474+
let temp_dir = TempDir::new("fakebin").unwrap();
475+
// Create fake executable
476+
let ref fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
477+
raw::append_file(fake_exe, "").unwrap();
478+
let temp_dir_path = temp_dir.path().to_str().unwrap();
479+
480+
setup(&|config| {
481+
let out = run(config, "rustup-init", &["-y"],
482+
&[("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", &temp_dir_path)]);
483+
assert!(out.ok);
484+
});
485+
}

0 commit comments

Comments
 (0)