1
- use std:: env:: consts:: EXE_SUFFIX ;
1
+ use std:: env:: { consts:: EXE_SUFFIX , split_paths } ;
2
2
use std:: ffi:: { OsStr , OsString } ;
3
+ use std:: fmt;
3
4
use std:: os:: windows:: ffi:: { OsStrExt , OsStringExt } ;
4
5
use std:: path:: Path ;
5
6
use std:: process:: Command ;
@@ -24,12 +25,18 @@ pub(crate) fn ensure_prompt() -> Result<()> {
24
25
Ok ( ( ) )
25
26
}
26
27
28
+ #[ derive( PartialEq , Eq ) ]
29
+ pub ( crate ) enum VsInstallPlan {
30
+ Automatic ,
31
+ Manual ,
32
+ }
33
+
27
34
// Provide guidance about setting up MSVC if it doesn't appear to be
28
35
// installed
29
- pub ( crate ) fn do_msvc_check ( opts : & InstallOpts < ' _ > ) -> bool {
36
+ pub ( crate ) fn do_msvc_check ( opts : & InstallOpts < ' _ > ) -> Option < VsInstallPlan > {
30
37
// Test suite skips this since it's env dependent
31
38
if process ( ) . var ( "RUSTUP_INIT_SKIP_MSVC_CHECK" ) . is_ok ( ) {
32
- return true ;
39
+ return None ;
33
40
}
34
41
35
42
use cc:: windows_registry;
@@ -41,10 +48,102 @@ pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> bool {
41
48
let installing_msvc = host_triple. contains ( "msvc" ) ;
42
49
let have_msvc = windows_registry:: find_tool ( & host_triple, "cl.exe" ) . is_some ( ) ;
43
50
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 )
45
96
}
97
+ }
46
98
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
+ }
48
147
}
49
148
50
149
/// Run by rustup-gc-$num.exe to delete CARGO_HOME
0 commit comments