@@ -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
+ GetMachineTypeAttributes , GetProcAddress , LoadLibraryA , UserEnabled ,
168
+ IMAGE_FILE_MACHINE_AMD64 , S_OK ,
169
+ } ;
166
170
use std:: convert:: TryFrom ;
167
171
use std:: env;
168
172
use std:: ffi:: OsString ;
@@ -199,6 +203,35 @@ mod impl_ {
199
203
include : Vec < PathBuf > ,
200
204
}
201
205
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
+
202
235
impl MsvcTool {
203
236
fn new ( tool : PathBuf ) -> MsvcTool {
204
237
MsvcTool {
@@ -226,7 +259,6 @@ mod impl_ {
226
259
227
260
/// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
228
261
/// given target's arch. Returns `None` if the variable does not exist.
229
- #[ cfg( windows) ]
230
262
fn is_vscmd_target ( target : TargetArch < ' _ > ) -> Option < bool > {
231
263
let vscmd_arch = env:: var ( "VSCMD_ARG_TGT_ARCH" ) . ok ( ) ?;
232
264
// Convert the Rust target arch to its VS arch equivalent.
@@ -482,13 +514,21 @@ mod impl_ {
482
514
) -> Option < ( PathBuf , PathBuf , PathBuf , PathBuf , Option < PathBuf > , PathBuf ) > {
483
515
let version = vs15plus_vc_read_version ( instance_path) ?;
484
516
517
+ let _ = is_amd64_emulation_supported ( ) ;
485
518
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
+ }
492
532
_ => return None ,
493
533
} ;
494
534
let target = lib_subdir ( target) ?;
0 commit comments