Skip to content

Commit b23f17c

Browse files
authored
Merge pull request #2954 from ChrisDenton/vsinstaller
Offer to auto install VS 2022
2 parents f363bfe + 6617431 commit b23f17c

File tree

2 files changed

+148
-7
lines changed

2 files changed

+148
-7
lines changed

src/cli/self_update.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,11 @@ static MSVC_MESSAGE: &str = r#"# Rust Visual C++ prerequisites
276276
Rust requires the Microsoft C++ build tools for Visual Studio 2013 or
277277
later, but they don't seem to be installed.
278278
279-
The easiest way to acquire the build tools is by installing Microsoft
279+
"#;
280+
281+
#[cfg(windows)]
282+
static MSVC_MANUAL_INSTALL_MESSAGE: &str = r#"
283+
You can acquire the build tools by installing Microsoft
280284
Visual C++ Build Tools 2019 which provides just the Visual C++ build
281285
tools:
282286
@@ -299,6 +303,15 @@ doing then it is fine to continue installation without the build
299303
tools, but otherwise, install the C++ build tools before proceeding.
300304
"#;
301305

306+
#[cfg(windows)]
307+
static MSVC_AUTO_INSTALL_MESSAGE: &str = r#"# Rust Visual C++ prerequisites
308+
309+
Rust requires a linker and Windows API libraries but they don't seem to be avaliable.
310+
311+
These components can be acquired by installing Visual Studio.
312+
313+
"#;
314+
302315
static UPDATE_ROOT: &str = "https://static.rust-lang.org/rustup";
303316

