@@ -42,7 +42,8 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
42
42
use std:: env;
43
43
use std:: ffi:: OsString ;
44
44
use std:: fs;
45
- use std:: path:: PathBuf ;
45
+ use std:: io;
46
+ use std:: path:: { Path , PathBuf } ;
46
47
use self :: registry:: { RegistryKey , LOCAL_MACHINE } ;
47
48
48
49
// When finding the link.exe binary the 32-bit version is at the top level
@@ -103,16 +104,32 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
103
104
// VS shells, so we only want to start adding our own pieces if it's not
104
105
// set.
105
106
//
106
- // If we're adding our own pieces, then we need to add two primary
107
+ // If we're adding our own pieces, then we need to add a few primary
107
108
// directories to the default search path for the linker. The first is in
108
- // the VS install direcotry and the next is the Windows SDK directory.
109
+ // the VS install direcotry, the next is the Windows SDK directory, and the
110
+ // last is the possible UCRT installation directory.
111
+ //
112
+ // The UCRT is a recent addition to Visual Studio installs (2015 at the time
113
+ // of this writing), and it's in the normal windows SDK folder, but there
114
+ // apparently aren't registry keys pointing to it. As a result we detect the
115
+ // installation and then add it manually. This logic will probably need to
116
+ // be tweaked over time...
109
117
if env:: var_os ( "LIB" ) . is_none ( ) {
110
118
if let Some ( mut vs_install_dir) = vs_install_dir {
111
119
vs_install_dir. push ( "VC/lib" ) ;
112
120
vs_install_dir. push ( extra) ;
113
121
let mut arg = OsString :: from ( "/LIBPATH:" ) ;
114
122
arg. push ( & vs_install_dir) ;
115
123
cmd. arg ( arg) ;
124
+
125
+ if let Some ( ( ucrt_root, vers) ) = ucrt_install_dir ( & vs_install_dir) {
126
+ if let Some ( arch) = windows_sdk_v8_subdir ( sess) {
127
+ let mut arg = OsString :: from ( "/LIBPATH:" ) ;
128
+ arg. push ( ucrt_root. join ( "Lib" ) . join ( vers)
129
+ . join ( "ucrt" ) . join ( arch) ) ;
130
+ cmd. arg ( arg) ;
131
+ }
132
+ }
116
133
}
117
134
if let Some ( path) = get_windows_sdk_lib_path ( sess) {
118
135
let mut arg = OsString :: from ( "/LIBPATH:" ) ;
@@ -189,7 +206,7 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
189
206
return max_key
190
207
}
191
208
192
- fn get_windows_sdk_lib_path ( sess : & Session ) -> Option < PathBuf > {
209
+ fn get_windows_sdk_path ( ) -> Option < ( PathBuf , usize ) > {
193
210
let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows" ;
194
211
let key = LOCAL_MACHINE . open ( key. as_ref ( ) ) ;
195
212
let ( n, k) = match key. ok ( ) . as_ref ( ) . and_then ( max_version) {
@@ -199,10 +216,17 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
199
216
let mut parts = n. to_str ( ) . unwrap ( ) . trim_left_matches ( "v" ) . splitn ( 2 , "." ) ;
200
217
let major = parts. next ( ) . unwrap ( ) . parse :: < usize > ( ) . unwrap ( ) ;
201
218
let _minor = parts. next ( ) . unwrap ( ) . parse :: < usize > ( ) . unwrap ( ) ;
202
- let path = match k. query_str ( "InstallationFolder" ) {
203
- Ok ( p) => PathBuf :: from ( p) . join ( "Lib" ) ,
204
- Err ( ..) => return None ,
219
+ k. query_str ( "InstallationFolder" ) . ok ( ) . map ( |folder| {
220
+ ( PathBuf :: from ( folder) , major)
221
+ } )
222
+ }
223
+
224
+ fn get_windows_sdk_lib_path ( sess : & Session ) -> Option < PathBuf > {
225
+ let ( mut path, major) = match get_windows_sdk_path ( ) {
226
+ Some ( p) => p,
227
+ None => return None ,
205
228
} ;
229
+ path. push ( "Lib" ) ;
206
230
if major <= 7 {
207
231
// In Windows SDK 7.x, x86 libraries are directly in the Lib folder,
208
232
// x64 libraries are inside, and it's not necessary to link agains
@@ -218,11 +242,9 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
218
242
// depend on the version of the OS you're targeting. By default
219
243
// choose the newest, which usually corresponds to the version of
220
244
// the OS you've installed the SDK on.
221
- let extra = match & sess. target . target . arch [ ..] {
222
- "x86" => "x86" ,
223
- "x86_64" => "x64" ,
224
- "arm" => "arm" ,
225
- _ => return None ,
245
+ let extra = match windows_sdk_v8_subdir ( sess) {
246
+ Some ( e) => e,
247
+ None => return None ,
226
248
} ;
227
249
[ "winv6.3" , "win8" , "win7" ] . iter ( ) . map ( |p| path. join ( p) ) . find ( |part| {
228
250
fs:: metadata ( part) . is_ok ( )
@@ -231,6 +253,48 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
231
253
} )
232
254
}
233
255
}
256
+
257
+ fn windows_sdk_v8_subdir ( sess : & Session ) -> Option < & ' static str > {
258
+ match & sess. target . target . arch [ ..] {
259
+ "x86" => Some ( "x86" ) ,
260
+ "x86_64" => Some ( "x64" ) ,
261
+ "arm" => Some ( "arm" ) ,
262
+ _ => return None ,
263
+ }
264
+ }
265
+
266
+ fn ucrt_install_dir ( vs_install_dir : & Path ) -> Option < ( PathBuf , String ) > {
267
+ let is_vs_14 = vs_install_dir. iter ( ) . filter_map ( |p| p. to_str ( ) ) . any ( |s| {
268
+ s == "Microsoft Visual Studio 14.0"
269
+ } ) ;
270
+ if !is_vs_14 {
271
+ return None
272
+ }
273
+ let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots" ;
274
+ let sdk_dir = LOCAL_MACHINE . open ( key. as_ref ( ) ) . and_then ( |p| {
275
+ p. query_str ( "KitsRoot10" )
276
+ } ) . map ( PathBuf :: from) ;
277
+ let sdk_dir = match sdk_dir {
278
+ Ok ( p) => p,
279
+ Err ( ..) => return None ,
280
+ } ;
281
+ ( move || -> io:: Result < _ > {
282
+ let mut max = None ;
283
+ let mut max_s = None ;
284
+ for entry in try!( fs:: read_dir ( & sdk_dir. join ( "Lib" ) ) ) {
285
+ let entry = try!( entry) ;
286
+ if let Ok ( s) = entry. file_name ( ) . into_string ( ) {
287
+ if let Ok ( u) = s. replace ( "." , "" ) . parse :: < usize > ( ) {
288
+ if Some ( u) > max {
289
+ max = Some ( u) ;
290
+ max_s = Some ( s) ;
291
+ }
292
+ }
293
+ }
294
+ }
295
+ Ok ( max_s. map ( |m| ( sdk_dir, m) ) )
296
+ } ) ( ) . ok ( ) . and_then ( |x| x)
297
+ }
234
298
}
235
299
236
300
#[ cfg( not( windows) ) ]
0 commit comments