From c053f757a1483a828a8f3bf258c6a0e875a9105e Mon Sep 17 00:00:00 2001 From: rami3l Date: Sun, 22 Sep 2024 14:51:40 +0800 Subject: [PATCH 1/5] refactor(cli/rustup-mode): rename `forced` to `force_non_host` --- src/cli/rustup_mode.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index bc8446ae3f..529a14d1ae 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -814,7 +814,7 @@ async fn update( let self_update = !self_update::NEVER_SELF_UPDATE && self_update_mode == SelfUpdateMode::Enable && !opts.no_self_update; - let forced = opts.force_non_host; + let force_non_host = opts.force_non_host; if let Some(p) = opts.profile { cfg.set_profile_override(p); } @@ -829,7 +829,12 @@ async fn update( if name.has_triple() { let host_arch = TargetTriple::from_host_or_build(cfg.process); let target_triple = name.clone().resolve(&host_arch)?.target; - common::warn_if_host_is_incompatible(&name, &host_arch, &target_triple, forced)?; + common::warn_if_host_is_incompatible( + &name, + &host_arch, + &target_triple, + force_non_host, + )?; } let desc = name.resolve(&cfg.get_default_host_triple()?)?; From 71c16522e5d2faceffafc9d4f3bf425f366684c3 Mon Sep 17 00:00:00 2001 From: rami3l Date: Sun, 22 Sep 2024 15:01:50 +0800 Subject: [PATCH 2/5] refactor(config)!: pass the `force_non_host` flag to `Cfg::ensure_installed()` --- src/cli/rustup_mode.rs | 6 ++++-- src/config.rs | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 529a14d1ae..429d5a4cbf 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -725,7 +725,7 @@ async fn default_( MaybeResolvableToolchainName::Some(ResolvableToolchainName::Official(toolchain)) => { let desc = toolchain.resolve(&cfg.get_default_host_triple()?)?; let status = cfg - .ensure_installed(&desc, vec![], vec![], None, true) + .ensure_installed(&desc, vec![], vec![], None, false, true) .await? .0; @@ -878,7 +878,9 @@ async fn update( exit_code &= common::self_update(|| Ok(()), cfg.process).await?; } } else if ensure_active_toolchain { - let (toolchain, reason) = cfg.find_or_install_active_toolchain(true).await?; + let (toolchain, reason) = cfg + .find_or_install_active_toolchain(force_non_host, true) + .await?; info!("the active toolchain `{toolchain}` has been installed"); info!("it's active because: {reason}"); } else { diff --git a/src/config.rs b/src/config.rs index d7a62d5d61..ef796e0914 100644 --- a/src/config.rs +++ b/src/config.rs @@ -742,6 +742,7 @@ impl<'a> Cfg<'a> { #[tracing::instrument(level = "trace", skip_all)] pub(crate) async fn find_or_install_active_toolchain( &self, + force_non_host: bool, verbose: bool, ) -> Result<(LocalToolchainName, ActiveReason)> { if let Some((override_config, reason)) = self.find_override_config()? { @@ -753,8 +754,15 @@ impl<'a> Cfg<'a> { profile, } = override_config { - self.ensure_installed(&toolchain, components, targets, profile, verbose) - .await?; + self.ensure_installed( + &toolchain, + components, + targets, + profile, + force_non_host, + verbose, + ) + .await?; } else { Toolchain::with_reason(self, toolchain.clone(), &reason)?; } @@ -762,7 +770,7 @@ impl<'a> Cfg<'a> { } else if let Some(toolchain) = self.get_default()? { let reason = ActiveReason::Default; if let ToolchainName::Official(desc) = &toolchain { - self.ensure_installed(desc, vec![], vec![], None, verbose) + self.ensure_installed(desc, vec![], vec![], None, force_non_host, verbose) .await?; } else { Toolchain::with_reason(self, toolchain.clone().into(), &reason)?; @@ -782,13 +790,14 @@ impl<'a> Cfg<'a> { components: Vec, targets: Vec, profile: Option, + force_non_host: bool, verbose: bool, ) -> Result<(UpdateStatus, Toolchain<'_>)> { common::warn_if_host_is_incompatible( toolchain, &TargetTriple::from_host_or_build(self.process), &toolchain.target, - false, + force_non_host, )?; if verbose { (self.notify_handler)(Notification::LookingForToolchain(toolchain)); From 8e3ef9fe952984433aefefe3d2ab356678d7bf60 Mon Sep 17 00:00:00 2001 From: rami3l Date: Sun, 22 Sep 2024 15:05:10 +0800 Subject: [PATCH 3/5] feat(cli/rustup-mode): add `--force-non-host` to `rustup default` --- src/cli/rustup_mode.rs | 12 ++++++++++-- .../rustup/rustup_default_cmd_help_flag_stdout.toml | 6 ++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 429d5a4cbf..a06baf02ed 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -161,6 +161,10 @@ enum RustupSubcmd { Default { #[arg(help = MAYBE_RESOLVABLE_TOOLCHAIN_ARG_HELP)] toolchain: Option, + + /// Install toolchains that require an emulator. See https://github.com/rust-lang/rustup/wiki/Non-host-toolchains + #[arg(long)] + force_non_host: bool, }, /// Modify or query the installed toolchains @@ -631,7 +635,10 @@ pub async fn main( ToolchainSubcmd::Uninstall { opts } => toolchain_remove(cfg, opts), }, RustupSubcmd::Check => check_updates(cfg).await, - RustupSubcmd::Default { toolchain } => default_(cfg, toolchain).await, + RustupSubcmd::Default { + toolchain, + force_non_host, + } => default_(cfg, toolchain, force_non_host).await, RustupSubcmd::Target { subcmd } => match subcmd { TargetSubcmd::List { toolchain, @@ -710,6 +717,7 @@ pub async fn main( async fn default_( cfg: &Cfg<'_>, toolchain: Option, + force_non_host: bool, ) -> Result { common::warn_if_host_is_emulated(cfg.process); @@ -725,7 +733,7 @@ async fn default_( MaybeResolvableToolchainName::Some(ResolvableToolchainName::Official(toolchain)) => { let desc = toolchain.resolve(&cfg.get_default_host_triple()?)?; let status = cfg - .ensure_installed(&desc, vec![], vec![], None, false, true) + .ensure_installed(&desc, vec![], vec![], None, force_non_host, true) .await? .0; diff --git a/tests/suite/cli-ui/rustup/rustup_default_cmd_help_flag_stdout.toml b/tests/suite/cli-ui/rustup/rustup_default_cmd_help_flag_stdout.toml index 0d9c0a5ce7..3c688e43f4 100644 --- a/tests/suite/cli-ui/rustup/rustup_default_cmd_help_flag_stdout.toml +++ b/tests/suite/cli-ui/rustup/rustup_default_cmd_help_flag_stdout.toml @@ -4,14 +4,16 @@ stdout = """ ... Set the default toolchain -Usage: rustup[EXE] default [TOOLCHAIN] +Usage: rustup[EXE] default [OPTIONS] [TOOLCHAIN] Arguments: [TOOLCHAIN] 'none', a toolchain name, such as 'stable', 'nightly', '1.8.0', or a custom toolchain name. For more information see `rustup help toolchain` Options: - -h, --help Print help + --force-non-host Install toolchains that require an emulator. See + https://github.com/rust-lang/rustup/wiki/Non-host-toolchains + -h, --help Print help Discussion: Sets the default toolchain to the one specified. If the toolchain From 7f6ea27a5c6336a8ae624aac2f7e577547f5d17e Mon Sep 17 00:00:00 2001 From: rami3l Date: Sun, 22 Sep 2024 15:25:53 +0800 Subject: [PATCH 4/5] refactor(cli/common)!: take in `toolchain: String` in `warn_if_host_is_incompatible()` --- src/cli/common.rs | 2 +- src/cli/rustup_mode.rs | 2 +- src/config.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli/common.rs b/src/cli/common.rs index 291ac86b78..e61877778c 100644 --- a/src/cli/common.rs +++ b/src/cli/common.rs @@ -629,7 +629,7 @@ pub(crate) fn ignorable_error( /// Warns if rustup is trying to install a toolchain that might not be /// able to run on the host system. pub(crate) fn warn_if_host_is_incompatible( - toolchain: impl Display, + toolchain: String, host_arch: &TargetTriple, target_triple: &TargetTriple, force_non_host: bool, diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index a06baf02ed..376bd34b27 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -838,7 +838,7 @@ async fn update( let host_arch = TargetTriple::from_host_or_build(cfg.process); let target_triple = name.clone().resolve(&host_arch)?.target; common::warn_if_host_is_incompatible( - &name, + name.to_string(), &host_arch, &target_triple, force_non_host, diff --git a/src/config.rs b/src/config.rs index ef796e0914..0d25d2cafd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -794,7 +794,7 @@ impl<'a> Cfg<'a> { verbose: bool, ) -> Result<(UpdateStatus, Toolchain<'_>)> { common::warn_if_host_is_incompatible( - toolchain, + toolchain.to_string(), &TargetTriple::from_host_or_build(self.process), &toolchain.target, force_non_host, From 73348d4054676b689b3c9492cb3b3e8bf5b7d41e Mon Sep 17 00:00:00 2001 From: rami3l Date: Sun, 22 Sep 2024 15:31:21 +0800 Subject: [PATCH 5/5] refactor(cli/common)!: deny installing a host-incompatible toolchain w/o `--force-non-host` --- src/cli/common.rs | 18 +++++++++++------- src/cli/rustup_mode.rs | 2 +- src/config.rs | 2 +- src/errors.rs | 9 +++++++++ tests/suite/cli_misc.rs | 2 +- tests/suite/cli_rustup.rs | 39 +++++++++++++++++++++++---------------- 6 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/cli/common.rs b/src/cli/common.rs index e61877778c..8495e6a703 100644 --- a/src/cli/common.rs +++ b/src/cli/common.rs @@ -22,6 +22,7 @@ use crate::{ dist::{ manifest::ComponentStatus, notifications as dist_notifications, TargetTriple, ToolchainDesc, }, + errors::RustupError, install::UpdateStatus, notifications::Notification, process::{terminalsource, Process}, @@ -626,9 +627,11 @@ pub(crate) fn ignorable_error( } } -/// Warns if rustup is trying to install a toolchain that might not be -/// able to run on the host system. -pub(crate) fn warn_if_host_is_incompatible( +/// Returns an error for a toolchain if both conditions are met: +/// - The toolchain has an incompatible target triple, +/// i.e. it might not be able to run on the host system. +/// - The `force_non_host` flag is set to `false`. +pub(crate) fn check_non_host_toolchain( toolchain: String, host_arch: &TargetTriple, target_triple: &TargetTriple, @@ -637,10 +640,11 @@ pub(crate) fn warn_if_host_is_incompatible( if force_non_host || host_arch.can_run(target_triple)? { return Ok(()); } - error!("DEPRECATED: future versions of rustup will require --force-non-host to install a non-host toolchain."); - warn!("toolchain '{toolchain}' may not be able to run on this system."); - warn!("If you meant to build software to target that platform, perhaps try `rustup target add {target_triple}` instead?"); - Ok(()) + Err(RustupError::ToolchainIncompatible { + toolchain, + target_triple: target_triple.clone(), + } + .into()) } /// Warns if rustup is running under emulation, such as macOS Rosetta diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 376bd34b27..9b84fe1a28 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -837,7 +837,7 @@ async fn update( if name.has_triple() { let host_arch = TargetTriple::from_host_or_build(cfg.process); let target_triple = name.clone().resolve(&host_arch)?.target; - common::warn_if_host_is_incompatible( + common::check_non_host_toolchain( name.to_string(), &host_arch, &target_triple, diff --git a/src/config.rs b/src/config.rs index 0d25d2cafd..9fc529e6e0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -793,7 +793,7 @@ impl<'a> Cfg<'a> { force_non_host: bool, verbose: bool, ) -> Result<(UpdateStatus, Toolchain<'_>)> { - common::warn_if_host_is_incompatible( + common::check_non_host_toolchain( toolchain.to_string(), &TargetTriple::from_host_or_build(self.process), &toolchain.target, diff --git a/src/errors.rs b/src/errors.rs index 0d13334839..d3ef0ca432 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -84,6 +84,15 @@ pub enum RustupError { }, #[error("command failed: '{}'", PathBuf::from(.name).display())] RunningCommand { name: OsString }, + #[error( + "toolchain '{toolchain}' may not be able to run on this system\n\ + note: to build software for that platform, try `rustup target add {target_triple}` instead\n\ + note: add the `--force-non-host` flag to install the toolchain anyway" + )] + ToolchainIncompatible { + toolchain: String, + target_triple: TargetTriple, + }, #[error("toolchain '{0}' is not installable")] ToolchainNotInstallable(String), #[error( diff --git a/tests/suite/cli_misc.rs b/tests/suite/cli_misc.rs index 69a13983b1..90a931db4a 100644 --- a/tests/suite/cli_misc.rs +++ b/tests/suite/cli_misc.rs @@ -210,7 +210,7 @@ async fn multi_host_smoke_test() { let mut cx = CliTestContext::new(Scenario::MultiHost).await; let toolchain = format!("nightly-{}", clitools::MULTI_ARCH1); cx.config - .expect_ok(&["rustup", "default", &toolchain]) + .expect_ok(&["rustup", "default", &toolchain, "--force-non-host"]) .await; cx.config .expect_stdout_ok(&["rustc", "--version"], "xxxx-nightly-2") diff --git a/tests/suite/cli_rustup.rs b/tests/suite/cli_rustup.rs index 41d8c496be..3fcf9760e7 100644 --- a/tests/suite/cli_rustup.rs +++ b/tests/suite/cli_rustup.rs @@ -754,6 +754,7 @@ async fn show_multiple_targets() { "rustup", "default", &format!("nightly-{}", clitools::MULTI_ARCH1), + "--force-non-host", ]) .await; cx.config @@ -801,6 +802,7 @@ async fn show_multiple_toolchains_and_targets() { "rustup", "default", &format!("nightly-{}", clitools::MULTI_ARCH1), + "--force-non-host", ]) .await; cx.config @@ -810,6 +812,7 @@ async fn show_multiple_toolchains_and_targets() { .expect_ok(&[ "rustup", "update", + "--force-non-host", &format!("stable-{}", clitools::MULTI_ARCH1), ]) .await; @@ -2731,29 +2734,33 @@ async fn check_unix_settings_fallback() { } #[tokio::test] -async fn warn_on_unmatch_build() { +async fn deny_incompatible_toolchain_install() { let cx = CliTestContext::new(Scenario::MultiHost).await; let arch = clitools::MULTI_ARCH1; - cx.config.expect_stderr_ok( - &["rustup", "toolchain", "install", &format!("nightly-{arch}")], - &format!( - r"warn: toolchain 'nightly-{arch}' may not be able to run on this system. -warn: If you meant to build software to target that platform, perhaps try `rustup target add {arch}` instead?", - ), - ).await; + cx.config + .expect_err( + &["rustup", "toolchain", "install", &format!("nightly-{arch}")], + &format!( + "error: toolchain 'nightly-{arch}' may not be able to run on this system +note: to build software for that platform, try `rustup target add {arch}` instead", + ), + ) + .await; } #[tokio::test] -async fn warn_on_unmatch_build_default() { +async fn deny_incompatible_toolchain_default() { let cx = CliTestContext::new(Scenario::MultiHost).await; let arch = clitools::MULTI_ARCH1; - cx.config.expect_stderr_ok( - &["rustup", "default", &format!("nightly-{arch}")], - &format!( - r"warn: toolchain 'nightly-{arch}' may not be able to run on this system. -warn: If you meant to build software to target that platform, perhaps try `rustup target add {arch}` instead?", - ), - ).await; + cx.config + .expect_err( + &["rustup", "default", &format!("nightly-{arch}")], + &format!( + "error: toolchain 'nightly-{arch}' may not be able to run on this system +note: to build software for that platform, try `rustup target add {arch}` instead", + ), + ) + .await; } #[tokio::test]