304317
/// `CARGO_HOME` suitable for display, possibly with $HOME
@@ -353,11 +366,26 @@ pub(crate) fn install(
353366
let mut term = term2::stdout();
354367

355368
#[cfg(windows)]
356-
if !do_msvc_check(&opts) {
369+
if let Some(plan) = do_msvc_check(&opts) {
357370
if no_prompt {
358371
warn!("installing msvc toolchain without its prerequisites");
372+
} else if !quiet && plan == VsInstallPlan::Automatic {
373+
md(&mut term, MSVC_AUTO_INSTALL_MESSAGE);
374+
if common::confirm(
375+
"\nAutomatically download and install Visual Studio 2022 Community edition? (Y/n)",
376+
true,
377+
)? {
378+
try_install_msvc()?;
379+
} else {
380+
md(&mut term, MSVC_MANUAL_INSTALL_MESSAGE);
381+
if !common::confirm("\nContinue? (y/N)", false)? {
382+
info!("aborting installation");
383+
return Ok(utils::ExitCode(0));
384+
}
385+
}
359386
} else {
360387
md(&mut term, MSVC_MESSAGE);
388+
md(&mut term, MSVC_MANUAL_INSTALL_MESSAGE);
361389
if !common::confirm("\nContinue? (y/N)", false)? {
362390
info!("aborting installation");
363391
return Ok(utils::ExitCode(0));

src/cli/self_update/windows.rs

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use std::env::consts::EXE_SUFFIX;
1+
use std::cell::RefCell;
2+
use std::env::{consts::EXE_SUFFIX, split_paths};
23
use std::ffi::{OsStr, OsString};
4+
use std::fmt;
35
use std::os::windows::ffi::{OsStrExt, OsStringExt};
46
use std::path::Path;
57
use std::process::Command;
@@ -9,6 +11,7 @@ use anyhow::{anyhow, Context, Result};
911
use super::super::errors::*;
1012
use super::common;
1113
use super::{install_bins, InstallOpts};
14+
use crate::cli::download_tracker::DownloadTracker;
1215
use crate::dist::dist::TargetTriple;
1316
use crate::process;
1417
use crate::utils::utils;
@@ -24,12 +27,18 @@ pub(crate) fn ensure_prompt() -> Result<()> {
2427
Ok(())
2528
}
2629

30+
#[derive(PartialEq, Eq)]
31+
pub(crate) enum VsInstallPlan {
32+
Automatic,
33+
Manual,
34+
}
35+
2736
// Provide guidance about setting up MSVC if it doesn't appear to be
2837
// installed
29-
pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> bool {
38+
pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> Option<VsInstallPlan> {
3039
// Test suite skips this since it's env dependent
3140
if process().var("RUSTUP_INIT_SKIP_MSVC_CHECK").is_ok() {
32-
return true;
41+
return None;
3342
}
3443

3544
use cc::windows_registry;
@@ -41,10 +50,114 @@ pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> bool {
4150
let installing_msvc = host_triple.contains("msvc");
4251
let have_msvc = windows_registry::find_tool(&host_triple, "cl.exe").is_some();
4352
if installing_msvc && !have_msvc {
44-
return false;
53+
// Visual Studio build tools are required.
54+
// If the user does not have Visual Studio installed and their host
55+
// machine is i686 or x86_64 then it's OK to try an auto install.
56+
// Otherwise a manual install will be required.
57+
let has_any_vs = windows_registry::find_vs_version().is_ok();
58+
let is_x86 = host_triple.contains("i686") || host_triple.contains("x86_64");
59+
if is_x86 && !has_any_vs {
60+
Some(VsInstallPlan::Automatic)
61+
} else {
62+
Some(VsInstallPlan::Manual)
63+
}
64+
} else {
65+
None
66+
}
67+
}
68+
69+
#[derive(Debug)]
70+
struct VsInstallError(i32);
71+
impl std::error::Error for VsInstallError {}
72+
impl fmt::Display for VsInstallError {
73+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74+
// See https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio?view=vs-2022#error-codes
75+
let message = match self.0 {
76+
740 => "elevation required",
77+
1001 => "Visual Studio installer process is running",
78+
1003 => "Visual Studio is in use",
79+
1602 => "operation was canceled",
80+
1618 => "another installation running",
81+
1641 => "operation completed successfully, and reboot was initiated",
82+
3010 => "operation completed successfully, but install requires reboot before it can be used",
83+
5003 => "bootstrapper failed to download installer",
84+
5004 => "operation was canceled",
85+
5005 => "bootstrapper command-line parse error",
86+
5007 => "operation was blocked - the computer does not meet the requirements",
87+
8001 => "arm machine check failure",
88+
8002 => "background download precheck failure",
89+
8003 => "out of support selectable failure",
90+
8004 => "target directory failure",
91+
8005 => "verifying source payloads failure",
92+
8006 => "Visual Studio processes running",
93+
-1073720687 => "connectivity failure",
94+
-1073741510 => "Microsoft Visual Studio Installer was terminated",
95+
_ => "error installing Visual Studio"
96+
};
97+
write!(f, "{} (exit code {})", message, self.0)
4598
}
99+
}
46100

47-
true
101+
pub(crate) fn try_install_msvc() -> Result<()> {
102+
// download the installer
103+
let visual_studio_url = utils::parse_url("https://aka.ms/vs/17/release/vs_community.exe")?;
104+
105+
let tempdir = tempfile::Builder::new()
106+
.prefix("rustup-visualstudio")
107+
.tempdir()
108+
.context("error creating temp directory")?;
109+
110+
let visual_studio = tempdir.path().join("vs_setup.exe");
111+
let download_tracker = RefCell::new(DownloadTracker::new().with_display_progress(true));
112+
download_tracker.borrow_mut().download_finished();
113+
114+
info!("downloading Visual Studio installer");
115+
utils::download_file(&visual_studio_url, &visual_studio, None, &move |n| {
116+
download_tracker
117+
.borrow_mut()
118+
.handle_notification(&crate::Notification::Install(
119+
crate::dist::Notification::Utils(n),
120+
));
121+
})?;
122+
123+
// Run the installer. Arguments are documented at:
124+
// https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio
125+
let mut cmd = Command::new(visual_studio);
126+
cmd.arg("--wait")
127+
// Display an interactive GUI focused on installing just the selected components.
128+
.arg("--focusedUi")
129+
// Add the linker and C runtime libraries.
130+
.args(["--add", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"]);
131+
132+
// It's possible an earlier or later version of the Windows SDK has been
133+
// installed separately from Visual Studio so installing it can be skipped.
134+
let mut has_libs = false;
135+
if let Some(paths) = process().var_os("lib") {
136+
for mut path in split_paths(&paths) {
137+
path.push("kernel32.lib");
138+
if path.exists() {
139+
has_libs = true;
140+
}
141+
}
142+
};
143+
if !has_libs {
144+
cmd.args([
145+
"--add",
146+
"Microsoft.VisualStudio.Component.Windows11SDK.22000",
147+
]);
148+
}
149+
info!("running the Visual Studio install");
150+
info!("rustup will continue once Visual Studio installation is complete\n");
151+
let exit_status = cmd
152+
.spawn()
153+
.and_then(|mut child| child.wait())
154+
.context("error running Visual Studio installer")?;
155+
156+
if exit_status.success() {
157+
Ok(())
158+
} else {
159+
Err(VsInstallError(exit_status.code().unwrap())).context("failed to install Visual Studio")
160+
}
48161
}
49162

50163
/// Run by rustup-gc-$num.exe to delete CARGO_HOME

0 commit comments

Comments
 (0)