1- use std:: env:: consts:: EXE_SUFFIX ;
1+ use std:: env:: { consts:: EXE_SUFFIX , split_paths } ;
22use std:: ffi:: { OsStr , OsString } ;
3+ use std:: fmt;
34use std:: os:: windows:: ffi:: { OsStrExt , OsStringExt } ;
45use std:: path:: Path ;
56use std:: process:: Command ;
@@ -24,12 +25,18 @@ pub(crate) fn ensure_prompt() -> Result<()> {
2425 Ok ( ( ) )
2526}
2627
28+ #[ derive( PartialEq , Eq ) ]
29+ pub ( crate ) enum VsInstallPlan {
30+ Automatic ,
31+ Manual ,
32+ }
33+
2734// Provide guidance about setting up MSVC if it doesn't appear to be
2835// installed
29- pub ( crate ) fn do_msvc_check ( opts : & InstallOpts < ' _ > ) -> bool {
36+ pub ( crate ) fn do_msvc_check ( opts : & InstallOpts < ' _ > ) -> Option < VsInstallPlan > {
3037 // Test suite skips this since it's env dependent
3138 if process ( ) . var ( "RUSTUP_INIT_SKIP_MSVC_CHECK" ) . is_ok ( ) {
32- return true ;
39+ return None ;
3340 }
3441
3542 use cc:: windows_registry;
@@ -41,10 +48,102 @@ pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> bool {
4148 let installing_msvc = host_triple. contains ( "msvc" ) ;
4249 let have_msvc = windows_registry:: find_tool ( & host_triple, "cl.exe" ) . is_some ( ) ;
4350 if installing_msvc && !have_msvc {
44- return false ;
51+ // Visual Studio build tools are required.
52+ // If the user does not have Visual Studio installed and their host
53+ // machine is i686 or x86_64 then it's OK to try an auto install.
54+ // Otherwise a manual install will be required.
55+ let has_any_vs = windows_registry:: find_vs_version ( ) . is_ok ( ) ;
56+ let is_x86 = host_triple. contains ( "i686" ) || host_triple. contains ( "x86_64" ) ;
57+ if is_x86 && !has_any_vs {
58+ Some ( VsInstallPlan :: Automatic )
59+ } else {
60+ Some ( VsInstallPlan :: Manual )
61+ }
62+ } else {
63+ None
64+ }
65+ }
66+
67+ #[ derive( Debug ) ]
68+ struct VsInstallError ( i32 ) ;
69+ impl std:: error:: Error for VsInstallError { }
70+ impl fmt:: Display for VsInstallError {
71+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
72+ // See https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio?view=vs-2022#error-codes
73+ let message = match self . 0 {
74+ 740 => "elevation required" ,
75+ 1001 => "Visual Studio installer process is running" ,
76+ 1003 => "Visual Studio is in use" ,
77+ 1602 => "operation was canceled" ,
78+ 1618 => "another installation running" ,
79+ 1641 => "operation completed successfully, and reboot was initiated" ,
80+ 3010 => "operation completed successfully, but install requires reboot before it can be used" ,
81+ 5003 => "bootstrapper failed to download installer" ,
82+ 5004 => "operation was canceled" ,
83+ 5005 => "bootstrapper command-line parse error" ,
84+ 5007 => "operation was blocked - the computer does not meet the requirements" ,
85+ 8001 => "arm machine check failure" ,
86+ 8002 => "background download precheck failure" ,
87+ 8003 => "out of support selectable failure" ,
88+ 8004 => "target directory failure" ,
89+ 8005 => "verifying source payloads failure" ,
90+ 8006 => "Visual Studio processes running" ,
91+ -1073720687 => "connectivity failure" ,
92+ -1073741510 => "Microsoft Visual Studio Installer was terminated" ,
93+ _ => "error installing Visual Studio"
94+ } ;
95+ write ! ( f, "{} (exit code {})" , message, self . 0 )
4596 }
97+ }
4698
47- true
99+ pub ( crate ) fn try_install_msvc ( ) -> Result < ( ) > {
100+ // download the installer
101+ let visual_studio_url = utils:: parse_url ( "https://aka.ms/vs/17/release/vs_community.exe" ) ?;
102+
103+ let tempdir = tempfile:: Builder :: new ( )
104+ . prefix ( "rustup-visualstudio" )
105+ . tempdir ( )
106+ . context ( "error creating temp directory" ) ?;
107+
108+ let visual_studio = tempdir. path ( ) . join ( "vs_setup.exe" ) ;
109+ utils:: download_file ( & visual_studio_url, & visual_studio, None , & |_| ( ) ) ?;
110+
111+ // Run the installer. Arguments are documented at:
112+ // https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio
113+ let mut cmd = Command :: new ( visual_studio) ;
114+ cmd. arg ( "--wait" )
115+ // Display an interactive GUI focused on installing just the selected components.
116+ . arg ( "--focusedUi" )
117+ // Add the linker and C runtime libraries.
118+ . args ( [ "--add" , "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" ] ) ;
119+
120+ // It's possible an earlier or later version of the Windows SDK has been
121+ // installed separately from Visual Studio so installing it can be skipped.
122+ let mut has_libs = false ;
123+ if let Some ( paths) = process ( ) . var_os ( "lib" ) {
124+ for mut path in split_paths ( & paths) {
125+ path. push ( "kernel32.lib" ) ;
126+ if path. exists ( ) {
127+ has_libs = true ;
128+ }
129+ }
130+ } ;
131+ if !has_libs {
132+ cmd. args ( [
133+ "--add" ,
134+ "Microsoft.VisualStudio.Component.Windows11SDK.22000" ,
135+ ] ) ;
136+ }
137+ let exit_status = cmd
138+ . spawn ( )
139+ . and_then ( |mut child| child. wait ( ) )
140+ . context ( "error running Visual Studio installer" ) ?;
141+
142+ if exit_status. success ( ) {
143+ Ok ( ( ) )
144+ } else {
145+ Err ( VsInstallError ( exit_status. code ( ) . unwrap ( ) ) ) . context ( "failed to install Visual Studio" )
146+ }
48147}
49148
50149/// Run by rustup-gc-$num.exe to delete CARGO_HOME
0 commit comments