From 6787016bf62e63aa4a573b516a5bcc0997a438ad Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 10 Jul 2026 16:26:36 +0200 Subject: [PATCH 1/6] style(cli/self-update): reorganize imports --- src/cli/self_update.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index edb255fac0..e5761bfd66 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -32,19 +32,19 @@ //! Deleting the running binary during uninstall is tricky //! and racy on Windows. -use std::borrow::Cow; -use std::env::{self, consts::EXE_SUFFIX}; -use std::io; -use std::io::Write; -use std::path::{Component, MAIN_SEPARATOR, Path, PathBuf}; -use std::process::Command; -use std::str::FromStr; -use std::{fmt, fs}; +use std::{ + borrow::Cow, + env::{self, consts::EXE_SUFFIX}, + fmt, fs, + io::{self, Write}, + path::{Component, MAIN_SEPARATOR, Path, PathBuf}, + process::Command, + str::FromStr, +}; use anstyle::Style; use anyhow::{Context, Result, anyhow}; -use clap::ValueEnum; -use clap::builder::PossibleValue; +use clap::{ValueEnum, builder::PossibleValue}; use clap_cargo::style::{GOOD, WARN}; use itertools::Itertools; use same_file::{Handle, is_same_file}; From 5271fe90fa4bc5440a9dec5ba07a7d583ad76922 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 10 Jul 2026 16:22:34 +0200 Subject: [PATCH 2/6] refactor(cli/self-update): move message templates to `mod msg` --- src/cli/self_update.rs | 154 +------------------------------------ src/cli/self_update/msg.rs | 150 ++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 151 deletions(-) create mode 100644 src/cli/self_update/msg.rs diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index e5761bfd66..ef4c996508 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -74,6 +74,9 @@ use crate::{ utils::{self, ExitCode}, }; +#[macro_use] +mod msg; + #[cfg(unix)] mod shell; @@ -369,157 +372,6 @@ impl fmt::Display for SelfUpdateMode { } } -// The big installation messages. These are macros because the first -// argument of format! needs to be a literal. - -macro_rules! pre_install_msg_template { - ($platform_msg:literal) => { - concat!( - r" -# Welcome to Rust! - -This will download and install the official compiler for the Rust -programming language, and its package manager, Cargo. - -Rustup metadata and toolchains will be installed into the Rustup -home directory, located at: - - {rustup_home} - -This can be modified with the RUSTUP_HOME environment variable. - -The Cargo home directory is located at: - - {cargo_home} - -This can be modified with the CARGO_HOME environment variable. - -The `cargo`, `rustc`, `rustup` and other commands will be added to -Cargo's bin directory, located at: - - {cargo_home_bin} - -", - $platform_msg, - r#" - -You can uninstall at any time with `rustup self uninstall` and -these changes will be reverted. -"# - ) - }; -} - -#[cfg(not(windows))] -macro_rules! pre_install_msg_unix { - () => { - pre_install_msg_template!( - "This path will then be added to your `PATH` environment variable by -modifying the profile file{plural} located at: - -{rcfiles}" - ) - }; -} - -#[cfg(windows)] -macro_rules! pre_install_msg_win { - () => { - pre_install_msg_template!( - r#"This path will then be added to your `PATH` environment variable by -modifying the `PATH` registry key at `HKEY_CURRENT_USER\Environment`."# - ) - }; -} - -macro_rules! pre_install_msg_no_modify_path { - () => { - pre_install_msg_template!( - "This path needs to be in your `PATH` environment variable, -but will not be added automatically." - ) - }; -} - -#[cfg(not(windows))] -macro_rules! post_install_msg_unix { - () => { - r"# Rust is installed now. Great! - -To get started you may need to restart your current shell. -This would reload your `PATH` environment variable to include -Cargo's bin directory ({cargo_home}/bin). - -To configure your current shell, you need to source the -corresponding `env` file under {cargo_home}. - -Consider running the right command for your shell (note the leading DOT): -{source_env_lines}" - }; -} - -#[cfg(windows)] -macro_rules! post_install_msg_win { - () => { - r"# Rust is installed now. Great! - - -To get started you may need to restart your current shell. -This would reload its `PATH` environment variable to include -Cargo's bin directory ({cargo_home}\\bin). -" - }; -} - -#[cfg(not(windows))] -macro_rules! post_install_msg_unix_no_modify_path { - () => { - r"# Rust is installed now. Great! - -To get started you need Cargo's bin directory ({cargo_home}/bin) in your `PATH` -environment variable. This has not been done automatically. - -To configure your current shell, you need to source -the corresponding `env` file under {cargo_home}. - -Consider running the right command for your shell (note the leading DOT): -{source_env_lines}" - }; -} - -#[cfg(windows)] -macro_rules! post_install_msg_win_no_modify_path { - () => { - r"# Rust is installed now. Great! - -To get started you need Cargo's bin directory ({cargo_home}\\bin) in your `PATH` -environment variable. This has not been done automatically. -" - }; -} - -macro_rules! pre_uninstall_msg { - () => { - r"# Thanks for hacking in Rust! - -This will uninstall all Rust toolchains and data, and remove -`{cargo_home}/bin` from your `PATH` environment variable. - -" - }; -} - -macro_rules! pre_uninstall_msg_no_modify_path { - () => { - r"# Thanks for hacking in Rust! - -This will uninstall all Rust toolchains and data. -Your `PATH` environment variable will not be touched. - -" - }; -} - static DEFAULT_UPDATE_ROOT: &str = "https://static.rust-lang.org/rustup"; fn update_root(process: &Process) -> String { diff --git a/src/cli/self_update/msg.rs b/src/cli/self_update/msg.rs new file mode 100644 index 0000000000..fecaafc695 --- /dev/null +++ b/src/cli/self_update/msg.rs @@ -0,0 +1,150 @@ +//! The big installation messages. These are macros because the first +//! argument of format! needs to be a literal. + +macro_rules! pre_install_msg_template { + ($platform_msg:literal) => { + concat!( + r" +# Welcome to Rust! + +This will download and install the official compiler for the Rust +programming language, and its package manager, Cargo. + +Rustup metadata and toolchains will be installed into the Rustup +home directory, located at: + + {rustup_home} + +This can be modified with the RUSTUP_HOME environment variable. + +The Cargo home directory is located at: + + {cargo_home} + +This can be modified with the CARGO_HOME environment variable. + +The `cargo`, `rustc`, `rustup` and other commands will be added to +Cargo's bin directory, located at: + + {cargo_home_bin} + +", + $platform_msg, + r#" + +You can uninstall at any time with `rustup self uninstall` and +these changes will be reverted. +"# + ) + }; +} + +#[cfg(not(windows))] +macro_rules! pre_install_msg_unix { + () => { + pre_install_msg_template!( + "This path will then be added to your `PATH` environment variable by +modifying the profile file{plural} located at: + +{rcfiles}" + ) + }; +} + +#[cfg(windows)] +macro_rules! pre_install_msg_win { + () => { + pre_install_msg_template!( + r#"This path will then be added to your `PATH` environment variable by +modifying the `PATH` registry key at `HKEY_CURRENT_USER\Environment`."# + ) + }; +} + +macro_rules! pre_install_msg_no_modify_path { + () => { + pre_install_msg_template!( + "This path needs to be in your `PATH` environment variable, +but will not be added automatically." + ) + }; +} + +#[cfg(not(windows))] +macro_rules! post_install_msg_unix { + () => { + r"# Rust is installed now. Great! + +To get started you may need to restart your current shell. +This would reload your `PATH` environment variable to include +Cargo's bin directory ({cargo_home}/bin). + +To configure your current shell, you need to source the +corresponding `env` file under {cargo_home}. + +Consider running the right command for your shell (note the leading DOT): +{source_env_lines}" + }; +} + +#[cfg(windows)] +macro_rules! post_install_msg_win { + () => { + r"# Rust is installed now. Great! + + +To get started you may need to restart your current shell. +This would reload its `PATH` environment variable to include +Cargo's bin directory ({cargo_home}\\bin). +" + }; +} + +#[cfg(not(windows))] +macro_rules! post_install_msg_unix_no_modify_path { + () => { + r"# Rust is installed now. Great! + +To get started you need Cargo's bin directory ({cargo_home}/bin) in your `PATH` +environment variable. This has not been done automatically. + +To configure your current shell, you need to source +the corresponding `env` file under {cargo_home}. + +Consider running the right command for your shell (note the leading DOT): +{source_env_lines}" + }; +} + +#[cfg(windows)] +macro_rules! post_install_msg_win_no_modify_path { + () => { + r"# Rust is installed now. Great! + +To get started you need Cargo's bin directory ({cargo_home}\\bin) in your `PATH` +environment variable. This has not been done automatically. +" + }; +} + +macro_rules! pre_uninstall_msg { + () => { + r"# Thanks for hacking in Rust! + +This will uninstall all Rust toolchains and data, and remove +`{cargo_home}/bin` from your `PATH` environment variable. + +" + }; +} + +macro_rules! pre_uninstall_msg_no_modify_path { + () => { + r"# Thanks for hacking in Rust! + +This will uninstall all Rust toolchains and data. +Your `PATH` environment variable will not be touched. + +" + }; +} From 5c439ef30cf7d4f63190abb17572c97fe8b79f3e Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 10 Jul 2026 15:41:52 +0200 Subject: [PATCH 3/6] refactor(toolchain/names)!: make more clones during conversions explicit --- src/cli/common.rs | 2 +- src/cli/rustup_mode.rs | 2 +- src/cli/self_update.rs | 10 +++++----- src/config.rs | 2 +- src/install.rs | 14 ++++++++------ src/toolchain/distributable.rs | 2 +- src/toolchain/names.rs | 12 +++--------- 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/cli/common.rs b/src/cli/common.rs index 8ec17a13ec..09e9abe31e 100644 --- a/src/cli/common.rs +++ b/src/cli/common.rs @@ -173,7 +173,7 @@ fn show_channel_updates( // this is a bit strange: we don't supply the version we // presumably had (for Installed and Unchanged), so we query it // again. Perhaps we can do better. - let version = match Toolchain::new(cfg, name.into()) { + let version = match Toolchain::new(cfg, name.clone().into()) { Ok(t) => t.rustc_version(), Err(_) => String::from("(toolchain not installed)"), }; diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 14b48241ce..c02e219ab7 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -873,7 +873,7 @@ async fn default_( cfg.set_default(None)?; } MaybeResolvableToolchainName::Some(ResolvableToolchainName::Custom(toolchain_name)) => { - Toolchain::new(cfg, (&toolchain_name).into())?; + Toolchain::new(cfg, toolchain_name.clone().into())?; cfg.set_default(Some(&toolchain_name.into()))?; } MaybeResolvableToolchainName::Some(ResolvableToolchainName::Official(toolchain)) => { diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index ef4c996508..34f363f6ee 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -870,9 +870,9 @@ async fn maybe_install_rust(opts: InstallOpts<'_>, cfg: &mut Cfg<'_>) -> Result< let (components, targets) = (opts.components, opts.targets); let toolchain = opts.install(cfg)?; - if let Some(desc) = &toolchain { - let options = DistOptions::new(components, targets, desc, cfg.get_profile()?, true, cfg)?; - let status = if Toolchain::exists(cfg, &desc.into())? { + if let Some(desc) = toolchain { + let options = DistOptions::new(components, targets, &desc, cfg.get_profile()?, true, cfg)?; + let status = if Toolchain::exists(cfg, &desc.clone().into())? { warn!("Updating existing toolchain, profile choice will be ignored"); // If we have a partial install we might not be able to read content here. We could: // - fail and folk have to delete the partially present toolchain to recover @@ -887,11 +887,11 @@ async fn maybe_install_rust(opts: InstallOpts<'_>, cfg: &mut Cfg<'_>) -> Result< DistributableToolchain::install(options).await?.status }; - check_proxy_sanity(cfg.process, components, desc)?; + check_proxy_sanity(cfg.process, components, &desc)?; cfg.set_default(Some(&desc.clone().into()))?; writeln!(cfg.process.stdout().lock())?; - common::show_channel_update(cfg, PackageUpdate::Toolchain(desc.clone()), Ok(status))?; + common::show_channel_update(cfg, PackageUpdate::Toolchain(desc), Ok(status))?; } Ok(()) } diff --git a/src/config.rs b/src/config.rs index 84e267ea42..03e70d6a43 100644 --- a/src/config.rs +++ b/src/config.rs @@ -243,7 +243,7 @@ impl OverrideCfg { match self { Self::PathBased(path_based_name) => path_based_name.into(), Self::Custom(custom_name) => custom_name.into(), - Self::Official { toolchain, .. } => (&toolchain).into(), + Self::Official { toolchain, .. } => toolchain.into(), } } } diff --git a/src/install.rs b/src/install.rs index b7f4df76b9..bb5e275c8b 100644 --- a/src/install.rs +++ b/src/install.rs @@ -126,11 +126,12 @@ impl InstallMethod<'_, '_> { fn local_name(&self) -> LocalToolchainName { match self { - InstallMethod::Copy { dest, .. } => (*dest).into(), - InstallMethod::Link { dest, .. } => (*dest).into(), + InstallMethod::Copy { dest, .. } | InstallMethod::Link { dest, .. } => { + (*dest).clone().into() + } InstallMethod::Dist(DistOptions { toolchain: desc, .. - }) => (*desc).into(), + }) => (*desc).clone().into(), } } @@ -140,13 +141,14 @@ impl InstallMethod<'_, '_> { fn dest_path(&self) -> PathBuf { match self { - InstallMethod::Copy { cfg, dest, .. } => cfg.toolchain_path(&(*dest).into()), - InstallMethod::Link { cfg, dest, .. } => cfg.toolchain_path(&(*dest).into()), + InstallMethod::Copy { cfg, dest, .. } | InstallMethod::Link { cfg, dest, .. } => { + cfg.toolchain_path(&(*dest).clone().into()) + } InstallMethod::Dist(DistOptions { cfg, toolchain: desc, .. - }) => cfg.toolchain_path(&(*desc).into()), + }) => cfg.toolchain_path(&(*desc).clone().into()), } } } diff --git a/src/toolchain/distributable.rs b/src/toolchain/distributable.rs index a76776fa07..521bcead2f 100644 --- a/src/toolchain/distributable.rs +++ b/src/toolchain/distributable.rs @@ -58,7 +58,7 @@ impl<'a> DistributableToolchain<'a> { } pub(crate) fn new(cfg: &'a Cfg<'a>, desc: ToolchainDesc) -> Result { - Toolchain::new(cfg, (&desc).into()).map(|toolchain| Self { toolchain, desc }) + Toolchain::new(cfg, desc.clone().into()).map(|toolchain| Self { toolchain, desc }) } pub(crate) fn desc(&self) -> &ToolchainDesc { diff --git a/src/toolchain/names.rs b/src/toolchain/names.rs index 37d92b9912..64a923d923 100644 --- a/src/toolchain/names.rs +++ b/src/toolchain/names.rs @@ -326,15 +326,9 @@ pub(crate) enum LocalToolchainName { Path(PathBasedToolchainName), } -impl From<&ToolchainDesc> for LocalToolchainName { - fn from(value: &ToolchainDesc) -> Self { - ToolchainName::Official(value.to_owned()).into() - } -} - -impl From<&CustomToolchainName> for LocalToolchainName { - fn from(value: &CustomToolchainName) -> Self { - ToolchainName::Custom(value.to_owned()).into() +impl From for LocalToolchainName { + fn from(value: ToolchainDesc) -> Self { + ToolchainName::Official(value).into() } } From 94427487ead5de05ea3f89c9f8d903da3d27d7e8 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 10 Jul 2026 15:52:47 +0200 Subject: [PATCH 4/6] refactor(cli/self-update)!: rename `InstallOpts::install()` to `InstallOpts::prepare()` --- src/cli/self_update.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 34f363f6ee..0741e01a74 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -109,7 +109,10 @@ pub(crate) struct InstallOpts<'a> { } impl InstallOpts<'_> { - fn install(self, cfg: &mut Cfg<'_>) -> Result> { + /// Prepares rustup's configuration for installation, and determines whether a toolchain should + /// be installed based on the user's intent. Returns the toolchain to install, or `None` if none + /// should be installed. + fn prepare(self, cfg: &mut Cfg<'_>) -> Result> { let Self { default_host_tuple, default_toolchain, @@ -869,7 +872,7 @@ async fn maybe_install_rust(opts: InstallOpts<'_>, cfg: &mut Cfg<'_>) -> Result< } let (components, targets) = (opts.components, opts.targets); - let toolchain = opts.install(cfg)?; + let toolchain = opts.prepare(cfg)?; if let Some(desc) = toolchain { let options = DistOptions::new(components, targets, &desc, cfg.get_profile()?, true, cfg)?; let status = if Toolchain::exists(cfg, &desc.clone().into())? { @@ -1377,7 +1380,7 @@ mod tests { .unwrap() .resolve(&cfg.default_host_tuple().unwrap()) .unwrap(), - opts.install(&mut cfg) + opts.prepare(&mut cfg) .unwrap() // result .unwrap() // option ); From f7b32788453bc7126ce7a34557539c6548abf1e3 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 10 Jul 2026 16:07:19 +0200 Subject: [PATCH 5/6] refactor(cli/self-update)!: rename `maybe_install_rust()` to `InstallOpts::install_rust()` --- src/cli/self_update.rs | 102 +++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 0741e01a74..1ab2ad68c2 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -109,6 +109,57 @@ pub(crate) struct InstallOpts<'a> { } impl InstallOpts<'_> { + /// Installs the rustup binary and proxies, and installs a toolchain if specified. + async fn install_rust(self, cfg: &mut Cfg<'_>) -> Result<()> { + install_bins(cfg.process)?; + + #[cfg(unix)] + unix::do_write_env_files(cfg.process)?; + + if !self.no_modify_path { + do_add_to_path(cfg.process)?; + } + + // If RUSTUP_HOME is not set, make sure it exists + if cfg.process.var_os("RUSTUP_HOME").is_none() { + let home = cfg + .process + .home_dir() + .map(|p| p.join(".rustup")) + .ok_or_else(|| anyhow::anyhow!("could not find home dir to put .rustup in"))?; + + fs::create_dir_all(home).context("unable to create ~/.rustup")?; + } + + let (components, targets) = (self.components, self.targets); + let toolchain = self.prepare(cfg)?; + if let Some(desc) = toolchain { + let options = + DistOptions::new(components, targets, &desc, cfg.get_profile()?, true, cfg)?; + let status = if Toolchain::exists(cfg, &desc.clone().into())? { + warn!("Updating existing toolchain, profile choice will be ignored"); + // If we have a partial install we might not be able to read content here. We could: + // - fail and folk have to delete the partially present toolchain to recover + // - silently ignore it (and provide inconsistent metadata for reporting the install/update change) + // - delete the partial install and start over + // For now, we error. + let toolchain = DistributableToolchain::new(cfg, desc.clone())?; + InstallMethod::Dist(options.for_update(&toolchain, false)) + .install(None) + .await? + } else { + DistributableToolchain::install(options).await?.status + }; + + check_proxy_sanity(cfg.process, components, &desc)?; + + cfg.set_default(Some(&desc.clone().into()))?; + writeln!(cfg.process.stdout().lock())?; + common::show_channel_update(cfg, PackageUpdate::Toolchain(desc), Ok(status))?; + } + Ok(()) + } + /// Prepares rustup's configuration for installation, and determines whether a toolchain should /// be installed based on the user's intent. Returns the toolchain to install, or `None` if none /// should be installed. @@ -466,7 +517,7 @@ pub(crate) async fn install( } let no_modify_path = opts.no_modify_path; - if let Err(e) = maybe_install_rust(opts, cfg).await { + if let Err(e) = opts.install_rust(cfg).await { report_error(&e, cfg.process); // On windows, where installation happens in a console @@ -850,55 +901,6 @@ fn check_proxy_sanity(process: &Process, components: &[&str], desc: &ToolchainDe Ok(()) } -async fn maybe_install_rust(opts: InstallOpts<'_>, cfg: &mut Cfg<'_>) -> Result<()> { - install_bins(cfg.process)?; - - #[cfg(unix)] - unix::do_write_env_files(cfg.process)?; - - if !opts.no_modify_path { - do_add_to_path(cfg.process)?; - } - - // If RUSTUP_HOME is not set, make sure it exists - if cfg.process.var_os("RUSTUP_HOME").is_none() { - let home = cfg - .process - .home_dir() - .map(|p| p.join(".rustup")) - .ok_or_else(|| anyhow::anyhow!("could not find home dir to put .rustup in"))?; - - fs::create_dir_all(home).context("unable to create ~/.rustup")?; - } - - let (components, targets) = (opts.components, opts.targets); - let toolchain = opts.prepare(cfg)?; - if let Some(desc) = toolchain { - let options = DistOptions::new(components, targets, &desc, cfg.get_profile()?, true, cfg)?; - let status = if Toolchain::exists(cfg, &desc.clone().into())? { - warn!("Updating existing toolchain, profile choice will be ignored"); - // If we have a partial install we might not be able to read content here. We could: - // - fail and folk have to delete the partially present toolchain to recover - // - silently ignore it (and provide inconsistent metadata for reporting the install/update change) - // - delete the partial install and start over - // For now, we error. - let toolchain = DistributableToolchain::new(cfg, desc.clone())?; - InstallMethod::Dist(options.for_update(&toolchain, false)) - .install(None) - .await? - } else { - DistributableToolchain::install(options).await?.status - }; - - check_proxy_sanity(cfg.process, components, &desc)?; - - cfg.set_default(Some(&desc.clone().into()))?; - writeln!(cfg.process.stdout().lock())?; - common::show_channel_update(cfg, PackageUpdate::Toolchain(desc), Ok(status))?; - } - Ok(()) -} - /// Uninstall process: /// 1. Remove all installed toolchains. /// 2. Remove rustup home. From de49ea35dae624d468be6538331f774a713830de Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 10 Jul 2026 16:12:29 +0200 Subject: [PATCH 6/6] refactor(cli/self-update)!: rename `install()` to `InstallOpts::install()` --- src/cli/self_update.rs | 238 ++++++++++++++++++++--------------------- src/cli/setup_mode.rs | 2 +- 2 files changed, 117 insertions(+), 123 deletions(-) diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 1ab2ad68c2..6982988bf0 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -109,6 +109,122 @@ pub(crate) struct InstallOpts<'a> { } impl InstallOpts<'_> { + /// Installing is a simple matter of copying the running binary to + /// `$CARGO_HOME/bin`, hard-linking the various Rust tools to it, + /// and adding `$CARGO_HOME/bin` to PATH. + pub(crate) async fn install(mut self, no_prompt: bool, cfg: &mut Cfg<'_>) -> Result { + #[cfg_attr(not(unix), allow(unused_mut))] + let mut exit_code = ExitCode::SUCCESS; + + self.validate(cfg.process).map_err(|e| { + anyhow!( + "Pre-checks for host and toolchain failed: {e}\n\ + If you are unsure of suitable values, the 'stable' toolchain is the default.\n\ + Valid host tuples look something like: {}", + TargetTuple::from_host_or_build(cfg.process) + ) + })?; + + if cfg + .process + .var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS") + .is_none_or(|s| s != "yes") + { + check_existence_of_rustc_or_cargo_in_path(no_prompt, cfg.process)?; + check_existence_of_settings_file(cfg)?; + } + + #[cfg(unix)] + { + exit_code &= unix::do_anti_sudo_check(no_prompt, cfg.process)?; + } + + let mut term = cfg.process.stdout(); + + #[cfg(windows)] + windows::maybe_install_msvc(&mut term, no_prompt, &self, &*cfg).await?; + + if !no_prompt { + let msg = pre_install_msg(self.no_modify_path, cfg.process)?; + + md(&mut term, msg); + let mut customized_install = false; + loop { + md(&mut term, current_install_opts(&self, cfg.process)); + match common::confirm_advanced(customized_install, cfg.process)? { + Confirm::No => { + info!("aborting installation"); + return Ok(ExitCode::SUCCESS); + } + Confirm::Yes => break, + Confirm::Advanced => { + customized_install = true; + self.customize(cfg.process)?; + } + } + } + } + + let no_modify_path = self.no_modify_path; + if let Err(e) = self.install_rust(cfg).await { + report_error(&e, cfg.process); + + // On windows, where installation happens in a console + // that may have opened just for this purpose, give + // the user an opportunity to see the error before the + // window closes. + #[cfg(windows)] + if !no_prompt { + windows::ensure_prompt(cfg.process)?; + } + + return Ok(ExitCode::FAILURE); + } + + let cargo_home = canonical_cargo_home(cfg.process)?; + #[cfg(windows)] + let cargo_home = cargo_home.replace('\\', r"\\"); + #[cfg(windows)] + let msg = if no_modify_path { + format!( + post_install_msg_win_no_modify_path!(), + cargo_home = cargo_home + ) + } else { + format!(post_install_msg_win!(), cargo_home = cargo_home) + }; + #[cfg(not(windows))] + let source_env_lines = shell::build_source_env_lines(cfg.process); + #[cfg(not(windows))] + let msg = if no_modify_path { + format!( + post_install_msg_unix_no_modify_path!(), + cargo_home = cargo_home, + source_env_lines = source_env_lines, + ) + } else { + format!( + post_install_msg_unix!(), + cargo_home = cargo_home, + source_env_lines = source_env_lines, + ) + }; + md(&mut term, msg); + + #[cfg(unix)] + warn_if_default_linker_missing(cfg.process); + + #[cfg(windows)] + if !no_prompt { + // On windows, where installation happens in a console + // that may have opened just for this purpose, require + // the user to press a key to continue. + windows::ensure_prompt(cfg.process)?; + } + + Ok(exit_code) + } + /// Installs the rustup binary and proxies, and installs a toolchain if specified. async fn install_rust(self, cfg: &mut Cfg<'_>) -> Result<()> { install_bins(cfg.process)?; @@ -454,128 +570,6 @@ fn canonical_cargo_home(process: &Process) -> Result> { }) } -/// Installing is a simple matter of copying the running binary to -/// `$CARGO_HOME/bin`, hard-linking the various Rust tools to it, -/// and adding `$CARGO_HOME/bin` to PATH. -pub(crate) async fn install( - no_prompt: bool, - mut opts: InstallOpts<'_>, - cfg: &mut Cfg<'_>, -) -> Result { - #[cfg_attr(not(unix), allow(unused_mut))] - let mut exit_code = ExitCode::SUCCESS; - - opts.validate(cfg.process).map_err(|e| { - anyhow!( - "Pre-checks for host and toolchain failed: {e}\n\ - If you are unsure of suitable values, the 'stable' toolchain is the default.\n\ - Valid host tuples look something like: {}", - TargetTuple::from_host_or_build(cfg.process) - ) - })?; - - if cfg - .process - .var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS") - .is_none_or(|s| s != "yes") - { - check_existence_of_rustc_or_cargo_in_path(no_prompt, cfg.process)?; - check_existence_of_settings_file(cfg)?; - } - - #[cfg(unix)] - { - exit_code &= unix::do_anti_sudo_check(no_prompt, cfg.process)?; - } - - let mut term = cfg.process.stdout(); - - #[cfg(windows)] - windows::maybe_install_msvc(&mut term, no_prompt, &opts, &*cfg).await?; - - if !no_prompt { - let msg = pre_install_msg(opts.no_modify_path, cfg.process)?; - - md(&mut term, msg); - let mut customized_install = false; - loop { - md(&mut term, current_install_opts(&opts, cfg.process)); - match common::confirm_advanced(customized_install, cfg.process)? { - Confirm::No => { - info!("aborting installation"); - return Ok(ExitCode::SUCCESS); - } - Confirm::Yes => { - break; - } - Confirm::Advanced => { - customized_install = true; - opts.customize(cfg.process)?; - } - } - } - } - - let no_modify_path = opts.no_modify_path; - if let Err(e) = opts.install_rust(cfg).await { - report_error(&e, cfg.process); - - // On windows, where installation happens in a console - // that may have opened just for this purpose, give - // the user an opportunity to see the error before the - // window closes. - #[cfg(windows)] - if !no_prompt { - windows::ensure_prompt(cfg.process)?; - } - - return Ok(ExitCode::FAILURE); - } - - let cargo_home = canonical_cargo_home(cfg.process)?; - #[cfg(windows)] - let cargo_home = cargo_home.replace('\\', r"\\"); - #[cfg(windows)] - let msg = if no_modify_path { - format!( - post_install_msg_win_no_modify_path!(), - cargo_home = cargo_home - ) - } else { - format!(post_install_msg_win!(), cargo_home = cargo_home) - }; - #[cfg(not(windows))] - let source_env_lines = shell::build_source_env_lines(cfg.process); - #[cfg(not(windows))] - let msg = if no_modify_path { - format!( - post_install_msg_unix_no_modify_path!(), - cargo_home = cargo_home, - source_env_lines = source_env_lines, - ) - } else { - format!( - post_install_msg_unix!(), - cargo_home = cargo_home, - source_env_lines = source_env_lines, - ) - }; - md(&mut term, msg); - - #[cfg(unix)] - warn_if_default_linker_missing(cfg.process); - - #[cfg(windows)] - if !no_prompt { - // On windows, where installation happens in a console - // that may have opened just for this purpose, require - // the user to press a key to continue. - windows::ensure_prompt(cfg.process)?; - } - - Ok(exit_code) -} - fn rustc_or_cargo_exists_in_path(process: &Process) -> Result<()> { // Ignore rustc and cargo if present in $HOME/.cargo/bin or a few other directories #[allow(clippy::ptr_arg)] diff --git a/src/cli/setup_mode.rs b/src/cli/setup_mode.rs index 907c4e9f7f..923dc295d1 100644 --- a/src/cli/setup_mode.rs +++ b/src/cli/setup_mode.rs @@ -131,5 +131,5 @@ pub async fn main( }; let mut cfg = Cfg::from_env(current_dir, quiet, false, process)?; - self_update::install(no_prompt, opts, &mut cfg).await + opts.install(no_prompt, &mut cfg).await }