@@ -163,6 +163,11 @@ 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+ FreeLibrary , GetProcAddress , LoadLibraryA , UserEnabled , HMODULE , IMAGE_FILE_MACHINE_AMD64 ,
168+ S_OK ,
169+ } ;
170+ use crate :: windows:: windows_sys_no_link:: GetMachineTypeAttributes ;
166171 use std:: convert:: TryFrom ;
167172 use std:: env;
168173 use std:: ffi:: OsString ;
@@ -199,6 +204,52 @@ mod impl_ {
199204 include : Vec < PathBuf > ,
200205 }
201206
207+ struct LibraryHandle ( HMODULE ) ;
208+
209+ impl LibraryHandle {
210+ fn new ( name : & [ u8 ] ) -> Option < Self > {
211+ let handle = unsafe { LoadLibraryA ( name. as_ptr ( ) as _ ) } ;
212+ ( !handle. is_null ( ) ) . then ( || Self ( handle) )
213+ }
214+
215+ /// Get a function pointer to a function in the library.
216+ /// SAFETY: The caller must ensure that the function signature matches the actual function.
217+ /// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the
218+ /// generated function for `func_signature`.
219+ unsafe fn get_proc_address < F > ( & self , name : & [ u8 ] , _func_signature : & F ) -> Option < F > {
220+ let symbol = unsafe { GetProcAddress ( self . 0 , name. as_ptr ( ) as _ ) } ;
221+ symbol. map ( |symbol| unsafe { mem:: transmute_copy ( & symbol) } )
222+ }
223+ }
224+
225+ impl Drop for LibraryHandle {
226+ fn drop ( & mut self ) {
227+ unsafe { FreeLibrary ( self . 0 ) } ;
228+ }
229+ }
230+
231+ fn is_amd64_emulation_supported ( ) -> bool {
232+ if let Some ( kernel32) = LibraryHandle :: new ( b"kernel32.dll\0 " ) {
233+ // GetMachineTypeAttributes is only available on Win11 22000+.
234+ if let Some ( get_machine_type_attributes) = unsafe {
235+ kernel32. get_proc_address ( b"GetMachineTypeAttributes\0 " , & GetMachineTypeAttributes )
236+ } {
237+ let mut attributes = Default :: default ( ) ;
238+ if unsafe { get_machine_type_attributes ( IMAGE_FILE_MACHINE_AMD64 , & mut attributes) }
239+ == S_OK
240+ {
241+ ( attributes & UserEnabled ) != 0
242+ } else {
243+ false
244+ }
245+ } else {
246+ false
247+ }
248+ } else {
249+ false
250+ }
251+ }
252+
202253 impl MsvcTool {
203254 fn new ( tool : PathBuf ) -> MsvcTool {
204255 MsvcTool {
@@ -226,7 +277,6 @@ mod impl_ {
226277
227278 /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
228279 /// given target's arch. Returns `None` if the variable does not exist.
229- #[ cfg( windows) ]
230280 fn is_vscmd_target ( target : TargetArch < ' _ > ) -> Option < bool > {
231281 let vscmd_arch = env:: var ( "VSCMD_ARG_TGT_ARCH" ) . ok ( ) ?;
232282 // Convert the Rust target arch to its VS arch equivalent.
@@ -483,34 +533,41 @@ mod impl_ {
483533 let version = vs15plus_vc_read_version ( instance_path) ?;
484534
485535 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" ] ,
536+ X86 => & [ "X86" ] ,
537+ X86_64 => & [ "X64" ] ,
538+ // Starting with VS 17.4, there is a natively hosted compiler on ARM64:
539+ // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
540+ // On older versions of VS, we use x64 if running under emulation is supported,
541+ // otherwise use x86.
542+ AARCH64 => {
543+ if is_amd64_emulation_supported ( ) {
544+ & [ "ARM64" , "X64" , "X86" ] [ ..]
545+ } else {
546+ & [ "ARM64" , "X86" ]
547+ }
548+ }
492549 _ => return None ,
493550 } ;
494551 let target = lib_subdir ( target) ?;
495552 // The directory layout here is MSVC/bin/Host$host/$target/
496553 let path = instance_path. join ( r"VC\Tools\MSVC" ) . join ( version) ;
497554 // We use the first available host architecture that can build for the target
498555 let ( host_path, host) = hosts. iter ( ) . find_map ( |& x| {
499- let candidate = path. join ( "bin" ) . join ( & format ! ( "Host{}" , x) ) ;
500- if candidate. join ( & target) . exists ( ) {
556+ let candidate = path. join ( "bin" ) . join ( format ! ( "Host{}" , x) ) ;
557+ if candidate. join ( target) . exists ( ) {
501558 Some ( ( candidate, x) )
502559 } else {
503560 None
504561 }
505562 } ) ?;
506563 // This is the path to the toolchain for a particular target, running
507564 // on a given host
508- let bin_path = host_path. join ( & target) ;
565+ let bin_path = host_path. join ( target) ;
509566 // But! we also need PATH to contain the target directory for the host
510567 // architecture, because it contains dlls like mspdb140.dll compiled for
511568 // the host architecture.
512- let host_dylib_path = host_path. join ( & host. to_lowercase ( ) ) ;
513- let lib_path = path. join ( "lib" ) . join ( & target) ;
569+ let host_dylib_path = host_path. join ( host. to_lowercase ( ) ) ;
570+ let lib_path = path. join ( "lib" ) . join ( target) ;
514571 let alt_lib_path = ( target == "arm64ec" ) . then ( || path. join ( "lib" ) . join ( "arm64ec" ) ) ;
515572 let include_path = path. join ( "include" ) ;
516573 Some ( (
0 commit comments