diff --git a/src/cli/common.rs b/src/cli/common.rs index 355d76d12d..bc93025a57 100644 --- a/src/cli/common.rs +++ b/src/cli/common.rs @@ -522,3 +522,17 @@ pub fn report_error(e: &Error) { } } } + +pub fn ignorable_error(error: crate::errors::Error, no_prompt: bool) -> Result<()> { + report_error(&error); + if no_prompt { + warn!("continuing (because the -y flag is set and the error is ignorable)"); + Ok(()) + } else { + if confirm("\nContinue? (y/N)", false).unwrap_or(false) { + Ok(()) + } else { + Err(error) + } + } +} diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 609cfdbd8a..3974f3376b 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -30,7 +30,7 @@ //! Deleting the running binary during uninstall is tricky //! and racy on Windows. -use crate::common::{self, Confirm}; +use crate::common::{self, ignorable_error, Confirm}; use crate::errors::*; use crate::markdown::md; use crate::term2; @@ -235,9 +235,16 @@ fn canonical_cargo_home() -> Result { /// `CARGO_HOME`/bin, hard-linking the various Rust tools to it, /// and adding `CARGO_HOME`/bin to PATH. pub fn install(no_prompt: bool, verbose: bool, quiet: bool, mut opts: InstallOpts) -> Result<()> { - do_pre_install_sanity_checks()?; + if !env::var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS").map_or(false, |s| s == "yes") { + do_pre_install_sanity_checks(no_prompt)?; + } + do_pre_install_options_sanity_checks(&opts)?; - check_existence_of_rustc_or_cargo_in_path(no_prompt)?; + + if !env::var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS").map_or(false, |s| s == "yes") { + check_existence_of_rustc_or_cargo_in_path(no_prompt)?; + } + #[cfg(unix)] do_anti_sudo_check(no_prompt)?; @@ -378,25 +385,25 @@ 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()) { + // Skip this if the environment variable is set + if 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 should not be installed alongside Rust. Please uninstall your existing Rust first."); - err!("Otherwise you may have confusion unless you are careful with your PATH"); - err!("If you are sure that you want both rustup and your already installed Rust"); - err!("then please restart the installation and pass `-y' to bypass this check."); - Err("cannot install while Rust is installed".into()) - } else { - Ok(()) + warn!("it looks like you have an existing installation of Rust at:"); + warn!("{}", path); + warn!("rustup should not be installed alongside Rust. Please uninstall your existing Rust first."); + warn!("Otherwise you may have confusion unless you are careful with your PATH"); + warn!("If you are sure that you want both rustup and your already installed Rust"); + warn!("then please reply `y' or `yes' or set RUSTUP_INIT_SKIP_PATH_CHECK to yes"); + warn!("or pass `-y' to ignore all ignorable checks."); + ignorable_error("cannot install while Rust is installed".into(), no_prompt)?; } + Ok(()) } -fn do_pre_install_sanity_checks() -> Result<()> { +fn do_pre_install_sanity_checks(no_prompt: bool) -> Result<()> { let rustc_manifest_path = PathBuf::from("/usr/local/lib/rustlib/manifest-rustc"); let uninstaller_path = PathBuf::from("/usr/local/lib/rustlib/uninstall.sh"); let rustup_sh_path = utils::home_dir().unwrap().join(".rustup"); @@ -412,7 +419,7 @@ fn do_pre_install_sanity_checks() -> Result<()> { "run `{}` as root to uninstall Rust", uninstaller_path.display() ); - return Err("cannot install while Rust is installed".into()); + ignorable_error("cannot install while Rust is installed".into(), no_prompt)?; } if rustup_sh_exists { @@ -422,7 +429,10 @@ fn do_pre_install_sanity_checks() -> Result<()> { warn!("or, if you already have rustup installed, you can run"); warn!("`rustup self update` and `rustup toolchain list` to upgrade"); warn!("your directory structure"); - return Err("cannot install while rustup.sh is installed".into()); + ignorable_error( + "cannot install while rustup.sh is installed".into(), + no_prompt, + )?; } Ok(()) diff --git a/tests/cli-inst-interactive.rs b/tests/cli-inst-interactive.rs index 93c8501943..53841283c1 100644 --- a/tests/cli-inst-interactive.rs +++ b/tests/cli-inst-interactive.rs @@ -8,6 +8,8 @@ use crate::mock::clitools::{ }; use crate::mock::{get_path, restore_path}; use lazy_static::lazy_static; +use rustup::utils::raw; +use std::fs; use std::io::Write; use std::process::Stdio; use std::sync::Mutex; @@ -39,9 +41,22 @@ pub fn setup(f: &dyn Fn(&Config)) { } fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput { + run_input_with_env(config, args, input, &[]) +} + +fn run_input_with_env( + config: &Config, + args: &[&str], + input: &str, + env: &[(&str, &str)], +) -> SanitizedOutput { let mut cmd = clitools::cmd(config, args[0], &args[1..]); clitools::env(config, &mut cmd); + for (key, value) in env.iter() { + cmd.env(key, value); + } + cmd.stdin(Stdio::piped()); cmd.stdout(Stdio::piped()); cmd.stderr(Stdio::piped()); @@ -266,3 +281,104 @@ fn test_warn_if_complete_profile_is_used() { ); }); } + +fn create_rustup_sh_metadata(config: &Config) { + let rustup_dir = config.homedir.join(".rustup"); + fs::create_dir_all(&rustup_dir).unwrap(); + let version_file = rustup_dir.join("rustup-version"); + raw::write_file(&version_file, "").unwrap(); +} + +#[test] +fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() { + setup(&|config| { + create_rustup_sh_metadata(&config); + let out = run_input(config, &["rustup-init"], "\n"); + assert!(!out.ok); + assert!(out + .stderr + .contains("warning: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out.stdout.contains("Continue? (y/N)")); + }) +} + +#[test] +fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() { + setup(&|config| { + create_rustup_sh_metadata(&config); + let out = run_input(config, &["rustup-init"], "no\n"); + assert!(!out.ok); + assert!(out + .stderr + .contains("warning: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out.stdout.contains("Continue? (y/N)")); + }) +} + +#[test] +fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() { + setup(&|config| { + create_rustup_sh_metadata(&config); + let out = run_input(config, &["rustup-init"], "yes\n\n\n"); + assert!(out.ok); + assert!(out + .stderr + .contains("warning: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out.stdout.contains("Continue? (y/N)")); + assert!(!out.stdout.contains( + "warning: continuing (because the -y flag is set and the error is ignorable)" + )) + }) +} + +#[test] +fn test_warn_succeed_if_rustup_sh_already_installed_y_flag() { + setup(&|config| { + create_rustup_sh_metadata(&config); + let out = run_input(config, &["rustup-init", "-y"], ""); + assert!(out.ok); + assert!(out + .stderr + .contains("warning: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out.stderr.contains( + "warning: continuing (because the -y flag is set and the error is ignorable)" + )); + assert!(!out.stdout.contains("Continue? (y/N)")); + }) +} + +#[test] +fn test_succeed_if_rustup_sh_already_installed_env_var_set() { + setup(&|config| { + create_rustup_sh_metadata(&config); + let out = run_input_with_env( + config, + &["rustup-init", "-y"], + "", + &[("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS", "yes")], + ); + assert!(out.ok); + assert!(!out + .stderr + .contains("warning: it looks like you have existing rustup.sh metadata")); + assert!(!out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(!out.stderr.contains( + "warning: continuing (because the -y flag is set and the error is ignorable)" + )); + assert!(!out.stdout.contains("Continue? (y/N)")); + }) +} diff --git a/tests/cli-misc.rs b/tests/cli-misc.rs index ba00332f22..eb53771f52 100644 --- a/tests/cli-misc.rs +++ b/tests/cli-misc.rs @@ -620,7 +620,7 @@ fn install_stops_if_rustc_exists() { .contains("it looks like you have an existing installation of Rust at:")); assert!(out .stderr - .contains("restart the installation and pass `-y'")); + .contains("If you are sure that you want both rustup and your already installed Rust")); }); } @@ -652,7 +652,7 @@ fn install_stops_if_cargo_exists() { .contains("it looks like you have an existing installation of Rust at:")); assert!(out .stderr - .contains("restart the installation and pass `-y'")); + .contains("If you are sure that you want both rustup and your already installed Rust")); }); } diff --git a/tests/cli-self-upd.rs b/tests/cli-self-upd.rs index 10f4079d8e..4eb96e9c01 100644 --- a/tests/cli-self-upd.rs +++ b/tests/cli-self-upd.rs @@ -1166,7 +1166,7 @@ fn install_but_rustup_sh_is_installed() { fs::create_dir_all(&rustup_dir).unwrap(); let version_file = rustup_dir.join("rustup-version"); raw::write_file(&version_file, "").unwrap(); - expect_err( + expect_stderr_ok( config, &["rustup-init", "-y"], "cannot install while rustup.sh is installed",