@@ -163,6 +163,10 @@ mod impl_ {
163
163
use crate :: windows:: registry:: { RegistryKey , LOCAL_MACHINE } ;
164
164
use crate :: windows:: setup_config:: SetupConfiguration ;
165
165
use crate :: windows:: vs_instances:: { VsInstances , VswhereInstance } ;
166
+ use crate :: windows:: windows_sys:: {
167
+ FreeLibrary , GetProcAddress , LoadLibraryA , UserEnabled , HMODULE , HRESULT ,
168
+ IMAGE_FILE_MACHINE_AMD64 , MACHINE_ATTRIBUTES , S_OK ,
169
+ } ;
166
170
use std:: convert:: TryFrom ;
167
171
use std:: env;
168
172
use std:: ffi:: OsString ;
@@ -199,6 +203,47 @@ mod impl_ {
199
203
include : Vec < PathBuf > ,
200
204
}
201
205
206
+ struct LibraryHandle ( HMODULE ) ;
207
+
208
+ impl LibraryHandle {
209
+ fn new ( name : & [ u8 ] ) -> Option < Self > {
210
+ let handle = unsafe { LoadLibraryA ( name. as_ptr ( ) as _ ) } ;
211
+ ( !handle. is_null ( ) ) . then ( || Self ( handle) )
212
+ }
213
+ }
214
+
215
+ impl Drop for LibraryHandle {
216
+ fn drop ( & mut self ) {
217
+ unsafe { FreeLibrary ( self . 0 ) } ;
218
+ }
219
+ }
220
+
221
+ fn is_amd64_emulation_supported ( ) -> bool {
222
+ let kernel32 = LibraryHandle :: new ( b"kernel32.dll\0 " ) . unwrap ( ) ;
223
+ // GetMachineTypeAttributes is only available on Win11 22000+.
224
+ if let Some ( get_machine_type_attributes) =
225
+ unsafe { GetProcAddress ( kernel32. 0 , b"GetMachineTypeAttributes\0 " . as_ptr ( ) as _ ) }
226
+ {
227
+ // SAFETY: We are getting a function pointer to GetMachineTypeAttributes and then transmuting it to match
228
+ // the signature that windows-sys would generate: https://docs.rs/windows-sys/0.52.0/windows_sys/Win32/System/Threading/fn.GetMachineTypeAttributes.html
229
+ type GetMachineTypeAttributesFuncType =
230
+ unsafe extern "system" fn ( u16 , * mut MACHINE_ATTRIBUTES ) -> HRESULT ;
231
+ let get_machine_type_attributes: GetMachineTypeAttributesFuncType =
232
+ unsafe { std:: mem:: transmute_copy ( & get_machine_type_attributes) } ;
233
+
234
+ let mut attributes = Default :: default ( ) ;
235
+ if unsafe { get_machine_type_attributes ( IMAGE_FILE_MACHINE_AMD64 , & mut attributes) }
236
+ == S_OK
237
+ {
238
+ ( attributes & UserEnabled ) != 0
239
+ } else {
240
+ false
241
+ }
242
+ } else {
243
+ false
244
+ }
245
+ }
246
+
202
247
impl MsvcTool {
203
248
fn new ( tool : PathBuf ) -> MsvcTool {
204
249
MsvcTool {
@@ -226,7 +271,6 @@ mod impl_ {
226
271
227
272
/// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
228
273
/// given target's arch. Returns `None` if the variable does not exist.
229
- #[ cfg( windows) ]
230
274
fn is_vscmd_target ( target : TargetArch < ' _ > ) -> Option < bool > {
231
275
let vscmd_arch = env:: var ( "VSCMD_ARG_TGT_ARCH" ) . ok ( ) ?;
232
276
// Convert the Rust target arch to its VS arch equivalent.
@@ -483,34 +527,41 @@ mod impl_ {
483
527
let version = vs15plus_vc_read_version ( instance_path) ?;
484
528
485
529
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" ] ,
530
+ X86 => & [ "X86" ] ,
531
+ X86_64 => & [ "X64" ] ,
532
+ // Starting with VS 17.4, there is a natively hosted compiler on ARM64:
533
+ // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
534
+ // On older versions of VS, we use x64 if running under emulation is supported,
535
+ // otherwise use x86.
536
+ AARCH64 => {
537
+ if is_amd64_emulation_supported ( ) {
538
+ & [ "ARM64" , "X64" , "X86" ] [ ..]
539
+ } else {
540
+ & [ "ARM64" , "X86" ]
541
+ }
542
+ }
492
543
_ => return None ,
493
544
} ;
494
545
let target = lib_subdir ( target) ?;
495
546
// The directory layout here is MSVC/bin/Host$host/$target/
496
547
let path = instance_path. join ( r"VC\Tools\MSVC" ) . join ( version) ;
497
548
// We use the first available host architecture that can build for the target
498
549
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 ( ) {
550
+ let candidate = path. join ( "bin" ) . join ( format ! ( "Host{}" , x) ) ;
551
+ if candidate. join ( target) . exists ( ) {
501
552
Some ( ( candidate, x) )
502
553
} else {
503
554
None
504
555
}
505
556
} ) ?;
506
557
// This is the path to the toolchain for a particular target, running
507
558
// on a given host
508
- let bin_path = host_path. join ( & target) ;
559
+ let bin_path = host_path. join ( target) ;
509
560
// But! we also need PATH to contain the target directory for the host
510
561
// architecture, because it contains dlls like mspdb140.dll compiled for
511
562
// the host architecture.
512
- let host_dylib_path = host_path. join ( & host. to_lowercase ( ) ) ;
513
- let lib_path = path. join ( "lib" ) . join ( & target) ;
563
+ let host_dylib_path = host_path. join ( host. to_lowercase ( ) ) ;
564
+ let lib_path = path. join ( "lib" ) . join ( target) ;
514
565
let alt_lib_path = ( target == "arm64ec" ) . then ( || path. join ( "lib" ) . join ( "arm64ec" ) ) ;
515
566
let include_path = path. join ( "include" ) ;
516
567
Some ( (
0 commit comments