Skip to content

Commit

Permalink
Merge pull request #2954 from ChrisDenton/vsinstaller
Browse files Browse the repository at this point in the history
Offer to auto install VS 2022
  • Loading branch information
kinnison authored Mar 12, 2022
2 parents f363bfe + 6617431 commit b23f17c
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 7 deletions.
32 changes: 30 additions & 2 deletions src/cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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 if !quiet && plan == VsInstallPlan::Automatic {
md(&mut term, MSVC_AUTO_INSTALL_MESSAGE);
if common::confirm(
"\nAutomatically 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 {
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));
Expand Down
123 changes: 118 additions & 5 deletions src/cli/self_update/windows.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::env::consts::EXE_SUFFIX;
use std::cell::RefCell;
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;
Expand All @@ -9,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;
Expand All @@ -24,12 +27,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<VsInstallPlan> {
// 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;
Expand All @@ -41,10 +50,114 @@ 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");
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()
.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
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",
]);
}
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())
.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
Expand Down

0 comments on commit b23f17c

Please sign in to comment.