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