From 742867f375526559286e0b2f9b4fb354038697a9 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 3 Mar 2022 04:59:37 +0000 Subject: [PATCH 1/4] Offer to auto install VS 2022 If there are no MSVC build tools already installed then default to auto installing Visual Studio 2022 Community Edition. --- src/cli/self_update.rs | 34 +++++++++- src/cli/self_update/windows.rs | 109 +++++++++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 8 deletions(-) diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 702715c4a1..af21e0d109 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -276,7 +276,11 @@ static MSVC_MESSAGE: &str = r#"# Rust Visual C++ prerequisites Rust requires the Microsoft C++ build tools for Visual Studio 2013 or later, but they don't seem to be installed. -The easiest way to acquire the build tools is by installing Microsoft +"#; + +#[cfg(windows)] +static MSVC_MANUAL_INSTALL_MESSAGE: &str = r#" +You can acquire the build tools by installing Microsoft Visual C++ Build Tools 2019 which provides just the Visual C++ build tools: @@ -299,6 +303,15 @@ doing then it is fine to continue installation without the build tools, but otherwise, install the C++ build tools before proceeding. "#; +#[cfg(windows)] +static MSVC_AUTO_INSTALL_MESSAGE: &str = r#"# Rust Visual C++ prerequisites + +Rust requires a linker and Windows API libraries but they don't seem to be avaliable. + +These components can be acquired by installing Visual Studio. + +"#; + static UPDATE_ROOT: &str = "https://static.rust-lang.org/rustup"; /// `CARGO_HOME` suitable for display, possibly with $HOME @@ -353,11 +366,26 @@ pub(crate) fn install( let mut term = term2::stdout(); #[cfg(windows)] - if !do_msvc_check(&opts) { + if let Some(plan) = do_msvc_check(&opts) { if no_prompt { warn!("installing msvc toolchain without its prerequisites"); - } else { + } else if plan == VsInstallPlan::Automatic { + md(&mut term, MSVC_AUTO_INSTALL_MESSAGE); + if common::confirm( + "Automatically download and install Visual Studio 2022 Community edition? (Y/n)", + true, + )? { + try_install_msvc()?; + } else { + md(&mut term, MSVC_MANUAL_INSTALL_MESSAGE); + if !common::confirm("\nContinue? (y/N)", false)? { + info!("aborting installation"); + return Ok(utils::ExitCode(0)); + } + } + } else if plan == VsInstallPlan::Manual { md(&mut term, MSVC_MESSAGE); + md(&mut term, MSVC_MANUAL_INSTALL_MESSAGE); if !common::confirm("\nContinue? (y/N)", false)? { info!("aborting installation"); return Ok(utils::ExitCode(0)); diff --git a/src/cli/self_update/windows.rs b/src/cli/self_update/windows.rs index 9291a79594..1286b267ff 100644 --- a/src/cli/self_update/windows.rs +++ b/src/cli/self_update/windows.rs @@ -1,5 +1,6 @@ -use std::env::consts::EXE_SUFFIX; +use std::env::{consts::EXE_SUFFIX, split_paths}; use std::ffi::{OsStr, OsString}; +use std::fmt; use std::os::windows::ffi::{OsStrExt, OsStringExt}; use std::path::Path; use std::process::Command; @@ -24,12 +25,18 @@ pub(crate) fn ensure_prompt() -> Result<()> { Ok(()) } +#[derive(PartialEq, Eq)] +pub(crate) enum VsInstallPlan { + Automatic, + Manual, +} + // Provide guidance about setting up MSVC if it doesn't appear to be // installed -pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> bool { +pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> Option { // Test suite skips this since it's env dependent if process().var("RUSTUP_INIT_SKIP_MSVC_CHECK").is_ok() { - return true; + return None; } use cc::windows_registry; @@ -41,10 +48,102 @@ pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> bool { let installing_msvc = host_triple.contains("msvc"); let have_msvc = windows_registry::find_tool(&host_triple, "cl.exe").is_some(); if installing_msvc && !have_msvc { - return false; + // Visual Studio build tools are required. + // If the user does not have Visual Studio installed and their host + // machine is i686 or x86_64 then it's OK to try an auto install. + // Otherwise a manual install will be required. + let has_any_vs = windows_registry::find_vs_version().is_ok(); + let is_x86 = host_triple.contains("i686") || host_triple.contains("x86_64"); + if is_x86 && !has_any_vs { + Some(VsInstallPlan::Automatic) + } else { + Some(VsInstallPlan::Manual) + } + } else { + None + } +} + +#[derive(Debug)] +struct VsInstallError(i32); +impl std::error::Error for VsInstallError {} +impl fmt::Display for VsInstallError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // See https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio?view=vs-2022#error-codes + let message = match self.0 { + 740 => "elevation required", + 1001 => "Visual Studio installer process is running", + 1003 => "Visual Studio is in use", + 1602 => "operation was canceled", + 1618 => "another installation running", + 1641 => "operation completed successfully, and reboot was initiated", + 3010 => "operation completed successfully, but install requires reboot before it can be used", + 5003 => "bootstrapper failed to download installer", + 5004 => "operation was canceled", + 5005 => "bootstrapper command-line parse error", + 5007 => "operation was blocked - the computer does not meet the requirements", + 8001 => "arm machine check failure", + 8002 => "background download precheck failure", + 8003 => "out of support selectable failure", + 8004 => "target directory failure", + 8005 => "verifying source payloads failure", + 8006 => "Visual Studio processes running", + -1073720687 => "connectivity failure", + -1073741510 => "Microsoft Visual Studio Installer was terminated", + _ => "error installing Visual Studio" + }; + write!(f, "{} (exit code {})", message, self.0) } +} - true +pub(crate) fn try_install_msvc() -> Result<()> { + // download the installer + let visual_studio_url = utils::parse_url("https://aka.ms/vs/17/release/vs_community.exe")?; + + let tempdir = tempfile::Builder::new() + .prefix("rustup-visualstudio") + .tempdir() + .context("error creating temp directory")?; + + let visual_studio = tempdir.path().join("vs_setup.exe"); + utils::download_file(&visual_studio_url, &visual_studio, None, &|_| ())?; + + // Run the installer. Arguments are documented at: + // https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio + let mut cmd = Command::new(visual_studio); + cmd.arg("--wait") + // Display an interactive GUI focused on installing just the selected components. + .arg("--focusedUi") + // Add the linker and C runtime libraries. + .args(["--add", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"]); + + // It's possible an earlier or later version of the Windows SDK has been + // installed separately from Visual Studio so installing it can be skipped. + let mut has_libs = false; + if let Some(paths) = process().var_os("lib") { + for mut path in split_paths(&paths) { + path.push("kernel32.lib"); + if path.exists() { + has_libs = true; + } + } + }; + if !has_libs { + cmd.args([ + "--add", + "Microsoft.VisualStudio.Component.Windows11SDK.22000", + ]); + } + let exit_status = cmd + .spawn() + .and_then(|mut child| child.wait()) + .context("error running Visual Studio installer")?; + + if exit_status.success() { + Ok(()) + } else { + Err(VsInstallError(exit_status.code().unwrap())).context("failed to install Visual Studio") + } } /// Run by rustup-gc-$num.exe to delete CARGO_HOME From da502023cf4ce291f00ef8e8c2edbe9f3d46f784 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 3 Mar 2022 15:43:35 +0000 Subject: [PATCH 2/4] Don't auto install VS for quiet installs --- src/cli/self_update.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index af21e0d109..94dda23179 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -369,7 +369,7 @@ pub(crate) fn install( if let Some(plan) = do_msvc_check(&opts) { if no_prompt { warn!("installing msvc toolchain without its prerequisites"); - } else if plan == VsInstallPlan::Automatic { + } else if !quiet && plan == VsInstallPlan::Automatic { md(&mut term, MSVC_AUTO_INSTALL_MESSAGE); if common::confirm( "Automatically download and install Visual Studio 2022 Community edition? (Y/n)", @@ -383,7 +383,7 @@ pub(crate) fn install( return Ok(utils::ExitCode(0)); } } - } else if plan == VsInstallPlan::Manual { + } else { md(&mut term, MSVC_MESSAGE); md(&mut term, MSVC_MANUAL_INSTALL_MESSAGE); if !common::confirm("\nContinue? (y/N)", false)? { From 6ca0869632bb63f1b2de174cee54a6b241dab99b Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Fri, 11 Mar 2022 22:05:53 +0000 Subject: [PATCH 3/4] Track msvc installer download progress --- src/cli/self_update/windows.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cli/self_update/windows.rs b/src/cli/self_update/windows.rs index 1286b267ff..0c1c2ac40e 100644 --- a/src/cli/self_update/windows.rs +++ b/src/cli/self_update/windows.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::env::{consts::EXE_SUFFIX, split_paths}; use std::ffi::{OsStr, OsString}; use std::fmt; @@ -10,6 +11,7 @@ use anyhow::{anyhow, Context, Result}; use super::super::errors::*; use super::common; use super::{install_bins, InstallOpts}; +use crate::cli::download_tracker::DownloadTracker; use crate::dist::dist::TargetTriple; use crate::process; use crate::utils::utils; @@ -106,7 +108,15 @@ pub(crate) fn try_install_msvc() -> Result<()> { .context("error creating temp directory")?; let visual_studio = tempdir.path().join("vs_setup.exe"); - utils::download_file(&visual_studio_url, &visual_studio, None, &|_| ())?; + let download_tracker = RefCell::new(DownloadTracker::new().with_display_progress(true)); + download_tracker.borrow_mut().download_finished(); + utils::download_file(&visual_studio_url, &visual_studio, None, &move |n| { + download_tracker + .borrow_mut() + .handle_notification(&crate::Notification::Install( + crate::dist::Notification::Utils(n), + )); + })?; // Run the installer. Arguments are documented at: // https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio From 66174317e7ed3ab9512911db205c14cfe2f82ac8 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 12 Mar 2022 01:19:07 +0000 Subject: [PATCH 4/4] Provide more information on progress --- src/cli/self_update.rs | 2 +- src/cli/self_update/windows.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 94dda23179..91ecb9e4a6 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -372,7 +372,7 @@ pub(crate) fn install( } else if !quiet && plan == VsInstallPlan::Automatic { md(&mut term, MSVC_AUTO_INSTALL_MESSAGE); if common::confirm( - "Automatically download and install Visual Studio 2022 Community edition? (Y/n)", + "\nAutomatically download and install Visual Studio 2022 Community edition? (Y/n)", true, )? { try_install_msvc()?; diff --git a/src/cli/self_update/windows.rs b/src/cli/self_update/windows.rs index 0c1c2ac40e..fac6a8bf18 100644 --- a/src/cli/self_update/windows.rs +++ b/src/cli/self_update/windows.rs @@ -110,6 +110,8 @@ pub(crate) fn try_install_msvc() -> Result<()> { let visual_studio = tempdir.path().join("vs_setup.exe"); let download_tracker = RefCell::new(DownloadTracker::new().with_display_progress(true)); download_tracker.borrow_mut().download_finished(); + + info!("downloading Visual Studio installer"); utils::download_file(&visual_studio_url, &visual_studio, None, &move |n| { download_tracker .borrow_mut() @@ -144,6 +146,8 @@ pub(crate) fn try_install_msvc() -> Result<()> { "Microsoft.VisualStudio.Component.Windows11SDK.22000", ]); } + info!("running the Visual Studio install"); + info!("rustup will continue once Visual Studio installation is complete\n"); let exit_status = cmd .spawn() .and_then(|mut child| child.wait())