| 1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT | 
| 2 | // file at the top-level directory of this distribution and at | 
|---|
| 3 | // http://rust-lang.org/COPYRIGHT. | 
|---|
| 4 | // | 
|---|
| 5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | 
|---|
| 6 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | 
|---|
| 7 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | 
|---|
| 8 | // option. This file may not be copied, modified, or distributed | 
|---|
| 9 | // except according to those terms. | 
|---|
| 10 |  | 
|---|
| 11 | //! A helper module to looking for windows-specific tools: | 
|---|
| 12 | //! 1. On Windows host, probe the Windows Registry if needed; | 
|---|
| 13 | //! 2. On non-Windows host, check specified environment variables. | 
|---|
| 14 |  | 
|---|
| 15 | #![ allow(clippy::upper_case_acronyms)] | 
|---|
| 16 |  | 
|---|
| 17 | use std::{ | 
|---|
| 18 | env, | 
|---|
| 19 | ffi::{OsStr, OsString}, | 
|---|
| 20 | ops::Deref, | 
|---|
| 21 | path::PathBuf, | 
|---|
| 22 | process::Command, | 
|---|
| 23 | sync::Arc, | 
|---|
| 24 | }; | 
|---|
| 25 |  | 
|---|
| 26 | use crate::Tool; | 
|---|
| 27 | use crate::ToolFamily; | 
|---|
| 28 |  | 
|---|
| 29 | const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false }; | 
|---|
| 30 |  | 
|---|
| 31 | /// The target provided by the user. | 
|---|
| 32 | #[ derive(Copy, Clone, PartialEq, Eq)] | 
|---|
| 33 | enum TargetArch { | 
|---|
| 34 | X86, | 
|---|
| 35 | X64, | 
|---|
| 36 | Arm, | 
|---|
| 37 | Arm64, | 
|---|
| 38 | Arm64ec, | 
|---|
| 39 | } | 
|---|
| 40 | impl TargetArch { | 
|---|
| 41 | /// Parse the `TargetArch` from a str. Returns `None` if the arch is unrecognized. | 
|---|
| 42 | fn new(arch: &str) -> Option<Self> { | 
|---|
| 43 | // NOTE: Keep up to date with docs in [`find`]. | 
|---|
| 44 | match arch { | 
|---|
| 45 | "x64"| "x86_64"=> Some(Self::X64), | 
|---|
| 46 | "arm64"| "aarch64"=> Some(Self::Arm64), | 
|---|
| 47 | "arm64ec"=> Some(Self::Arm64ec), | 
|---|
| 48 | "x86"| "i686"| "i586"=> Some(Self::X86), | 
|---|
| 49 | "arm"| "thumbv7a"=> Some(Self::Arm), | 
|---|
| 50 | _ => None, | 
|---|
| 51 | } | 
|---|
| 52 | } | 
|---|
| 53 |  | 
|---|
| 54 | #[ cfg(windows)] | 
|---|
| 55 | /// Gets the Visual Studio name for the architecture. | 
|---|
| 56 | fn as_vs_arch(&self) -> &'static str { | 
|---|
| 57 | match self { | 
|---|
| 58 | Self::X64 => "x64", | 
|---|
| 59 | Self::Arm64 | Self::Arm64ec => "arm64", | 
|---|
| 60 | Self::X86 => "x86", | 
|---|
| 61 | Self::Arm => "arm", | 
|---|
| 62 | } | 
|---|
| 63 | } | 
|---|
| 64 | } | 
|---|
| 65 |  | 
|---|
| 66 | pub(crate) enum Env { | 
|---|
| 67 | Owned(OsString), | 
|---|
| 68 | Arced(Arc<OsStr>), | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | impl AsRef<OsStr> for Env { | 
|---|
| 72 | fn as_ref(&self) -> &OsStr { | 
|---|
| 73 | self.deref() | 
|---|
| 74 | } | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | impl Deref for Env { | 
|---|
| 78 | type Target = OsStr; | 
|---|
| 79 |  | 
|---|
| 80 | fn deref(&self) -> &Self::Target { | 
|---|
| 81 | match self { | 
|---|
| 82 | Env::Owned(os_str: &OsString) => os_str, | 
|---|
| 83 | Env::Arced(os_str: &Arc) => os_str, | 
|---|
| 84 | } | 
|---|
| 85 | } | 
|---|
| 86 | } | 
|---|
| 87 |  | 
|---|
| 88 | impl From<Env> for PathBuf { | 
|---|
| 89 | fn from(env: Env) -> Self { | 
|---|
| 90 | match env { | 
|---|
| 91 | Env::Owned(os_str: OsString) => PathBuf::from(os_str), | 
|---|
| 92 | Env::Arced(os_str: Arc) => PathBuf::from(os_str.deref()), | 
|---|
| 93 | } | 
|---|
| 94 | } | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | pub(crate) trait EnvGetter { | 
|---|
| 98 | fn get_env(&self, name: &'static str) -> Option<Env>; | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | struct StdEnvGetter; | 
|---|
| 102 |  | 
|---|
| 103 | impl EnvGetter for StdEnvGetter { | 
|---|
| 104 | #[ allow(clippy::disallowed_methods)] | 
|---|
| 105 | fn get_env(&self, name: &'static str) -> Option<Env> { | 
|---|
| 106 | env::var_os(key:name).map(Env::Owned) | 
|---|
| 107 | } | 
|---|
| 108 | } | 
|---|
| 109 |  | 
|---|
| 110 | /// Attempts to find a tool within an MSVC installation using the Windows | 
|---|
| 111 | /// registry as a point to search from. | 
|---|
| 112 | /// | 
|---|
| 113 | /// The `arch_or_target` argument is the architecture or the Rust target | 
|---|
| 114 | /// triple that the tool should work for (e.g. compile or link for). The | 
|---|
| 115 | /// supported architecture names are: | 
|---|
| 116 | /// - `"x64"` or `"x86_64"` | 
|---|
| 117 | /// - `"arm64"` or `"aarch64"` | 
|---|
| 118 | /// - `"arm64ec"` | 
|---|
| 119 | /// - `"x86"`, `"i586"` or `"i686"` | 
|---|
| 120 | /// - `"arm"` or `"thumbv7a"` | 
|---|
| 121 | /// | 
|---|
| 122 | /// The `tool` argument is the tool to find (e.g. `cl.exe` or `link.exe`). | 
|---|
| 123 | /// | 
|---|
| 124 | /// This function will return `None` if the tool could not be found, or it will | 
|---|
| 125 | /// return `Some(cmd)` which represents a command that's ready to execute the | 
|---|
| 126 | /// tool with the appropriate environment variables set. | 
|---|
| 127 | /// | 
|---|
| 128 | /// Note that this function always returns `None` for non-MSVC targets (if a | 
|---|
| 129 | /// full target name was specified). | 
|---|
| 130 | pub fn find(arch_or_target: &str, tool: &str) -> Option<Command> { | 
|---|
| 131 | find_tool(arch_or_target, tool).map(|c: Tool| c.to_command()) | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | /// Similar to the `find` function above, this function will attempt the same | 
|---|
| 135 | /// operation (finding a MSVC tool in a local install) but instead returns a | 
|---|
| 136 | /// `Tool` which may be introspected. | 
|---|
| 137 | pub fn find_tool(arch_or_target: &str, tool: &str) -> Option<Tool> { | 
|---|
| 138 | let full_arch: &str = if let Some((full_arch: &str, rest: &str)) = arch_or_target.split_once(delimiter: "-") { | 
|---|
| 139 | // The logic is all tailored for MSVC, if the target is not that then | 
|---|
| 140 | // bail out early. | 
|---|
| 141 | if !rest.contains( "msvc") { | 
|---|
| 142 | return None; | 
|---|
| 143 | } | 
|---|
| 144 | full_arch | 
|---|
| 145 | } else { | 
|---|
| 146 | arch_or_target | 
|---|
| 147 | }; | 
|---|
| 148 | find_tool_inner(full_arch, tool, &StdEnvGetter) | 
|---|
| 149 | } | 
|---|
| 150 |  | 
|---|
| 151 | pub(crate) fn find_tool_inner( | 
|---|
| 152 | full_arch: &str, | 
|---|
| 153 | tool: &str, | 
|---|
| 154 | env_getter: &dyn EnvGetter, | 
|---|
| 155 | ) -> Option<Tool> { | 
|---|
| 156 | // We only need the arch. | 
|---|
| 157 | let target = TargetArch::new(full_arch)?; | 
|---|
| 158 |  | 
|---|
| 159 | // Looks like msbuild isn't located in the same location as other tools like | 
|---|
| 160 | // cl.exe and lib.exe. | 
|---|
| 161 | if tool.contains( "msbuild") { | 
|---|
| 162 | return impl_::find_msbuild(target, env_getter); | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | // Looks like devenv isn't located in the same location as other tools like | 
|---|
| 166 | // cl.exe and lib.exe. | 
|---|
| 167 | if tool.contains( "devenv") { | 
|---|
| 168 | return impl_::find_devenv(target, env_getter); | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | // Ok, if we're here, now comes the fun part of the probing. Default shells | 
|---|
| 172 | // or shells like MSYS aren't really configured to execute `cl.exe` and the | 
|---|
| 173 | // various compiler tools shipped as part of Visual Studio. Here we try to | 
|---|
| 174 | // first find the relevant tool, then we also have to be sure to fill in | 
|---|
| 175 | // environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that | 
|---|
| 176 | // the tool is actually usable. | 
|---|
| 177 |  | 
|---|
| 178 | impl_::find_msvc_environment(tool, target, env_getter) | 
|---|
| 179 | .or_else(|| impl_::find_msvc_15plus(tool, target, env_getter)) | 
|---|
| 180 | .or_else(|| impl_::find_msvc_14(tool, target, env_getter)) | 
|---|
| 181 | } | 
|---|
| 182 |  | 
|---|
| 183 | /// A version of Visual Studio | 
|---|
| 184 | #[ derive(Debug, PartialEq, Eq, Copy, Clone)] | 
|---|
| 185 | #[ non_exhaustive] | 
|---|
| 186 | pub enum VsVers { | 
|---|
| 187 | /// Visual Studio 12 (2013) | 
|---|
| 188 | #[ deprecated( | 
|---|
| 189 | note = "Visual Studio 12 is no longer supported. cc will never return this value." | 
|---|
| 190 | )] | 
|---|
| 191 | Vs12, | 
|---|
| 192 | /// Visual Studio 14 (2015) | 
|---|
| 193 | Vs14, | 
|---|
| 194 | /// Visual Studio 15 (2017) | 
|---|
| 195 | Vs15, | 
|---|
| 196 | /// Visual Studio 16 (2019) | 
|---|
| 197 | Vs16, | 
|---|
| 198 | /// Visual Studio 17 (2022) | 
|---|
| 199 | Vs17, | 
|---|
| 200 | } | 
|---|
| 201 |  | 
|---|
| 202 | /// Find the most recent installed version of Visual Studio | 
|---|
| 203 | /// | 
|---|
| 204 | /// This is used by the cmake crate to figure out the correct | 
|---|
| 205 | /// generator. | 
|---|
| 206 | #[ allow(clippy::disallowed_methods)] | 
|---|
| 207 | pub fn find_vs_version() -> Result<VsVers, String> { | 
|---|
| 208 | fn has_msbuild_version(version: &str) -> bool { | 
|---|
| 209 | impl_::has_msbuild_version(version, &StdEnvGetter) | 
|---|
| 210 | } | 
|---|
| 211 |  | 
|---|
| 212 | match std::env::var( "VisualStudioVersion") { | 
|---|
| 213 | Ok(version) => match &version[..] { | 
|---|
| 214 | "17.0"=> Ok(VsVers::Vs17), | 
|---|
| 215 | "16.0"=> Ok(VsVers::Vs16), | 
|---|
| 216 | "15.0"=> Ok(VsVers::Vs15), | 
|---|
| 217 | "14.0"=> Ok(VsVers::Vs14), | 
|---|
| 218 | vers => Err(format!( | 
|---|
| 219 | "\n\n \ | 
|---|
| 220 |                  unsupported or unknown VisualStudio version: {}\n \ | 
|---|
| 221 |                  if another version is installed consider running \ | 
|---|
| 222 |                  the appropriate vcvars script before building this \ | 
|---|
| 223 |                  crate\n \ | 
|---|
| 224 |                  ", | 
|---|
| 225 | vers | 
|---|
| 226 | )), | 
|---|
| 227 | }, | 
|---|
| 228 | _ => { | 
|---|
| 229 | // Check for the presence of a specific registry key | 
|---|
| 230 | // that indicates visual studio is installed. | 
|---|
| 231 | if has_msbuild_version( "17.0") { | 
|---|
| 232 | Ok(VsVers::Vs17) | 
|---|
| 233 | } else if has_msbuild_version( "16.0") { | 
|---|
| 234 | Ok(VsVers::Vs16) | 
|---|
| 235 | } else if has_msbuild_version( "15.0") { | 
|---|
| 236 | Ok(VsVers::Vs15) | 
|---|
| 237 | } else if has_msbuild_version( "14.0") { | 
|---|
| 238 | Ok(VsVers::Vs14) | 
|---|
| 239 | } else { | 
|---|
| 240 | Err( "\n\n \ | 
|---|
| 241 |                      couldn't determine visual studio generator\n \ | 
|---|
| 242 |                      if VisualStudio is installed, however, consider \ | 
|---|
| 243 |                      running the appropriate vcvars script before building \ | 
|---|
| 244 |                      this crate\n \ | 
|---|
| 245 |                      " | 
|---|
| 246 | .to_string()) | 
|---|
| 247 | } | 
|---|
| 248 | } | 
|---|
| 249 | } | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | /// Windows Implementation. | 
|---|
| 253 | #[ cfg(windows)] | 
|---|
| 254 | mod impl_ { | 
|---|
| 255 | use crate::windows::com; | 
|---|
| 256 | use crate::windows::registry::{RegistryKey, LOCAL_MACHINE}; | 
|---|
| 257 | use crate::windows::setup_config::SetupConfiguration; | 
|---|
| 258 | use crate::windows::vs_instances::{VsInstances, VswhereInstance}; | 
|---|
| 259 | use crate::windows::windows_sys::{ | 
|---|
| 260 | GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE, | 
|---|
| 261 | IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK, | 
|---|
| 262 | }; | 
|---|
| 263 | use std::convert::TryFrom; | 
|---|
| 264 | use std::env; | 
|---|
| 265 | use std::ffi::OsString; | 
|---|
| 266 | use std::fs::File; | 
|---|
| 267 | use std::io::Read; | 
|---|
| 268 | use std::iter; | 
|---|
| 269 | use std::mem; | 
|---|
| 270 | use std::path::{Path, PathBuf}; | 
|---|
| 271 | use std::process::Command; | 
|---|
| 272 | use std::str::FromStr; | 
|---|
| 273 | use std::sync::atomic::{AtomicBool, Ordering}; | 
|---|
| 274 | use std::sync::Once; | 
|---|
| 275 |  | 
|---|
| 276 | use super::{EnvGetter, TargetArch, MSVC_FAMILY}; | 
|---|
| 277 | use crate::Tool; | 
|---|
| 278 |  | 
|---|
| 279 | struct MsvcTool { | 
|---|
| 280 | tool: PathBuf, | 
|---|
| 281 | libs: Vec<PathBuf>, | 
|---|
| 282 | path: Vec<PathBuf>, | 
|---|
| 283 | include: Vec<PathBuf>, | 
|---|
| 284 | } | 
|---|
| 285 |  | 
|---|
| 286 | struct LibraryHandle(HMODULE); | 
|---|
| 287 |  | 
|---|
| 288 | impl LibraryHandle { | 
|---|
| 289 | fn new(name: &[u8]) -> Option<Self> { | 
|---|
| 290 | let handle = unsafe { LoadLibraryA(name.as_ptr() as _) }; | 
|---|
| 291 | (!handle.is_null()).then_some(Self(handle)) | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 | /// Get a function pointer to a function in the library. | 
|---|
| 295 | /// # SAFETY | 
|---|
| 296 | /// | 
|---|
| 297 | /// The caller must ensure that the function signature matches the actual function. | 
|---|
| 298 | /// The easiest way to do this is to add an entry to `windows_sys_no_link.list` and use the | 
|---|
| 299 | /// generated function for `func_signature`. | 
|---|
| 300 | /// | 
|---|
| 301 | /// The function returned cannot be used after the handle is dropped. | 
|---|
| 302 | unsafe fn get_proc_address<F>(&self, name: &[u8]) -> Option<F> { | 
|---|
| 303 | let symbol = GetProcAddress(self.0, name.as_ptr() as _); | 
|---|
| 304 | symbol.map(|symbol| mem::transmute_copy(&symbol)) | 
|---|
| 305 | } | 
|---|
| 306 | } | 
|---|
| 307 |  | 
|---|
| 308 | type GetMachineTypeAttributesFuncType = | 
|---|
| 309 | unsafe extern "system"fn(u16, *mut MACHINE_ATTRIBUTES) -> i32; | 
|---|
| 310 | const _: () = { | 
|---|
| 311 | // Ensure that our hand-written signature matches the actual function signature. | 
|---|
| 312 | // We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to | 
|---|
| 313 | // it, which will fail to load on older versions of Windows. | 
|---|
| 314 | let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes; | 
|---|
| 315 | }; | 
|---|
| 316 |  | 
|---|
| 317 | fn is_amd64_emulation_supported_inner() -> Option<bool> { | 
|---|
| 318 | // GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it. | 
|---|
| 319 | let kernel32 = LibraryHandle::new( b"kernel32.dll\0 ")?; | 
|---|
| 320 | // SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature. | 
|---|
| 321 | let get_machine_type_attributes = unsafe { | 
|---|
| 322 | kernel32 | 
|---|
| 323 | .get_proc_address::<GetMachineTypeAttributesFuncType>( b"GetMachineTypeAttributes\0 ") | 
|---|
| 324 | }?; | 
|---|
| 325 | let mut attributes = Default::default(); | 
|---|
| 326 | if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) } == S_OK | 
|---|
| 327 | { | 
|---|
| 328 | Some((attributes & UserEnabled) != 0) | 
|---|
| 329 | } else { | 
|---|
| 330 | Some(false) | 
|---|
| 331 | } | 
|---|
| 332 | } | 
|---|
| 333 |  | 
|---|
| 334 | fn is_amd64_emulation_supported() -> bool { | 
|---|
| 335 | // TODO: Replace with a OnceLock once MSRV is 1.70. | 
|---|
| 336 | static LOAD_VALUE: Once = Once::new(); | 
|---|
| 337 | static IS_SUPPORTED: AtomicBool = AtomicBool::new(false); | 
|---|
| 338 |  | 
|---|
| 339 | // Using Relaxed ordering since the Once is providing synchronization. | 
|---|
| 340 | LOAD_VALUE.call_once(|| { | 
|---|
| 341 | IS_SUPPORTED.store( | 
|---|
| 342 | is_amd64_emulation_supported_inner().unwrap_or(false), | 
|---|
| 343 | Ordering::Relaxed, | 
|---|
| 344 | ); | 
|---|
| 345 | }); | 
|---|
| 346 | IS_SUPPORTED.load(Ordering::Relaxed) | 
|---|
| 347 | } | 
|---|
| 348 |  | 
|---|
| 349 | impl MsvcTool { | 
|---|
| 350 | fn new(tool: PathBuf) -> MsvcTool { | 
|---|
| 351 | MsvcTool { | 
|---|
| 352 | tool, | 
|---|
| 353 | libs: Vec::new(), | 
|---|
| 354 | path: Vec::new(), | 
|---|
| 355 | include: Vec::new(), | 
|---|
| 356 | } | 
|---|
| 357 | } | 
|---|
| 358 |  | 
|---|
| 359 | fn into_tool(self, env_getter: &dyn EnvGetter) -> Tool { | 
|---|
| 360 | let MsvcTool { | 
|---|
| 361 | tool, | 
|---|
| 362 | libs, | 
|---|
| 363 | path, | 
|---|
| 364 | include, | 
|---|
| 365 | } = self; | 
|---|
| 366 | let mut tool = Tool::with_family(tool, MSVC_FAMILY); | 
|---|
| 367 | add_env(&mut tool, "LIB", libs, env_getter); | 
|---|
| 368 | add_env(&mut tool, "PATH", path, env_getter); | 
|---|
| 369 | add_env(&mut tool, "INCLUDE", include, env_getter); | 
|---|
| 370 | tool | 
|---|
| 371 | } | 
|---|
| 372 | } | 
|---|
| 373 |  | 
|---|
| 374 | /// Checks to see if the target's arch matches the VS environment. Returns `None` if the | 
|---|
| 375 | /// environment is unknown. | 
|---|
| 376 | fn is_vscmd_target(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<bool> { | 
|---|
| 377 | is_vscmd_target_env(target, env_getter).or_else(|| is_vscmd_target_cl(target, env_getter)) | 
|---|
| 378 | } | 
|---|
| 379 |  | 
|---|
| 380 | /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the | 
|---|
| 381 | /// given target's arch. Returns `None` if the variable does not exist. | 
|---|
| 382 | fn is_vscmd_target_env(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<bool> { | 
|---|
| 383 | let vscmd_arch = env_getter.get_env( "VSCMD_ARG_TGT_ARCH")?; | 
|---|
| 384 | Some(target.as_vs_arch() == vscmd_arch.as_ref()) | 
|---|
| 385 | } | 
|---|
| 386 |  | 
|---|
| 387 | /// Checks if the cl.exe target matches the given target's arch. Returns `None` if anything | 
|---|
| 388 | /// fails. | 
|---|
| 389 | fn is_vscmd_target_cl(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<bool> { | 
|---|
| 390 | let cmd_target = vscmd_target_cl(env_getter)?; | 
|---|
| 391 | Some(target.as_vs_arch() == cmd_target) | 
|---|
| 392 | } | 
|---|
| 393 |  | 
|---|
| 394 | /// Detect the target architecture of `cl.exe` in the current path, and return `None` if this | 
|---|
| 395 | /// fails for any reason. | 
|---|
| 396 | fn vscmd_target_cl(env_getter: &dyn EnvGetter) -> Option<&'static str> { | 
|---|
| 397 | let cl_exe = env_getter.get_env( "PATH").and_then(|path| { | 
|---|
| 398 | env::split_paths(&path) | 
|---|
| 399 | .map(|p| p.join( "cl.exe")) | 
|---|
| 400 | .find(|p| p.exists()) | 
|---|
| 401 | })?; | 
|---|
| 402 | let mut cl = Command::new(cl_exe); | 
|---|
| 403 | cl.stderr(std::process::Stdio::piped()) | 
|---|
| 404 | .stdout(std::process::Stdio::null()); | 
|---|
| 405 |  | 
|---|
| 406 | let out = cl.output().ok()?; | 
|---|
| 407 | let cl_arch = out | 
|---|
| 408 | .stderr | 
|---|
| 409 | .split(|&b| b == b'\n '|| b == b'\r ') | 
|---|
| 410 | .next()? | 
|---|
| 411 | .rsplit(|&b| b == b' ') | 
|---|
| 412 | .next()?; | 
|---|
| 413 |  | 
|---|
| 414 | match cl_arch { | 
|---|
| 415 | b"x64"=> Some( "x64"), | 
|---|
| 416 | b"x86"=> Some( "x86"), | 
|---|
| 417 | b"ARM64"=> Some( "arm64"), | 
|---|
| 418 | b"ARM"=> Some( "arm"), | 
|---|
| 419 | _ => None, | 
|---|
| 420 | } | 
|---|
| 421 | } | 
|---|
| 422 |  | 
|---|
| 423 | /// Attempt to find the tool using environment variables set by vcvars. | 
|---|
| 424 | pub(super) fn find_msvc_environment( | 
|---|
| 425 | tool: &str, | 
|---|
| 426 | target: TargetArch, | 
|---|
| 427 | env_getter: &dyn EnvGetter, | 
|---|
| 428 | ) -> Option<Tool> { | 
|---|
| 429 | // Early return if the environment isn't one that is known to have compiler toolsets in PATH | 
|---|
| 430 | // `VCINSTALLDIR` is set from vcvarsall.bat (developer command prompt) | 
|---|
| 431 | // `VSTEL_MSBuildProjectFullPath` is set by msbuild when invoking custom build steps | 
|---|
| 432 | // NOTE: `VisualStudioDir` used to be used but this isn't set when invoking msbuild from the commandline | 
|---|
| 433 | if env_getter.get_env( "VCINSTALLDIR").is_none() | 
|---|
| 434 | && env_getter.get_env( "VSTEL_MSBuildProjectFullPath").is_none() | 
|---|
| 435 | { | 
|---|
| 436 | return None; | 
|---|
| 437 | } | 
|---|
| 438 |  | 
|---|
| 439 | // If the vscmd target differs from the requested target then | 
|---|
| 440 | // attempt to get the tool using the VS install directory. | 
|---|
| 441 | if is_vscmd_target(target, env_getter) == Some(false) { | 
|---|
| 442 | // We will only get here with versions 15+. | 
|---|
| 443 | let vs_install_dir: PathBuf = env_getter.get_env( "VSINSTALLDIR")?.into(); | 
|---|
| 444 | tool_from_vs15plus_instance(tool, target, &vs_install_dir, env_getter) | 
|---|
| 445 | } else { | 
|---|
| 446 | // Fallback to simply using the current environment. | 
|---|
| 447 | env_getter | 
|---|
| 448 | .get_env( "PATH") | 
|---|
| 449 | .and_then(|path| { | 
|---|
| 450 | env::split_paths(&path) | 
|---|
| 451 | .map(|p| p.join(tool)) | 
|---|
| 452 | .find(|p| p.exists()) | 
|---|
| 453 | }) | 
|---|
| 454 | .map(|path| Tool::with_family(path, MSVC_FAMILY)) | 
|---|
| 455 | } | 
|---|
| 456 | } | 
|---|
| 457 |  | 
|---|
| 458 | fn find_msbuild_vs17(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 459 | find_tool_in_vs16plus_path( r"MSBuild\Current\Bin\MSBuild.exe", target, "17", env_getter) | 
|---|
| 460 | } | 
|---|
| 461 |  | 
|---|
| 462 | #[ allow(bare_trait_objects)] | 
|---|
| 463 | fn vs16plus_instances( | 
|---|
| 464 | target: TargetArch, | 
|---|
| 465 | version: &'static str, | 
|---|
| 466 | env_getter: &dyn EnvGetter, | 
|---|
| 467 | ) -> Box<Iterator<Item = PathBuf>> { | 
|---|
| 468 | let instances = if let Some(instances) = vs15plus_instances(target, env_getter) { | 
|---|
| 469 | instances | 
|---|
| 470 | } else { | 
|---|
| 471 | return Box::new(iter::empty()); | 
|---|
| 472 | }; | 
|---|
| 473 | Box::new(instances.into_iter().filter_map(move |instance| { | 
|---|
| 474 | let installation_name = instance.installation_name()?; | 
|---|
| 475 | if installation_name.starts_with(&format!( "VisualStudio/{}.", version)) | 
|---|
| 476 | || installation_name.starts_with(&format!( "VisualStudioPreview/{}.", version)) | 
|---|
| 477 | { | 
|---|
| 478 | Some(instance.installation_path()?) | 
|---|
| 479 | } else { | 
|---|
| 480 | None | 
|---|
| 481 | } | 
|---|
| 482 | })) | 
|---|
| 483 | } | 
|---|
| 484 |  | 
|---|
| 485 | fn find_tool_in_vs16plus_path( | 
|---|
| 486 | tool: &str, | 
|---|
| 487 | target: TargetArch, | 
|---|
| 488 | version: &'static str, | 
|---|
| 489 | env_getter: &dyn EnvGetter, | 
|---|
| 490 | ) -> Option<Tool> { | 
|---|
| 491 | vs16plus_instances(target, version, env_getter) | 
|---|
| 492 | .filter_map(|path| { | 
|---|
| 493 | let path = path.join(tool); | 
|---|
| 494 | if !path.is_file() { | 
|---|
| 495 | return None; | 
|---|
| 496 | } | 
|---|
| 497 | let mut tool = Tool::with_family(path, MSVC_FAMILY); | 
|---|
| 498 | if target == TargetArch::X64 { | 
|---|
| 499 | tool.env.push(( "Platform".into(), "X64".into())); | 
|---|
| 500 | } | 
|---|
| 501 | if matches!(target, TargetArch::Arm64 | TargetArch::Arm64ec) { | 
|---|
| 502 | tool.env.push(( "Platform".into(), "ARM64".into())); | 
|---|
| 503 | } | 
|---|
| 504 | Some(tool) | 
|---|
| 505 | }) | 
|---|
| 506 | .next() | 
|---|
| 507 | } | 
|---|
| 508 |  | 
|---|
| 509 | fn find_msbuild_vs16(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 510 | find_tool_in_vs16plus_path( r"MSBuild\Current\Bin\MSBuild.exe", target, "16", env_getter) | 
|---|
| 511 | } | 
|---|
| 512 |  | 
|---|
| 513 | // In MSVC 15 (2017) MS once again changed the scheme for locating | 
|---|
| 514 | // the tooling.  Now we must go through some COM interfaces, which | 
|---|
| 515 | // is super fun for Rust. | 
|---|
| 516 | // | 
|---|
| 517 | // Note that much of this logic can be found [online] wrt paths, COM, etc. | 
|---|
| 518 | // | 
|---|
| 519 | // [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/ | 
|---|
| 520 | // | 
|---|
| 521 | // Returns MSVC 15+ instances (15, 16 right now), the order should be consider undefined. | 
|---|
| 522 | // | 
|---|
| 523 | // However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64. | 
|---|
| 524 | // Hence, as the last resort we try to use vswhere.exe to list available instances. | 
|---|
| 525 | fn vs15plus_instances(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<VsInstances> { | 
|---|
| 526 | vs15plus_instances_using_com() | 
|---|
| 527 | .or_else(|| vs15plus_instances_using_vswhere(target, env_getter)) | 
|---|
| 528 | } | 
|---|
| 529 |  | 
|---|
| 530 | fn vs15plus_instances_using_com() -> Option<VsInstances> { | 
|---|
| 531 | com::initialize().ok()?; | 
|---|
| 532 |  | 
|---|
| 533 | let config = SetupConfiguration::new().ok()?; | 
|---|
| 534 | let enum_setup_instances = config.enum_all_instances().ok()?; | 
|---|
| 535 |  | 
|---|
| 536 | Some(VsInstances::ComBased(enum_setup_instances)) | 
|---|
| 537 | } | 
|---|
| 538 |  | 
|---|
| 539 | fn vs15plus_instances_using_vswhere( | 
|---|
| 540 | target: TargetArch, | 
|---|
| 541 | env_getter: &dyn EnvGetter, | 
|---|
| 542 | ) -> Option<VsInstances> { | 
|---|
| 543 | let program_files_path = env_getter | 
|---|
| 544 | .get_env( "ProgramFiles(x86)") | 
|---|
| 545 | .or_else(|| env_getter.get_env( "ProgramFiles"))?; | 
|---|
| 546 |  | 
|---|
| 547 | let program_files_path = Path::new(program_files_path.as_ref()); | 
|---|
| 548 |  | 
|---|
| 549 | let vswhere_path = | 
|---|
| 550 | program_files_path.join( r"Microsoft Visual Studio\Installer\vswhere.exe"); | 
|---|
| 551 |  | 
|---|
| 552 | if !vswhere_path.exists() { | 
|---|
| 553 | return None; | 
|---|
| 554 | } | 
|---|
| 555 |  | 
|---|
| 556 | let tools_arch = match target { | 
|---|
| 557 | TargetArch::X86 | TargetArch::X64 => Some( "x86.x64"), | 
|---|
| 558 | TargetArch::Arm => Some( "ARM"), | 
|---|
| 559 | TargetArch::Arm64 | TargetArch::Arm64ec => Some( "ARM64"), | 
|---|
| 560 | }; | 
|---|
| 561 |  | 
|---|
| 562 | let vswhere_output = Command::new(vswhere_path) | 
|---|
| 563 | .args([ | 
|---|
| 564 | "-latest", | 
|---|
| 565 | "-products", | 
|---|
| 566 | "*", | 
|---|
| 567 | "-requires", | 
|---|
| 568 | &format!( "Microsoft.VisualStudio.Component.VC.Tools.{}", tools_arch?), | 
|---|
| 569 | "-format", | 
|---|
| 570 | "text", | 
|---|
| 571 | "-nologo", | 
|---|
| 572 | ]) | 
|---|
| 573 | .stderr(std::process::Stdio::inherit()) | 
|---|
| 574 | .output() | 
|---|
| 575 | .ok()?; | 
|---|
| 576 |  | 
|---|
| 577 | let vs_instances = | 
|---|
| 578 | VsInstances::VswhereBased(VswhereInstance::try_from(&vswhere_output.stdout).ok()?); | 
|---|
| 579 |  | 
|---|
| 580 | Some(vs_instances) | 
|---|
| 581 | } | 
|---|
| 582 |  | 
|---|
| 583 | // Inspired from official microsoft/vswhere ParseVersionString | 
|---|
| 584 | // i.e. at most four u16 numbers separated by '.' | 
|---|
| 585 | fn parse_version(version: &str) -> Option<Vec<u16>> { | 
|---|
| 586 | version | 
|---|
| 587 | .split( '.') | 
|---|
| 588 | .map(|chunk| u16::from_str(chunk).ok()) | 
|---|
| 589 | .collect() | 
|---|
| 590 | } | 
|---|
| 591 |  | 
|---|
| 592 | pub(super) fn find_msvc_15plus( | 
|---|
| 593 | tool: &str, | 
|---|
| 594 | target: TargetArch, | 
|---|
| 595 | env_getter: &dyn EnvGetter, | 
|---|
| 596 | ) -> Option<Tool> { | 
|---|
| 597 | let iter = vs15plus_instances(target, env_getter)?; | 
|---|
| 598 | iter.into_iter() | 
|---|
| 599 | .filter_map(|instance| { | 
|---|
| 600 | let version = parse_version(&instance.installation_version()?)?; | 
|---|
| 601 | let instance_path = instance.installation_path()?; | 
|---|
| 602 | let tool = tool_from_vs15plus_instance(tool, target, &instance_path, env_getter)?; | 
|---|
| 603 | Some((version, tool)) | 
|---|
| 604 | }) | 
|---|
| 605 | .max_by(|(a_version, _), (b_version, _)| a_version.cmp(b_version)) | 
|---|
| 606 | .map(|(_version, tool)| tool) | 
|---|
| 607 | } | 
|---|
| 608 |  | 
|---|
| 609 | // While the paths to Visual Studio 2017's devenv and MSBuild could | 
|---|
| 610 | // potentially be retrieved from the registry, finding them via | 
|---|
| 611 | // SetupConfiguration has shown to be [more reliable], and is preferred | 
|---|
| 612 | // according to Microsoft. To help head off potential regressions though, | 
|---|
| 613 | // we keep the registry method as a fallback option. | 
|---|
| 614 | // | 
|---|
| 615 | // [more reliable]: https://github.com/rust-lang/cc-rs/pull/331 | 
|---|
| 616 | fn find_tool_in_vs15_path( | 
|---|
| 617 | tool: &str, | 
|---|
| 618 | target: TargetArch, | 
|---|
| 619 | env_getter: &dyn EnvGetter, | 
|---|
| 620 | ) -> Option<Tool> { | 
|---|
| 621 | let mut path = match vs15plus_instances(target, env_getter) { | 
|---|
| 622 | Some(instances) => instances | 
|---|
| 623 | .into_iter() | 
|---|
| 624 | .filter_map(|instance| instance.installation_path()) | 
|---|
| 625 | .map(|path| path.join(tool)) | 
|---|
| 626 | .find(|path| path.is_file()), | 
|---|
| 627 | None => None, | 
|---|
| 628 | }; | 
|---|
| 629 |  | 
|---|
| 630 | if path.is_none() { | 
|---|
| 631 | let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"; | 
|---|
| 632 | path = LOCAL_MACHINE | 
|---|
| 633 | .open(key.as_ref()) | 
|---|
| 634 | .ok() | 
|---|
| 635 | .and_then(|key| key.query_str( "15.0").ok()) | 
|---|
| 636 | .map(|path| PathBuf::from(path).join(tool)) | 
|---|
| 637 | .and_then(|path| if path.is_file() { Some(path) } else { None }); | 
|---|
| 638 | } | 
|---|
| 639 |  | 
|---|
| 640 | path.map(|path| { | 
|---|
| 641 | let mut tool = Tool::with_family(path, MSVC_FAMILY); | 
|---|
| 642 | if target == TargetArch::X64 { | 
|---|
| 643 | tool.env.push(( "Platform".into(), "X64".into())); | 
|---|
| 644 | } else if matches!(target, TargetArch::Arm64 | TargetArch::Arm64ec) { | 
|---|
| 645 | tool.env.push(( "Platform".into(), "ARM64".into())); | 
|---|
| 646 | } | 
|---|
| 647 | tool | 
|---|
| 648 | }) | 
|---|
| 649 | } | 
|---|
| 650 |  | 
|---|
| 651 | fn tool_from_vs15plus_instance( | 
|---|
| 652 | tool: &str, | 
|---|
| 653 | target: TargetArch, | 
|---|
| 654 | instance_path: &Path, | 
|---|
| 655 | env_getter: &dyn EnvGetter, | 
|---|
| 656 | ) -> Option<Tool> { | 
|---|
| 657 | let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) = | 
|---|
| 658 | vs15plus_vc_paths(target, instance_path, env_getter)?; | 
|---|
| 659 | let tool_path = bin_path.join(tool); | 
|---|
| 660 | if !tool_path.exists() { | 
|---|
| 661 | return None; | 
|---|
| 662 | }; | 
|---|
| 663 |  | 
|---|
| 664 | let mut tool = MsvcTool::new(tool_path); | 
|---|
| 665 | tool.path.push(bin_path.clone()); | 
|---|
| 666 | tool.path.push(host_dylib_path); | 
|---|
| 667 | if let Some(alt_lib_path) = alt_lib_path { | 
|---|
| 668 | tool.libs.push(alt_lib_path); | 
|---|
| 669 | } | 
|---|
| 670 | tool.libs.push(lib_path); | 
|---|
| 671 | tool.include.push(include_path); | 
|---|
| 672 |  | 
|---|
| 673 | if let Some((atl_lib_path, atl_include_path)) = atl_paths(target, &root_path) { | 
|---|
| 674 | tool.libs.push(atl_lib_path); | 
|---|
| 675 | tool.include.push(atl_include_path); | 
|---|
| 676 | } | 
|---|
| 677 |  | 
|---|
| 678 | add_sdks(&mut tool, target, env_getter)?; | 
|---|
| 679 |  | 
|---|
| 680 | Some(tool.into_tool(env_getter)) | 
|---|
| 681 | } | 
|---|
| 682 |  | 
|---|
| 683 | fn vs15plus_vc_paths( | 
|---|
| 684 | target_arch: TargetArch, | 
|---|
| 685 | instance_path: &Path, | 
|---|
| 686 | env_getter: &dyn EnvGetter, | 
|---|
| 687 | ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option<PathBuf>, PathBuf)> { | 
|---|
| 688 | let version = vs15plus_vc_read_version(instance_path)?; | 
|---|
| 689 |  | 
|---|
| 690 | let hosts = match host_arch() { | 
|---|
| 691 | X86 => &[ "X86"], | 
|---|
| 692 | X86_64 => &[ "X64"], | 
|---|
| 693 | // Starting with VS 17.4, there is a natively hosted compiler on ARM64: | 
|---|
| 694 | // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/ | 
|---|
| 695 | // On older versions of VS, we use x64 if running under emulation is supported, | 
|---|
| 696 | // otherwise use x86. | 
|---|
| 697 | AARCH64 => { | 
|---|
| 698 | if is_amd64_emulation_supported() { | 
|---|
| 699 | &[ "ARM64", "X64", "X86"][..] | 
|---|
| 700 | } else { | 
|---|
| 701 | &[ "ARM64", "X86"] | 
|---|
| 702 | } | 
|---|
| 703 | } | 
|---|
| 704 | _ => return None, | 
|---|
| 705 | }; | 
|---|
| 706 | let target_dir = target_arch.as_vs_arch(); | 
|---|
| 707 | // The directory layout here is MSVC/bin/Host$host/$target/ | 
|---|
| 708 | let path = instance_path.join( r"VC\Tools\MSVC").join(version); | 
|---|
| 709 | // We use the first available host architecture that can build for the target | 
|---|
| 710 | let (host_path, host) = hosts.iter().find_map(|&x| { | 
|---|
| 711 | let candidate = path.join( "bin").join(format!( "Host{}", x)); | 
|---|
| 712 | if candidate.join(target_dir).exists() { | 
|---|
| 713 | Some((candidate, x)) | 
|---|
| 714 | } else { | 
|---|
| 715 | None | 
|---|
| 716 | } | 
|---|
| 717 | })?; | 
|---|
| 718 | // This is the path to the toolchain for a particular target, running | 
|---|
| 719 | // on a given host | 
|---|
| 720 | let bin_path = host_path.join(target_dir); | 
|---|
| 721 | // But! we also need PATH to contain the target directory for the host | 
|---|
| 722 | // architecture, because it contains dlls like mspdb140.dll compiled for | 
|---|
| 723 | // the host architecture. | 
|---|
| 724 | let host_dylib_path = host_path.join(host.to_lowercase()); | 
|---|
| 725 | let lib_fragment = if use_spectre_mitigated_libs(env_getter) { | 
|---|
| 726 | r"lib\spectre" | 
|---|
| 727 | } else { | 
|---|
| 728 | "lib" | 
|---|
| 729 | }; | 
|---|
| 730 | let lib_path = path.join(lib_fragment).join(target_dir); | 
|---|
| 731 | let alt_lib_path = | 
|---|
| 732 | (target_arch == TargetArch::Arm64ec).then(|| path.join(lib_fragment).join( "arm64ec")); | 
|---|
| 733 | let include_path = path.join( "include"); | 
|---|
| 734 | Some(( | 
|---|
| 735 | path, | 
|---|
| 736 | bin_path, | 
|---|
| 737 | host_dylib_path, | 
|---|
| 738 | lib_path, | 
|---|
| 739 | alt_lib_path, | 
|---|
| 740 | include_path, | 
|---|
| 741 | )) | 
|---|
| 742 | } | 
|---|
| 743 |  | 
|---|
| 744 | fn vs15plus_vc_read_version(dir: &Path) -> Option<String> { | 
|---|
| 745 | // Try to open the default version file. | 
|---|
| 746 | let mut version_path: PathBuf = | 
|---|
| 747 | dir.join( r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"); | 
|---|
| 748 | let mut version_file = if let Ok(f) = File::open(&version_path) { | 
|---|
| 749 | f | 
|---|
| 750 | } else { | 
|---|
| 751 | // If the default doesn't exist, search for other version files. | 
|---|
| 752 | // These are in the form Microsoft.VCToolsVersion.v143.default.txt | 
|---|
| 753 | // where `143` is any three decimal digit version number. | 
|---|
| 754 | // This sorts versions by lexical order and selects the highest version. | 
|---|
| 755 | let mut version_file = String::new(); | 
|---|
| 756 | version_path.pop(); | 
|---|
| 757 | for file in version_path.read_dir().ok()? { | 
|---|
| 758 | let name = file.ok()?.file_name(); | 
|---|
| 759 | let name = name.to_str()?; | 
|---|
| 760 | if name.starts_with( "Microsoft.VCToolsVersion.v") | 
|---|
| 761 | && name.ends_with( ".default.txt") | 
|---|
| 762 | && name > &version_file | 
|---|
| 763 | { | 
|---|
| 764 | version_file.replace_range(.., name); | 
|---|
| 765 | } | 
|---|
| 766 | } | 
|---|
| 767 | if version_file.is_empty() { | 
|---|
| 768 | return None; | 
|---|
| 769 | } | 
|---|
| 770 | version_path.push(version_file); | 
|---|
| 771 | File::open(version_path).ok()? | 
|---|
| 772 | }; | 
|---|
| 773 |  | 
|---|
| 774 | // Get the version string from the file we found. | 
|---|
| 775 | let mut version = String::new(); | 
|---|
| 776 | version_file.read_to_string(&mut version).ok()?; | 
|---|
| 777 | version.truncate(version.trim_end().len()); | 
|---|
| 778 | Some(version) | 
|---|
| 779 | } | 
|---|
| 780 |  | 
|---|
| 781 | fn use_spectre_mitigated_libs(env_getter: &dyn EnvGetter) -> bool { | 
|---|
| 782 | env_getter | 
|---|
| 783 | .get_env( "VSCMD_ARG_VCVARS_SPECTRE") | 
|---|
| 784 | .map(|env| env.as_ref() == "spectre") | 
|---|
| 785 | .unwrap_or_default() | 
|---|
| 786 | } | 
|---|
| 787 |  | 
|---|
| 788 | fn atl_paths(target: TargetArch, path: &Path) -> Option<(PathBuf, PathBuf)> { | 
|---|
| 789 | let atl_path = path.join( "atlmfc"); | 
|---|
| 790 | let sub = target.as_vs_arch(); | 
|---|
| 791 | if atl_path.exists() { | 
|---|
| 792 | Some((atl_path.join( "lib").join(sub), atl_path.join( "include"))) | 
|---|
| 793 | } else { | 
|---|
| 794 | None | 
|---|
| 795 | } | 
|---|
| 796 | } | 
|---|
| 797 |  | 
|---|
| 798 | // For MSVC 14 we need to find the Universal CRT as well as either | 
|---|
| 799 | // the Windows 10 SDK or Windows 8.1 SDK. | 
|---|
| 800 | pub(super) fn find_msvc_14( | 
|---|
| 801 | tool: &str, | 
|---|
| 802 | target: TargetArch, | 
|---|
| 803 | env_getter: &dyn EnvGetter, | 
|---|
| 804 | ) -> Option<Tool> { | 
|---|
| 805 | let vcdir = get_vc_dir( "14.0")?; | 
|---|
| 806 | let mut tool = get_tool(tool, &vcdir, target)?; | 
|---|
| 807 | add_sdks(&mut tool, target, env_getter)?; | 
|---|
| 808 | Some(tool.into_tool(env_getter)) | 
|---|
| 809 | } | 
|---|
| 810 |  | 
|---|
| 811 | fn add_sdks(tool: &mut MsvcTool, target: TargetArch, env_getter: &dyn EnvGetter) -> Option<()> { | 
|---|
| 812 | let sub = target.as_vs_arch(); | 
|---|
| 813 | let (ucrt, ucrt_version) = get_ucrt_dir()?; | 
|---|
| 814 |  | 
|---|
| 815 | let host = match host_arch() { | 
|---|
| 816 | X86 => "x86", | 
|---|
| 817 | X86_64 => "x64", | 
|---|
| 818 | AARCH64 => "arm64", | 
|---|
| 819 | _ => return None, | 
|---|
| 820 | }; | 
|---|
| 821 |  | 
|---|
| 822 | tool.path | 
|---|
| 823 | .push(ucrt.join( "bin").join(&ucrt_version).join(host)); | 
|---|
| 824 |  | 
|---|
| 825 | let ucrt_include = ucrt.join( "include").join(&ucrt_version); | 
|---|
| 826 | tool.include.push(ucrt_include.join( "ucrt")); | 
|---|
| 827 |  | 
|---|
| 828 | let ucrt_lib = ucrt.join( "lib").join(&ucrt_version); | 
|---|
| 829 | tool.libs.push(ucrt_lib.join( "ucrt").join(sub)); | 
|---|
| 830 |  | 
|---|
| 831 | if let Some((sdk, version)) = get_sdk10_dir(env_getter) { | 
|---|
| 832 | tool.path.push(sdk.join( "bin").join(host)); | 
|---|
| 833 | let sdk_lib = sdk.join( "lib").join(&version); | 
|---|
| 834 | tool.libs.push(sdk_lib.join( "um").join(sub)); | 
|---|
| 835 | let sdk_include = sdk.join( "include").join(&version); | 
|---|
| 836 | tool.include.push(sdk_include.join( "um")); | 
|---|
| 837 | tool.include.push(sdk_include.join( "cppwinrt")); | 
|---|
| 838 | tool.include.push(sdk_include.join( "winrt")); | 
|---|
| 839 | tool.include.push(sdk_include.join( "shared")); | 
|---|
| 840 | } else if let Some(sdk) = get_sdk81_dir() { | 
|---|
| 841 | tool.path.push(sdk.join( "bin").join(host)); | 
|---|
| 842 | let sdk_lib = sdk.join( "lib").join( "winv6.3"); | 
|---|
| 843 | tool.libs.push(sdk_lib.join( "um").join(sub)); | 
|---|
| 844 | let sdk_include = sdk.join( "include"); | 
|---|
| 845 | tool.include.push(sdk_include.join( "um")); | 
|---|
| 846 | tool.include.push(sdk_include.join( "winrt")); | 
|---|
| 847 | tool.include.push(sdk_include.join( "shared")); | 
|---|
| 848 | } | 
|---|
| 849 |  | 
|---|
| 850 | Some(()) | 
|---|
| 851 | } | 
|---|
| 852 |  | 
|---|
| 853 | fn add_env( | 
|---|
| 854 | tool: &mut Tool, | 
|---|
| 855 | env: &'static str, | 
|---|
| 856 | paths: Vec<PathBuf>, | 
|---|
| 857 | env_getter: &dyn EnvGetter, | 
|---|
| 858 | ) { | 
|---|
| 859 | let prev = env_getter.get_env(env); | 
|---|
| 860 | let prev = prev.as_ref().map(AsRef::as_ref).unwrap_or_default(); | 
|---|
| 861 | let prev = env::split_paths(&prev); | 
|---|
| 862 | let new = paths.into_iter().chain(prev); | 
|---|
| 863 | tool.env | 
|---|
| 864 | .push((env.to_string().into(), env::join_paths(new).unwrap())); | 
|---|
| 865 | } | 
|---|
| 866 |  | 
|---|
| 867 | // Given a possible MSVC installation directory, we look for the linker and | 
|---|
| 868 | // then add the MSVC library path. | 
|---|
| 869 | fn get_tool(tool: &str, path: &Path, target: TargetArch) -> Option<MsvcTool> { | 
|---|
| 870 | bin_subdir(target) | 
|---|
| 871 | .into_iter() | 
|---|
| 872 | .map(|(sub, host)| { | 
|---|
| 873 | ( | 
|---|
| 874 | path.join( "bin").join(sub).join(tool), | 
|---|
| 875 | path.join( "bin").join(host), | 
|---|
| 876 | ) | 
|---|
| 877 | }) | 
|---|
| 878 | .filter(|(path, _)| path.is_file()) | 
|---|
| 879 | .map(|(path, host)| { | 
|---|
| 880 | let mut tool = MsvcTool::new(path); | 
|---|
| 881 | tool.path.push(host); | 
|---|
| 882 | tool | 
|---|
| 883 | }) | 
|---|
| 884 | .filter_map(|mut tool| { | 
|---|
| 885 | let sub = vc_lib_subdir(target); | 
|---|
| 886 | tool.libs.push(path.join( "lib").join(sub)); | 
|---|
| 887 | tool.include.push(path.join( "include")); | 
|---|
| 888 | let atlmfc_path = path.join( "atlmfc"); | 
|---|
| 889 | if atlmfc_path.exists() { | 
|---|
| 890 | tool.libs.push(atlmfc_path.join( "lib").join(sub)); | 
|---|
| 891 | tool.include.push(atlmfc_path.join( "include")); | 
|---|
| 892 | } | 
|---|
| 893 | Some(tool) | 
|---|
| 894 | }) | 
|---|
| 895 | .next() | 
|---|
| 896 | } | 
|---|
| 897 |  | 
|---|
| 898 | // To find MSVC we look in a specific registry key for the version we are | 
|---|
| 899 | // trying to find. | 
|---|
| 900 | fn get_vc_dir(ver: &str) -> Option<PathBuf> { | 
|---|
| 901 | let key = r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7"; | 
|---|
| 902 | let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; | 
|---|
| 903 | let path = key.query_str(ver).ok()?; | 
|---|
| 904 | Some(path.into()) | 
|---|
| 905 | } | 
|---|
| 906 |  | 
|---|
| 907 | // To find the Universal CRT we look in a specific registry key for where | 
|---|
| 908 | // all the Universal CRTs are located and then sort them asciibetically to | 
|---|
| 909 | // find the newest version. While this sort of sorting isn't ideal,  it is | 
|---|
| 910 | // what vcvars does so that's good enough for us. | 
|---|
| 911 | // | 
|---|
| 912 | // Returns a pair of (root, version) for the ucrt dir if found | 
|---|
| 913 | fn get_ucrt_dir() -> Option<(PathBuf, String)> { | 
|---|
| 914 | let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"; | 
|---|
| 915 | let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; | 
|---|
| 916 | let root = key.query_str( "KitsRoot10").ok()?; | 
|---|
| 917 | let readdir = Path::new(&root).join( "lib").read_dir().ok()?; | 
|---|
| 918 | let max_libdir = readdir | 
|---|
| 919 | .filter_map(|dir| dir.ok()) | 
|---|
| 920 | .map(|dir| dir.path()) | 
|---|
| 921 | .filter(|dir| { | 
|---|
| 922 | dir.components() | 
|---|
| 923 | .last() | 
|---|
| 924 | .and_then(|c| c.as_os_str().to_str()) | 
|---|
| 925 | .map(|c| c.starts_with( "10.") && dir.join( "ucrt").is_dir()) | 
|---|
| 926 | .unwrap_or(false) | 
|---|
| 927 | }) | 
|---|
| 928 | .max()?; | 
|---|
| 929 | let version = max_libdir.components().last().unwrap(); | 
|---|
| 930 | let version = version.as_os_str().to_str().unwrap().to_string(); | 
|---|
| 931 | Some((root.into(), version)) | 
|---|
| 932 | } | 
|---|
| 933 |  | 
|---|
| 934 | // Vcvars finds the correct version of the Windows 10 SDK by looking | 
|---|
| 935 | // for the include `um\Windows.h` because sometimes a given version will | 
|---|
| 936 | // only have UCRT bits without the rest of the SDK. Since we only care about | 
|---|
| 937 | // libraries and not includes, we instead look for `um\x64\kernel32.lib`. | 
|---|
| 938 | // Since the 32-bit and 64-bit libraries are always installed together we | 
|---|
| 939 | // only need to bother checking x64, making this code a tiny bit simpler. | 
|---|
| 940 | // Like we do for the Universal CRT, we sort the possibilities | 
|---|
| 941 | // asciibetically to find the newest one as that is what vcvars does. | 
|---|
| 942 | // Before doing that, we check the "WindowsSdkDir" and "WindowsSDKVersion" | 
|---|
| 943 | // environment variables set by vcvars to use the environment sdk version | 
|---|
| 944 | // if one is already configured. | 
|---|
| 945 | fn get_sdk10_dir(env_getter: &dyn EnvGetter) -> Option<(PathBuf, String)> { | 
|---|
| 946 | if let (Some(root), Some(version)) = ( | 
|---|
| 947 | env_getter.get_env( "WindowsSdkDir"), | 
|---|
| 948 | env_getter | 
|---|
| 949 | .get_env( "WindowsSDKVersion") | 
|---|
| 950 | .as_ref() | 
|---|
| 951 | .and_then(|version| version.as_ref().to_str()), | 
|---|
| 952 | ) { | 
|---|
| 953 | return Some(( | 
|---|
| 954 | PathBuf::from(root), | 
|---|
| 955 | version.trim_end_matches( '\\ ').to_string(), | 
|---|
| 956 | )); | 
|---|
| 957 | } | 
|---|
| 958 |  | 
|---|
| 959 | let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0"; | 
|---|
| 960 | let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; | 
|---|
| 961 | let root = key.query_str( "InstallationFolder").ok()?; | 
|---|
| 962 | let readdir = Path::new(&root).join( "lib").read_dir().ok()?; | 
|---|
| 963 | let mut dirs = readdir | 
|---|
| 964 | .filter_map(|dir| dir.ok()) | 
|---|
| 965 | .map(|dir| dir.path()) | 
|---|
| 966 | .collect::<Vec<_>>(); | 
|---|
| 967 | dirs.sort(); | 
|---|
| 968 | let dir = dirs | 
|---|
| 969 | .into_iter() | 
|---|
| 970 | .rev() | 
|---|
| 971 | .find(|dir| dir.join( "um").join( "x64").join( "kernel32.lib").is_file())?; | 
|---|
| 972 | let version = dir.components().last().unwrap(); | 
|---|
| 973 | let version = version.as_os_str().to_str().unwrap().to_string(); | 
|---|
| 974 | Some((root.into(), version)) | 
|---|
| 975 | } | 
|---|
| 976 |  | 
|---|
| 977 | // Interestingly there are several subdirectories, `win7` `win8` and | 
|---|
| 978 | // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same | 
|---|
| 979 | // applies to us. Note that if we were targeting kernel mode drivers | 
|---|
| 980 | // instead of user mode applications, we would care. | 
|---|
| 981 | fn get_sdk81_dir() -> Option<PathBuf> { | 
|---|
| 982 | let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1"; | 
|---|
| 983 | let key = LOCAL_MACHINE.open(key.as_ref()).ok()?; | 
|---|
| 984 | let root = key.query_str( "InstallationFolder").ok()?; | 
|---|
| 985 | Some(root.into()) | 
|---|
| 986 | } | 
|---|
| 987 |  | 
|---|
| 988 | const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0; | 
|---|
| 989 | const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9; | 
|---|
| 990 | const PROCESSOR_ARCHITECTURE_ARM64: u16 = 12; | 
|---|
| 991 | const X86: u16 = PROCESSOR_ARCHITECTURE_INTEL; | 
|---|
| 992 | const X86_64: u16 = PROCESSOR_ARCHITECTURE_AMD64; | 
|---|
| 993 | const AARCH64: u16 = PROCESSOR_ARCHITECTURE_ARM64; | 
|---|
| 994 |  | 
|---|
| 995 | // When choosing the tool to use, we have to choose the one which matches | 
|---|
| 996 | // the target architecture. Otherwise we end up in situations where someone | 
|---|
| 997 | // on 32-bit Windows is trying to cross compile to 64-bit and it tries to | 
|---|
| 998 | // invoke the native 64-bit compiler which won't work. | 
|---|
| 999 | // | 
|---|
| 1000 | // For the return value of this function, the first member of the tuple is | 
|---|
| 1001 | // the folder of the tool we will be invoking, while the second member is | 
|---|
| 1002 | // the folder of the host toolchain for that tool which is essential when | 
|---|
| 1003 | // using a cross linker. We return a Vec since on x64 there are often two | 
|---|
| 1004 | // linkers that can target the architecture we desire. The 64-bit host | 
|---|
| 1005 | // linker is preferred, and hence first, due to 64-bit allowing it more | 
|---|
| 1006 | // address space to work with and potentially being faster. | 
|---|
| 1007 | fn bin_subdir(target: TargetArch) -> Vec<(&'static str, &'static str)> { | 
|---|
| 1008 | match (target, host_arch()) { | 
|---|
| 1009 | (TargetArch::X86, X86) => vec![( "", "")], | 
|---|
| 1010 | (TargetArch::X86, X86_64) => vec![( "amd64_x86", "amd64"), ( "", "")], | 
|---|
| 1011 | (TargetArch::X64, X86) => vec![( "x86_amd64", "")], | 
|---|
| 1012 | (TargetArch::X64, X86_64) => vec![( "amd64", "amd64"), ( "x86_amd64", "")], | 
|---|
| 1013 | (TargetArch::Arm, X86) => vec![( "x86_arm", "")], | 
|---|
| 1014 | (TargetArch::Arm, X86_64) => vec![( "amd64_arm", "amd64"), ( "x86_arm", "")], | 
|---|
| 1015 | _ => vec![], | 
|---|
| 1016 | } | 
|---|
| 1017 | } | 
|---|
| 1018 |  | 
|---|
| 1019 | // MSVC's x86 libraries are not in a subfolder | 
|---|
| 1020 | fn vc_lib_subdir(target: TargetArch) -> &'static str { | 
|---|
| 1021 | match target { | 
|---|
| 1022 | TargetArch::X86 => "", | 
|---|
| 1023 | TargetArch::X64 => "amd64", | 
|---|
| 1024 | TargetArch::Arm => "arm", | 
|---|
| 1025 | TargetArch::Arm64 | TargetArch::Arm64ec => "arm64", | 
|---|
| 1026 | } | 
|---|
| 1027 | } | 
|---|
| 1028 |  | 
|---|
| 1029 | #[ allow(bad_style)] | 
|---|
| 1030 | fn host_arch() -> u16 { | 
|---|
| 1031 | type DWORD = u32; | 
|---|
| 1032 | type WORD = u16; | 
|---|
| 1033 | type LPVOID = *mut u8; | 
|---|
| 1034 | type DWORD_PTR = usize; | 
|---|
| 1035 |  | 
|---|
| 1036 | #[ repr(C)] | 
|---|
| 1037 | struct SYSTEM_INFO { | 
|---|
| 1038 | wProcessorArchitecture: WORD, | 
|---|
| 1039 | _wReserved: WORD, | 
|---|
| 1040 | _dwPageSize: DWORD, | 
|---|
| 1041 | _lpMinimumApplicationAddress: LPVOID, | 
|---|
| 1042 | _lpMaximumApplicationAddress: LPVOID, | 
|---|
| 1043 | _dwActiveProcessorMask: DWORD_PTR, | 
|---|
| 1044 | _dwNumberOfProcessors: DWORD, | 
|---|
| 1045 | _dwProcessorType: DWORD, | 
|---|
| 1046 | _dwAllocationGranularity: DWORD, | 
|---|
| 1047 | _wProcessorLevel: WORD, | 
|---|
| 1048 | _wProcessorRevision: WORD, | 
|---|
| 1049 | } | 
|---|
| 1050 |  | 
|---|
| 1051 | extern "system"{ | 
|---|
| 1052 | fn GetNativeSystemInfo(lpSystemInfo: *mut SYSTEM_INFO); | 
|---|
| 1053 | } | 
|---|
| 1054 |  | 
|---|
| 1055 | unsafe { | 
|---|
| 1056 | let mut info = mem::zeroed(); | 
|---|
| 1057 | GetNativeSystemInfo(&mut info); | 
|---|
| 1058 | info.wProcessorArchitecture | 
|---|
| 1059 | } | 
|---|
| 1060 | } | 
|---|
| 1061 |  | 
|---|
| 1062 | // Given a registry key, look at all the sub keys and find the one which has | 
|---|
| 1063 | // the maximal numeric value. | 
|---|
| 1064 | // | 
|---|
| 1065 | // Returns the name of the maximal key as well as the opened maximal key. | 
|---|
| 1066 | fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> { | 
|---|
| 1067 | let mut max_vers = 0; | 
|---|
| 1068 | let mut max_key = None; | 
|---|
| 1069 | for subkey in key.iter().filter_map(|k| k.ok()) { | 
|---|
| 1070 | let val = subkey | 
|---|
| 1071 | .to_str() | 
|---|
| 1072 | .and_then(|s| s.trim_start_matches( 'v').replace( '.', "").parse().ok()); | 
|---|
| 1073 | let val = match val { | 
|---|
| 1074 | Some(s) => s, | 
|---|
| 1075 | None => continue, | 
|---|
| 1076 | }; | 
|---|
| 1077 | if val > max_vers { | 
|---|
| 1078 | if let Ok(k) = key.open(&subkey) { | 
|---|
| 1079 | max_vers = val; | 
|---|
| 1080 | max_key = Some((subkey, k)); | 
|---|
| 1081 | } | 
|---|
| 1082 | } | 
|---|
| 1083 | } | 
|---|
| 1084 | max_key | 
|---|
| 1085 | } | 
|---|
| 1086 |  | 
|---|
| 1087 | #[ inline(always)] | 
|---|
| 1088 | pub(super) fn has_msbuild_version(version: &str, env_getter: &dyn EnvGetter) -> bool { | 
|---|
| 1089 | match version { | 
|---|
| 1090 | "17.0"=> { | 
|---|
| 1091 | find_msbuild_vs17(TargetArch::X64, env_getter).is_some() | 
|---|
| 1092 | || find_msbuild_vs17(TargetArch::X86, env_getter).is_some() | 
|---|
| 1093 | || find_msbuild_vs17(TargetArch::Arm64, env_getter).is_some() | 
|---|
| 1094 | } | 
|---|
| 1095 | "16.0"=> { | 
|---|
| 1096 | find_msbuild_vs16(TargetArch::X64, env_getter).is_some() | 
|---|
| 1097 | || find_msbuild_vs16(TargetArch::X86, env_getter).is_some() | 
|---|
| 1098 | || find_msbuild_vs16(TargetArch::Arm64, env_getter).is_some() | 
|---|
| 1099 | } | 
|---|
| 1100 | "15.0"=> { | 
|---|
| 1101 | find_msbuild_vs15(TargetArch::X64, env_getter).is_some() | 
|---|
| 1102 | || find_msbuild_vs15(TargetArch::X86, env_getter).is_some() | 
|---|
| 1103 | || find_msbuild_vs15(TargetArch::Arm64, env_getter).is_some() | 
|---|
| 1104 | } | 
|---|
| 1105 | "14.0"=> LOCAL_MACHINE | 
|---|
| 1106 | .open(&OsString::from(format!( | 
|---|
| 1107 | "SOFTWARE\\ Microsoft\\ MSBuild\\ ToolsVersions\\ {}", | 
|---|
| 1108 | version | 
|---|
| 1109 | ))) | 
|---|
| 1110 | .is_ok(), | 
|---|
| 1111 | _ => false, | 
|---|
| 1112 | } | 
|---|
| 1113 | } | 
|---|
| 1114 |  | 
|---|
| 1115 | pub(super) fn find_devenv(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 1116 | find_devenv_vs15(target, env_getter) | 
|---|
| 1117 | } | 
|---|
| 1118 |  | 
|---|
| 1119 | fn find_devenv_vs15(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 1120 | find_tool_in_vs15_path( r"Common7\IDE\devenv.exe", target, env_getter) | 
|---|
| 1121 | } | 
|---|
| 1122 |  | 
|---|
| 1123 | // see http://stackoverflow.com/questions/328017/path-to-msbuild | 
|---|
| 1124 | pub(super) fn find_msbuild(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 1125 | // VS 15 (2017) changed how to locate msbuild | 
|---|
| 1126 | if let Some(r) = find_msbuild_vs17(target, env_getter) { | 
|---|
| 1127 | Some(r) | 
|---|
| 1128 | } else if let Some(r) = find_msbuild_vs16(target, env_getter) { | 
|---|
| 1129 | return Some(r); | 
|---|
| 1130 | } else if let Some(r) = find_msbuild_vs15(target, env_getter) { | 
|---|
| 1131 | return Some(r); | 
|---|
| 1132 | } else { | 
|---|
| 1133 | find_old_msbuild(target) | 
|---|
| 1134 | } | 
|---|
| 1135 | } | 
|---|
| 1136 |  | 
|---|
| 1137 | fn find_msbuild_vs15(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 1138 | find_tool_in_vs15_path( r"MSBuild\15.0\Bin\MSBuild.exe", target, env_getter) | 
|---|
| 1139 | } | 
|---|
| 1140 |  | 
|---|
| 1141 | fn find_old_msbuild(target: TargetArch) -> Option<Tool> { | 
|---|
| 1142 | let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions"; | 
|---|
| 1143 | LOCAL_MACHINE | 
|---|
| 1144 | .open(key.as_ref()) | 
|---|
| 1145 | .ok() | 
|---|
| 1146 | .and_then(|key| { | 
|---|
| 1147 | max_version(&key).and_then(|(_vers, key)| key.query_str( "MSBuildToolsPath").ok()) | 
|---|
| 1148 | }) | 
|---|
| 1149 | .map(|path| { | 
|---|
| 1150 | let mut path = PathBuf::from(path); | 
|---|
| 1151 | path.push( "MSBuild.exe"); | 
|---|
| 1152 | let mut tool = Tool::with_family(path, MSVC_FAMILY); | 
|---|
| 1153 | if target == TargetArch::X64 { | 
|---|
| 1154 | tool.env.push(( "Platform".into(), "X64".into())); | 
|---|
| 1155 | } | 
|---|
| 1156 | tool | 
|---|
| 1157 | }) | 
|---|
| 1158 | } | 
|---|
| 1159 | } | 
|---|
| 1160 |  | 
|---|
| 1161 | /// Non-Windows Implementation. | 
|---|
| 1162 | #[ cfg(not(windows))] | 
|---|
| 1163 | mod impl_ { | 
|---|
| 1164 | use std::{env, ffi::OsStr}; | 
|---|
| 1165 |  | 
|---|
| 1166 | use super::{EnvGetter, TargetArch, MSVC_FAMILY}; | 
|---|
| 1167 | use crate::Tool; | 
|---|
| 1168 |  | 
|---|
| 1169 | /// Finding msbuild.exe tool under unix system is not currently supported. | 
|---|
| 1170 | /// Maybe can check it using an environment variable looks like `MSBUILD_BIN`. | 
|---|
| 1171 | #[ inline(always)] | 
|---|
| 1172 | pub(super) fn find_msbuild(_target: TargetArch, _: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 1173 | None | 
|---|
| 1174 | } | 
|---|
| 1175 |  | 
|---|
| 1176 | // Finding devenv.exe tool under unix system is not currently supported. | 
|---|
| 1177 | // Maybe can check it using an environment variable looks like `DEVENV_BIN`. | 
|---|
| 1178 | #[ inline(always)] | 
|---|
| 1179 | pub(super) fn find_devenv(_target: TargetArch, _: &dyn EnvGetter) -> Option<Tool> { | 
|---|
| 1180 | None | 
|---|
| 1181 | } | 
|---|
| 1182 |  | 
|---|
| 1183 | /// Attempt to find the tool using environment variables set by vcvars. | 
|---|
| 1184 | pub(super) fn find_msvc_environment( | 
|---|
| 1185 | tool: &str, | 
|---|
| 1186 | _target: TargetArch, | 
|---|
| 1187 | env_getter: &dyn EnvGetter, | 
|---|
| 1188 | ) -> Option<Tool> { | 
|---|
| 1189 | // Early return if the environment doesn't contain a VC install. | 
|---|
| 1190 | let vc_install_dir = env_getter.get_env( "VCINSTALLDIR")?; | 
|---|
| 1191 | let vs_install_dir = env_getter.get_env( "VSINSTALLDIR")?; | 
|---|
| 1192 |  | 
|---|
| 1193 | let get_tool = |install_dir: &OsStr| { | 
|---|
| 1194 | env::split_paths(install_dir) | 
|---|
| 1195 | .map(|p| p.join(tool)) | 
|---|
| 1196 | .find(|p| p.exists()) | 
|---|
| 1197 | .map(|path| Tool::with_family(path, MSVC_FAMILY)) | 
|---|
| 1198 | }; | 
|---|
| 1199 |  | 
|---|
| 1200 | // Take the path of tool for the vc install directory. | 
|---|
| 1201 | get_tool(vc_install_dir.as_ref()) | 
|---|
| 1202 | // Take the path of tool for the vs install directory. | 
|---|
| 1203 | .or_else(|| get_tool(vs_install_dir.as_ref())) | 
|---|
| 1204 | // Take the path of tool for the current path environment. | 
|---|
| 1205 | .or_else(|| { | 
|---|
| 1206 | env_getter | 
|---|
| 1207 | .get_env( "PATH") | 
|---|
| 1208 | .as_ref() | 
|---|
| 1209 | .map(|path| path.as_ref()) | 
|---|
| 1210 | .and_then(get_tool) | 
|---|
| 1211 | }) | 
|---|
| 1212 | } | 
|---|
| 1213 |  | 
|---|
| 1214 | #[ inline(always)] | 
|---|
| 1215 | pub(super) fn find_msvc_15plus( | 
|---|
| 1216 | _tool: &str, | 
|---|
| 1217 | _target: TargetArch, | 
|---|
| 1218 | _: &dyn EnvGetter, | 
|---|
| 1219 | ) -> Option<Tool> { | 
|---|
| 1220 | None | 
|---|
| 1221 | } | 
|---|
| 1222 |  | 
|---|
| 1223 | // For MSVC 14 we need to find the Universal CRT as well as either | 
|---|
| 1224 | // the Windows 10 SDK or Windows 8.1 SDK. | 
|---|
| 1225 | #[ inline(always)] | 
|---|
| 1226 | pub(super) fn find_msvc_14( | 
|---|
| 1227 | _tool: &str, | 
|---|
| 1228 | _target: TargetArch, | 
|---|
| 1229 | _: &dyn EnvGetter, | 
|---|
| 1230 | ) -> Option<Tool> { | 
|---|
| 1231 | None | 
|---|
| 1232 | } | 
|---|
| 1233 |  | 
|---|
| 1234 | #[ inline(always)] | 
|---|
| 1235 | pub(super) fn has_msbuild_version(_version: &str, _: &dyn EnvGetter) -> bool { | 
|---|
| 1236 | false | 
|---|
| 1237 | } | 
|---|
| 1238 | } | 
|---|
| 1239 |  | 
|---|