@@ -42,7 +42,8 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
4242 use std:: env;
4343 use std:: ffi:: OsString ;
4444 use std:: fs;
45- use std:: path:: PathBuf ;
45+ use std:: io;
46+ use std:: path:: { Path , PathBuf } ;
4647 use self :: registry:: { RegistryKey , LOCAL_MACHINE } ;
4748
4849 // 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 {
103104 // VS shells, so we only want to start adding our own pieces if it's not
104105 // set.
105106 //
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
107108 // 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...
109117 if env:: var_os ( "LIB" ) . is_none ( ) {
110118 if let Some ( mut vs_install_dir) = vs_install_dir {
111119 vs_install_dir. push ( "VC/lib" ) ;
112120 vs_install_dir. push ( extra) ;
113121 let mut arg = OsString :: from ( "/LIBPATH:" ) ;
114122 arg. push ( & vs_install_dir) ;
115123 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+ }
116133 }
117134 if let Some ( path) = get_windows_sdk_lib_path ( sess) {
118135 let mut arg = OsString :: from ( "/LIBPATH:" ) ;
@@ -189,7 +206,7 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
189206 return max_key
190207 }
191208
192- fn get_windows_sdk_lib_path ( sess : & Session ) -> Option < PathBuf > {
209+ fn get_windows_sdk_path ( ) -> Option < ( PathBuf , usize ) > {
193210 let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows" ;
194211 let key = LOCAL_MACHINE . open ( key. as_ref ( ) ) ;
195212 let ( n, k) = match key. ok ( ) . as_ref ( ) . and_then ( max_version) {
@@ -199,10 +216,17 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
199216 let mut parts = n. to_str ( ) . unwrap ( ) . trim_left_matches ( "v" ) . splitn ( 2 , "." ) ;
200217 let major = parts. next ( ) . unwrap ( ) . parse :: < usize > ( ) . unwrap ( ) ;
201218 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 ,
205228 } ;
229+ path. push ( "Lib" ) ;
206230 if major <= 7 {
207231 // In Windows SDK 7.x, x86 libraries are directly in the Lib folder,
208232 // x64 libraries are inside, and it's not necessary to link agains
@@ -218,11 +242,9 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
218242 // depend on the version of the OS you're targeting. By default
219243 // choose the newest, which usually corresponds to the version of
220244 // 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 ,
226248 } ;
227249 [ "winv6.3" , "win8" , "win7" ] . iter ( ) . map ( |p| path. join ( p) ) . find ( |part| {
228250 fs:: metadata ( part) . is_ok ( )
@@ -231,6 +253,48 @@ pub fn link_exe_cmd(sess: &Session) -> Command {
231253 } )
232254 }
233255 }
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+ }
234298}
235299
236300#[ cfg( not( windows) ) ]
0 commit comments