@@ -163,6 +163,10 @@ mod impl_ {
163163 use crate :: windows:: registry:: { RegistryKey , LOCAL_MACHINE } ;
164164 use crate :: windows:: setup_config:: SetupConfiguration ;
165165 use crate :: windows:: vs_instances:: { VsInstances , VswhereInstance } ;
166+ use crate :: windows:: windows_sys:: {
167+ GetMachineTypeAttributes , GetProcAddress , LoadLibraryA , UserEnabled ,
168+ IMAGE_FILE_MACHINE_AMD64 , S_OK ,
169+ } ;
166170 use std:: convert:: TryFrom ;
167171 use std:: env;
168172 use std:: ffi:: OsString ;
@@ -199,6 +203,35 @@ mod impl_ {
199203 include : Vec < PathBuf > ,
200204 }
201205
206+ unsafe fn cast_to_function < F : Sized > (
207+ raw_func : unsafe extern "system" fn ( ) -> isize ,
208+ _func_type : & F ,
209+ ) -> F {
210+ std:: mem:: transmute_copy ( & raw_func)
211+ }
212+
213+ fn is_amd64_emulation_supported ( ) -> bool {
214+ // In theory we should free the library handle, but since kernel32.dll is loaded in every
215+ // process and we're a short-lived process, leaking the handles doesn't matter.
216+ let kernel32 = unsafe { LoadLibraryA ( b"kernel32.dll\0 " . as_ptr ( ) as _ ) } ;
217+ if let Some ( get_machine_type_attributes) =
218+ unsafe { GetProcAddress ( kernel32, b"GetMachineTypeAttributes\0 " . as_ptr ( ) as _ ) }
219+ {
220+ let mut attributes = Default :: default ( ) ;
221+ let get_machine_type_attributes =
222+ unsafe { cast_to_function ( get_machine_type_attributes, & GetMachineTypeAttributes ) } ;
223+ if unsafe {
224+ get_machine_type_attributes ( IMAGE_FILE_MACHINE_AMD64 , & mut attributes) == S_OK
225+ } {
226+ ( attributes & UserEnabled ) != 0
227+ } else {
228+ false
229+ }
230+ } else {
231+ false
232+ }
233+ }
234+
202235 impl MsvcTool {
203236 fn new ( tool : PathBuf ) -> MsvcTool {
204237 MsvcTool {
@@ -226,7 +259,6 @@ mod impl_ {
226259
227260 /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
228261 /// given target's arch. Returns `None` if the variable does not exist.
229- #[ cfg( windows) ]
230262 fn is_vscmd_target ( target : TargetArch < ' _ > ) -> Option < bool > {
231263 let vscmd_arch = env:: var ( "VSCMD_ARG_TGT_ARCH" ) . ok ( ) ?;
232264 // Convert the Rust target arch to its VS arch equivalent.
@@ -482,13 +514,21 @@ mod impl_ {
482514 ) -> Option < ( PathBuf , PathBuf , PathBuf , PathBuf , Option < PathBuf > , PathBuf ) > {
483515 let version = vs15plus_vc_read_version ( instance_path) ?;
484516
517+ let _ = is_amd64_emulation_supported ( ) ;
485518 let hosts = match host_arch ( ) {
486- X86 => vec ! [ "X86" ] ,
487- X86_64 => vec ! [ "X64" ] ,
488- // Starting with VS 17.3, there is a natively hosted compiler on ARM64.
489- // On older versions of VS, we use the x86 toolchain under emulation.
490- // We don't want to overcomplicate compatibility checks, so we ignore x64 emulation.
491- AARCH64 => vec ! [ "ARM64" , "X86" ] ,
519+ X86 => & [ "X86" ] ,
520+ X86_64 => & [ "X64" ] ,
521+ // Starting with VS 17.4, there is a natively hosted compiler on ARM64:
522+ // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
523+ // On older versions of VS, we use x64 if running under emulation is supported,
524+ // otherwise use x86.
525+ AARCH64 => {
526+ if is_amd64_emulation_supported ( ) {
527+ & [ "ARM64" , "X64" , "X86" ] [ ..]
528+ } else {
529+ & [ "ARM64" , "X86" ]
530+ }
531+ }
492532 _ => return None ,
493533 } ;
494534 let target = lib_subdir ( target) ?;
0 commit comments