1- use std:: env:: consts:: EXE_SUFFIX ;
1+ use std:: cell:: RefCell ;
2+ use std:: env:: { consts:: EXE_SUFFIX , split_paths} ;
23use std:: ffi:: { OsStr , OsString } ;
4+ use std:: fmt;
35use std:: os:: windows:: ffi:: { OsStrExt , OsStringExt } ;
46use std:: path:: Path ;
57use std:: process:: Command ;
@@ -9,6 +11,7 @@ use anyhow::{anyhow, Context, Result};
911use super :: super :: errors:: * ;
1012use super :: common;
1113use super :: { install_bins, InstallOpts } ;
14+ use crate :: cli:: download_tracker:: DownloadTracker ;
1215use crate :: dist:: dist:: TargetTriple ;
1316use crate :: process;
1417use 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