diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index e059cc8176..d85141dfa4 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -1096,7 +1096,7 @@ async fn run( install: bool, ) -> Result { let toolchain = toolchain.resolve(&cfg.default_host_tuple()?)?; - let toolchain = Toolchain::from_local(toolchain, install, cfg).await?; + let toolchain = Toolchain::from_local(toolchain, || Ok(install), cfg).await?; let cmd = toolchain.command(&command[0])?; command::run_command_for_dir(cmd, &command[0], &command[1..]) } diff --git a/src/config.rs b/src/config.rs index 59bb120312..87b2e295ed 100644 --- a/src/config.rs +++ b/src/config.rs @@ -717,13 +717,10 @@ impl<'a> Cfg<'a> { name: Option<(LocalToolchainName, ActiveSource)>, ) -> Result<(Toolchain<'_>, ActiveSource)> { match name { - Some((tc, source)) => { - let install_if_missing = self.should_auto_install()?; - Ok(( - Toolchain::from_local(tc, install_if_missing, self).await?, - source, - )) - } + Some((tc, src)) => Ok(( + Toolchain::from_local(tc, || self.should_auto_install(), self).await?, + src, + )), None => { let (tc, source) = self .maybe_ensure_active_toolchain(None) diff --git a/src/test/clitools.rs b/src/test/clitools.rs index 4dcf2b79e3..1311322696 100644 --- a/src/test/clitools.rs +++ b/src/test/clitools.rs @@ -314,6 +314,12 @@ impl Config { "/bogus-config-file.toml", ); + // Clear current recursion count to avoid messing up related logic + cmd.env("RUST_RECURSION_COUNT", ""); + + // Clear override for auto installation of active toolchain unless explicitly requested + cmd.env("RUSTUP_AUTO_INSTALL", ""); + // Pass `RUSTUP_CI` over to the test process in case it is required downstream if let Some(ci) = env::var_os("RUSTUP_CI") { cmd.env("RUSTUP_CI", ci); diff --git a/src/toolchain.rs b/src/toolchain.rs index 695a14c045..e59963a6ec 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -15,7 +15,7 @@ use std::{ use anyhow::{Context, anyhow, bail}; use fs_at::OpenOptions; use same_file::is_same_file; -use tracing::info; +use tracing::{info, warn}; use url::Url; use wait_timeout::ChildExt; @@ -52,7 +52,7 @@ pub(crate) struct Toolchain<'a> { impl<'a> Toolchain<'a> { pub(crate) async fn from_local( name: LocalToolchainName, - install_if_missing: bool, + install_if_missing: impl Fn() -> anyhow::Result, cfg: &'a Cfg<'a>, ) -> anyhow::Result> { match Self::new(cfg, name) { @@ -60,7 +60,19 @@ impl<'a> Toolchain<'a> { Err(RustupError::ToolchainNotInstalled { name: ToolchainName::Official(desc), .. - }) if install_if_missing => { + }) if install_if_missing()? => { + // Emit a warning about auto-installation if we aren't deep inside a recursive call. + let recursions = cfg.process.var("RUST_RECURSION_COUNT"); + if !recursions.is_ok_and(|it| it != "0") { + warn!("auto-install is enabled, active toolchain will be installed if absent"); + warn!( + "this might cause rustup commands to take longer time to finish than expected" + ); + info!( + "you may opt out with `RUSTUP_AUTO_INSTALL=0` or `rustup set auto-install disable`" + ); + } + let options = DistOptions::new(&[], &[], &desc, cfg.get_profile()?, true, cfg)?; Ok(DistributableToolchain::install(options).await?.1.toolchain) } diff --git a/tests/suite/cli_misc.rs b/tests/suite/cli_misc.rs index 5316a2db84..964f2af8b8 100644 --- a/tests/suite/cli_misc.rs +++ b/tests/suite/cli_misc.rs @@ -64,7 +64,7 @@ async fn rustc_with_bad_rustup_toolchain_env_var() { .expect_with_env(["rustc"], [("RUSTUP_TOOLCHAIN", "bogus")]) .await .with_stderr(snapbox::str![[r#" -error: override toolchain 'bogus' is not installed[..] +error:[..] toolchain 'bogus' is not installed[..] "#]]) .is_err(); @@ -1381,7 +1381,10 @@ async fn which_asking_uninstalled_toolchain() { "#]]) .is_ok(); cx.config - .expect(["rustup", "which", "--toolchain=nightly", "rustc"]) + .expect_with_env( + ["rustup", "which", "--toolchain=nightly", "rustc"], + [("RUSTUP_AUTO_INSTALL", "1")], + ) .await .with_stderr(snapbox::str![[r#" error: toolchain 'nightly-[HOST_TUPLE]' is not installed @@ -1746,3 +1749,26 @@ info: falling back to "[EXTERN_PATH]" "#]]) .is_ok(); } + +#[tokio::test] +async fn warn_auto_install() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_with_env( + ["rustc", "--version"], + [("RUSTUP_TOOLCHAIN", "stable"), ("RUSTUP_AUTO_INSTALL", "1")], + ) + .await + .with_stdout(snapbox::str![[r#" +1.1.0 (hash-stable-1.1.0) + +"#]]) + .with_stderr(snapbox::str![[r#" +... +warn: auto-install is enabled, active toolchain will be installed if absent +warn: this might cause rustup commands to take longer time to finish than expected +info: you may opt out with `RUSTUP_AUTO_INSTALL=0` or `rustup set auto-install disable` +... +"#]]) + .is_ok(); +} diff --git a/tests/suite/cli_rustup.rs b/tests/suite/cli_rustup.rs index 82fd9278d1..6ceb147e9c 100644 --- a/tests/suite/cli_rustup.rs +++ b/tests/suite/cli_rustup.rs @@ -1247,7 +1247,7 @@ async fn show_toolchain_override_not_installed() { .await .is_ok(); cx.config - .expect(["rustup", "show"]) + .expect_with_env(["rustup", "show"], [("RUSTUP_AUTO_INSTALL", "1")]) .await .extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())]) .with_stdout(snapbox::str![[r#" @@ -1349,7 +1349,13 @@ installed targets: async fn show_toolchain_env_not_installed() { let cx = CliTestContext::new(Scenario::SimpleV2).await; cx.config - .expect_with_env(["rustup", "show"], [("RUSTUP_TOOLCHAIN", "nightly")]) + .expect_with_env( + ["rustup", "show"], + [ + ("RUSTUP_TOOLCHAIN", "nightly"), + ("RUSTUP_AUTO_INSTALL", "1"), + ], + ) .await .extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())]) .is_ok() diff --git a/tests/suite/cli_v1.rs b/tests/suite/cli_v1.rs index 465b26b59e..2a98f95b65 100644 --- a/tests/suite/cli_v1.rs +++ b/tests/suite/cli_v1.rs @@ -271,7 +271,7 @@ async fn remove_override_toolchain_err_handling() { .await .is_ok(); cx.config - .expect(["rustc", "--version"]) + .expect_with_env(["rustc", "--version"], [("RUSTUP_AUTO_INSTALL", "1")]) .await .with_stdout(snapbox::str![[r#" 1.2.0 (hash-beta-1.2.0) diff --git a/tests/suite/cli_v2.rs b/tests/suite/cli_v2.rs index 73eda294a6..956466625e 100644 --- a/tests/suite/cli_v2.rs +++ b/tests/suite/cli_v2.rs @@ -478,7 +478,7 @@ async fn remove_override_toolchain_err_handling() { .await .is_ok(); cx.config - .expect(["rustc", "--version"]) + .expect_with_env(["rustc", "--version"], [("RUSTUP_AUTO_INSTALL", "1")]) .await .with_stdout(snapbox::str![[r#" 1.2.0 (hash-beta-1.2.0) @@ -511,7 +511,7 @@ async fn file_override_toolchain_err_handling() { let toolchain_file = cwd.join("rust-toolchain"); rustup::utils::raw::write_file(&toolchain_file, "beta").unwrap(); cx.config - .expect(["rustc", "--version"]) + .expect_with_env(["rustc", "--version"], [("RUSTUP_AUTO_INSTALL", "1")]) .await .with_stdout(snapbox::str![[r#" 1.2.0 (hash-beta-1.2.0) @@ -553,7 +553,10 @@ error: toolchain 'beta-[HOST_TUPLE]' is not installed "#]]) .is_err(); cx.config - .expect(["rustc", "+beta", "--version"]) + .expect_with_env( + ["rustc", "+beta", "--version"], + [("RUSTUP_AUTO_INSTALL", "1")], + ) .await .with_stdout(snapbox::str![[r#" 1.2.0 (hash-beta-1.2.0)