Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ async fn run(
install: bool,
) -> Result<ExitStatus> {
let toolchain = toolchain.resolve(&cfg.get_default_host_triple()?)?;
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..])
}
Expand Down
31 changes: 21 additions & 10 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use anyhow::{Context, Result, anyhow, bail};
use serde::Deserialize;
use thiserror::Error as ThisError;
use tokio_stream::StreamExt;
use tracing::trace;
use tracing::{info, trace, warn};

use crate::dist::AutoInstallMode;
use crate::{
Expand Down Expand Up @@ -387,12 +387,26 @@ impl<'a> Cfg<'a> {
}

pub(crate) fn should_auto_install(&self) -> Result<bool> {
if let Ok(mode) = self.process.var("RUSTUP_AUTO_INSTALL") {
Ok(mode != "0")
} else {
self.settings_file
.with(|s| Ok(s.auto_install != Some(AutoInstallMode::Disable)))
let res = match self.process.var("RUSTUP_AUTO_INSTALL") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest not naming this res (which I usually read as short for result and expect to be of type Result) but auto_install (or maybe auto).

Ok(mode) => mode != "0",
Err(_) => self
.settings_file
.with(|s| Ok(s.auto_install != Some(AutoInstallMode::Disable)))?,
};
if res
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest making this more linear to avoid the messy layout:

if !res {
    return Ok(res);
}

let recursing = self.process.var("RUST_RECURSION_COUNT");
if matches!(recursing.as_deref(), Ok("1")) {
     return Ok(res);
}

warn!(..);
Ok(res)

// We also need to suppress this warning if we're deep inside a recursive call.
&& matches!(
self.process.var("RUST_RECURSION_COUNT").as_deref(),
Err(_) | Ok("0"),
)
{
warn!("auto-install is enabled, active toolchain will be installed if absent");
Copy link
Contributor

@djc djc Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too strong until we have a clearer plan about how/when we are actually going to disable it by default. So I would only want to show a message when the active toolchain is actually absent, and make it more informational. I do like showing the suggestion on how to disable the behavior!

warn!("this behavior is deprecated and will be removed in a future version");
info!(
"you may opt out now with `RUSTUP_AUTO_INSTALL=0` or `rustup set auto-install disable`"
);
}
Ok(res)
}

// Returns a profile, if one exists in the settings file.
Expand Down Expand Up @@ -746,10 +760,7 @@ impl<'a> Cfg<'a> {

async fn local_toolchain(&self, name: Option<LocalToolchainName>) -> Result<Toolchain<'_>> {
match name {
Some(tc) => {
let install_if_missing = self.should_auto_install()?;
Toolchain::from_local(tc, install_if_missing, self).await
}
Some(tc) => Toolchain::from_local(tc, || self.should_auto_install(), self).await,
None => {
let tc = self
.maybe_ensure_active_toolchain(None)
Expand Down
2 changes: 1 addition & 1 deletion src/dist/component/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl Components {
}
pub fn find(&self, name: &str) -> Result<Option<Component>> {
let result = self.list()?;
Ok(result.into_iter().find(|c| (c.name() == name)))
Ok(result.into_iter().find(|c| c.name() == name))
}
pub(crate) fn prefix(&self) -> InstallPrefix {
self.prefix.clone()
Expand Down
8 changes: 7 additions & 1 deletion src/test/clitools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@ impl Config {
"/bogus-config-file.toml",
);

// Clear current recursion count to avoid messing up related logic
cmd.env("RUST_RECURSION_COUNT", "");

// Disable auto installation of active toolchain unless explicitly requested
cmd.env("RUSTUP_AUTO_INSTALL", "0");
Comment on lines +283 to +284
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this. IMO we should keep the defaults in the tests the same way they are when running real-world rustup, otherwise things get confusing.


// 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);
Expand All @@ -291,7 +297,7 @@ impl Config {
/// specified by `args` under the default environment.
#[must_use]
pub async fn expect<S: AsRef<OsStr> + Clone + Debug>(&self, args: impl AsRef<[S]>) -> Assert {
self.expect_with_env(args, &[]).await
self.expect_with_env(args, []).await
}

/// Returns an [`Assert`] object to check the output of running the command
Expand Down
4 changes: 2 additions & 2 deletions src/toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ 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<bool>,
Copy link
Member Author

@rami3l rami3l Aug 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is made so that the self.should_auto_install() call is postponed to L64, and that the false positive in L60 can be suppressed.

cfg: &'a Cfg<'a>,
) -> anyhow::Result<Toolchain<'a>> {
match Self::new(cfg, name) {
Ok(tc) => Ok(tc),
Err(RustupError::ToolchainNotInstalled {
name: ToolchainName::Official(desc),
..
}) if install_if_missing => {
}) if install_if_missing()? => {
Ok(
DistributableToolchain::install(cfg, &desc, &[], &[], cfg.get_profile()?, true)
.await?
Expand Down
32 changes: 29 additions & 3 deletions tests/suite/cli_misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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_stdout(snapbox::str![[r#"
[..]/toolchains/nightly-[HOST_TRIPLE]/bin/rustc[EXE]
Expand Down Expand Up @@ -1512,7 +1515,7 @@ active because: overridden by +toolchain on the command line
.expect(["rustup", "+foo", "which", "rustc"])
.await
.with_stderr(snapbox::str![[r#"
error: override toolchain 'foo' is not installed: the +toolchain on the command line specifies an uninstalled toolchain
error:[..] toolchain 'foo' is not installed[..]

"#]])
.is_err();
Expand Down Expand Up @@ -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 behavior is deprecated and will be removed in a future version
info: you may opt out now with `RUSTUP_AUTO_INSTALL=0` or `rustup set auto-install disable`
...
"#]])
.is_ok();
}
14 changes: 10 additions & 4 deletions tests/suite/cli_rustup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,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#"
Expand Down Expand Up @@ -1327,7 +1327,7 @@ async fn show_toolchain_env() {
.await
.is_ok();
cx.config
.expect_with_env(["rustup", "show"], &[("RUSTUP_TOOLCHAIN", "nightly")])
.expect_with_env(["rustup", "show"], [("RUSTUP_TOOLCHAIN", "nightly")])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.is_ok()
Expand All @@ -1353,7 +1353,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()
Expand Down Expand Up @@ -3027,7 +3033,7 @@ error: no active toolchain
"show",
"active-toolchain",
],
&[("RUSTUP_TOOLCHAIN", &**env_tc)],
[("RUSTUP_TOOLCHAIN", &**env_tc)],
)
.await
.extend_redactions([("[COMMAND_TC]", command_tc)])
Expand Down
3 changes: 2 additions & 1 deletion tests/suite/cli_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,14 @@ 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)

"#]])
.with_stderr(snapbox::str![[r#"
...
info: syncing channel updates for 'beta-[HOST_TRIPLE]'
...
"#]])
Expand Down
23 changes: 14 additions & 9 deletions tests/suite/cli_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,13 +478,14 @@ 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)

"#]])
.with_stderr(snapbox::str![[r#"
...
info: syncing channel updates for 'beta-[HOST_TRIPLE]'
info: latest update on 2015-01-02, rust version 1.2.0 (hash-beta-1.2.0)
info: downloading component[..]
Expand All @@ -511,13 +512,14 @@ 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)

"#]])
.with_stderr(snapbox::str![[r#"
...
info: syncing channel updates for 'beta-[HOST_TRIPLE]'
info: latest update on 2015-01-02, rust version 1.2.0 (hash-beta-1.2.0)
info: downloading component[..]
Expand All @@ -543,7 +545,7 @@ async fn plus_override_toolchain_err_handling() {
cx.config
.expect_with_env(
["rustc", "+beta", "--version"],
&[("RUSTUP_AUTO_INSTALL", "0")],
[("RUSTUP_AUTO_INSTALL", "0")],
)
.await
.with_stderr(snapbox::str![[r#"
Expand All @@ -553,7 +555,10 @@ error: toolchain 'beta-[HOST_TRIPLE]' 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)
Expand Down Expand Up @@ -1191,7 +1196,7 @@ async fn list_targets_no_toolchain() {
cx.config
.expect_with_env(
["rustup", "target", "list", "--toolchain=nightly"],
&[("RUSTUP_AUTO_INSTALL", "0")],
[("RUSTUP_AUTO_INSTALL", "0")],
)
.await
.with_stderr(snapbox::str![[r#"
Expand Down Expand Up @@ -1221,7 +1226,7 @@ error: toolchain 'nightly-[HOST_TRIPLE]' is not installed
cx.config
.expect_with_env(
["rustup", "target", "list", "--toolchain=nightly"],
&[("RUSTUP_AUTO_INSTALL", "0")],
[("RUSTUP_AUTO_INSTALL", "0")],
)
.await
.with_stderr(snapbox::str![[r#"
Expand All @@ -1234,7 +1239,7 @@ error: toolchain 'nightly-[HOST_TRIPLE]' is not installed
cx.config
.expect_with_env(
["rustup", "target", "list", "--toolchain=nightly"],
&[("RUSTUP_AUTO_INSTALL", "1")],
[("RUSTUP_AUTO_INSTALL", "1")],
)
.await
.is_ok();
Expand Down Expand Up @@ -1502,7 +1507,7 @@ async fn add_target_no_toolchain() {
CROSS_ARCH1,
"--toolchain=nightly",
],
&[("RUSTUP_AUTO_INSTALL", "0")],
[("RUSTUP_AUTO_INSTALL", "0")],
)
.await
.with_stderr(snapbox::str![[r#"
Expand Down Expand Up @@ -1725,7 +1730,7 @@ async fn remove_target_no_toolchain() {
CROSS_ARCH1,
"--toolchain=nightly",
],
&[("RUSTUP_AUTO_INSTALL", "0")],
[("RUSTUP_AUTO_INSTALL", "0")],
)
.await
.with_stderr(snapbox::str![[r#"
Expand Down
Loading