summaryrefslogtreecommitdiff
path: root/vendor/cc/src/windows
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cc/src/windows')
-rw-r--r--vendor/cc/src/windows/com.rs110
-rw-r--r--vendor/cc/src/windows/find_tools.rs1238
-rw-r--r--vendor/cc/src/windows/mod.rs22
-rw-r--r--vendor/cc/src/windows/registry.rs191
-rw-r--r--vendor/cc/src/windows/setup_config.rs283
-rw-r--r--vendor/cc/src/windows/vs_instances.rs199
-rw-r--r--vendor/cc/src/windows/winapi.rs146
-rw-r--r--vendor/cc/src/windows/windows_link.rs19
-rw-r--r--vendor/cc/src/windows/windows_sys.rs139
9 files changed, 2347 insertions, 0 deletions
diff --git a/vendor/cc/src/windows/com.rs b/vendor/cc/src/windows/com.rs
new file mode 100644
index 00000000..0391b5af
--- /dev/null
+++ b/vendor/cc/src/windows/com.rs
@@ -0,0 +1,110 @@
+// Copyright © 2017 winapi-rs developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::windows::{
+ winapi::{IUnknown, Interface},
+ windows_sys::{
+ CoInitializeEx, SysFreeString, SysStringLen, BSTR, COINIT_MULTITHREADED, HRESULT, S_FALSE,
+ S_OK,
+ },
+};
+use std::{
+ convert::TryInto,
+ ffi::OsString,
+ ops::Deref,
+ os::windows::ffi::OsStringExt,
+ ptr::{null, null_mut},
+ slice::from_raw_parts,
+};
+
+pub fn initialize() -> Result<(), HRESULT> {
+ let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED.try_into().unwrap()) };
+ if err != S_OK && err != S_FALSE {
+ // S_FALSE just means COM is already initialized
+ Err(err)
+ } else {
+ Ok(())
+ }
+}
+
+pub struct ComPtr<T>(*mut T)
+where
+ T: Interface;
+impl<T> ComPtr<T>
+where
+ T: Interface,
+{
+ /// Creates a `ComPtr` to wrap a raw pointer.
+ /// It takes ownership over the pointer which means it does __not__ call `AddRef`.
+ /// `T` __must__ be a COM interface that inherits from `IUnknown`.
+ pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> {
+ assert!(!ptr.is_null());
+ ComPtr(ptr)
+ }
+ /// For internal use only.
+ fn as_unknown(&self) -> &IUnknown {
+ unsafe { &*(self.0 as *mut IUnknown) }
+ }
+ /// Performs `QueryInterface` fun.
+ pub fn cast<U>(&self) -> Result<ComPtr<U>, i32>
+ where
+ U: Interface,
+ {
+ let mut obj = null_mut();
+ let err = unsafe { self.as_unknown().QueryInterface(&U::uuidof(), &mut obj) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(unsafe { ComPtr::from_raw(obj as *mut U) })
+ }
+}
+impl<T> Deref for ComPtr<T>
+where
+ T: Interface,
+{
+ type Target = T;
+ fn deref(&self) -> &T {
+ unsafe { &*self.0 }
+ }
+}
+impl<T> Clone for ComPtr<T>
+where
+ T: Interface,
+{
+ fn clone(&self) -> Self {
+ unsafe {
+ self.as_unknown().AddRef();
+ ComPtr::from_raw(self.0)
+ }
+ }
+}
+impl<T> Drop for ComPtr<T>
+where
+ T: Interface,
+{
+ fn drop(&mut self) {
+ unsafe {
+ self.as_unknown().Release();
+ }
+ }
+}
+pub struct BStr(BSTR);
+impl BStr {
+ pub unsafe fn from_raw(s: BSTR) -> BStr {
+ BStr(s)
+ }
+ pub fn to_osstring(&self) -> OsString {
+ let len = unsafe { SysStringLen(self.0) };
+ let slice = unsafe { from_raw_parts(self.0, len as usize) };
+ OsStringExt::from_wide(slice)
+ }
+}
+impl Drop for BStr {
+ fn drop(&mut self) {
+ unsafe { SysFreeString(self.0) };
+ }
+}
diff --git a/vendor/cc/src/windows/find_tools.rs b/vendor/cc/src/windows/find_tools.rs
new file mode 100644
index 00000000..ec7c786c
--- /dev/null
+++ b/vendor/cc/src/windows/find_tools.rs
@@ -0,0 +1,1238 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A helper module to looking for windows-specific tools:
+//! 1. On Windows host, probe the Windows Registry if needed;
+//! 2. On non-Windows host, check specified environment variables.
+
+#![allow(clippy::upper_case_acronyms)]
+
+use std::{
+ env,
+ ffi::{OsStr, OsString},
+ ops::Deref,
+ path::PathBuf,
+ process::Command,
+ sync::Arc,
+};
+
+use crate::Tool;
+use crate::ToolFamily;
+
+const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false };
+
+/// The target provided by the user.
+#[derive(Copy, Clone, PartialEq, Eq)]
+enum TargetArch {
+ X86,
+ X64,
+ Arm,
+ Arm64,
+ Arm64ec,
+}
+impl TargetArch {
+ /// Parse the `TargetArch` from a str. Returns `None` if the arch is unrecognized.
+ fn new(arch: &str) -> Option<Self> {
+ // NOTE: Keep up to date with docs in [`find`].
+ match arch {
+ "x64" | "x86_64" => Some(Self::X64),
+ "arm64" | "aarch64" => Some(Self::Arm64),
+ "arm64ec" => Some(Self::Arm64ec),
+ "x86" | "i686" | "i586" => Some(Self::X86),
+ "arm" | "thumbv7a" => Some(Self::Arm),
+ _ => None,
+ }
+ }
+
+ #[cfg(windows)]
+ /// Gets the Visual Studio name for the architecture.
+ fn as_vs_arch(&self) -> &'static str {
+ match self {
+ Self::X64 => "x64",
+ Self::Arm64 | Self::Arm64ec => "arm64",
+ Self::X86 => "x86",
+ Self::Arm => "arm",
+ }
+ }
+}
+
+pub(crate) enum Env {
+ Owned(OsString),
+ Arced(Arc<OsStr>),
+}
+
+impl AsRef<OsStr> for Env {
+ fn as_ref(&self) -> &OsStr {
+ self.deref()
+ }
+}
+
+impl Deref for Env {
+ type Target = OsStr;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ Env::Owned(os_str) => os_str,
+ Env::Arced(os_str) => os_str,
+ }
+ }
+}
+
+impl From<Env> for PathBuf {
+ fn from(env: Env) -> Self {
+ match env {
+ Env::Owned(os_str) => PathBuf::from(os_str),
+ Env::Arced(os_str) => PathBuf::from(os_str.deref()),
+ }
+ }
+}
+
+pub(crate) trait EnvGetter {
+ fn get_env(&self, name: &'static str) -> Option<Env>;
+}
+
+struct StdEnvGetter;
+
+impl EnvGetter for StdEnvGetter {
+ #[allow(clippy::disallowed_methods)]
+ fn get_env(&self, name: &'static str) -> Option<Env> {
+ env::var_os(name).map(Env::Owned)
+ }
+}
+
+/// Attempts to find a tool within an MSVC installation using the Windows
+/// registry as a point to search from.
+///
+/// The `arch_or_target` argument is the architecture or the Rust target name
+/// that the tool should work for (e.g. compile or link for). The supported
+/// architecture names are:
+/// - `"x64"` or `"x86_64"`
+/// - `"arm64"` or `"aarch64"`
+/// - `"arm64ec"`
+/// - `"x86"`, `"i586"` or `"i686"`
+/// - `"arm"` or `"thumbv7a"`
+///
+/// The `tool` argument is the tool to find (e.g. `cl.exe` or `link.exe`).
+///
+/// This function will return `None` if the tool could not be found, or it will
+/// return `Some(cmd)` which represents a command that's ready to execute the
+/// tool with the appropriate environment variables set.
+///
+/// Note that this function always returns `None` for non-MSVC targets (if a
+/// full target name was specified).
+pub fn find(arch_or_target: &str, tool: &str) -> Option<Command> {
+ find_tool(arch_or_target, tool).map(|c| c.to_command())
+}
+
+/// Similar to the `find` function above, this function will attempt the same
+/// operation (finding a MSVC tool in a local install) but instead returns a
+/// `Tool` which may be introspected.
+pub fn find_tool(arch_or_target: &str, tool: &str) -> Option<Tool> {
+ let full_arch = if let Some((full_arch, rest)) = arch_or_target.split_once("-") {
+ // The logic is all tailored for MSVC, if the target is not that then
+ // bail out early.
+ if !rest.contains("msvc") {
+ return None;
+ }
+ full_arch
+ } else {
+ arch_or_target
+ };
+ find_tool_inner(full_arch, tool, &StdEnvGetter)
+}
+
+pub(crate) fn find_tool_inner(
+ full_arch: &str,
+ tool: &str,
+ env_getter: &dyn EnvGetter,
+) -> Option<Tool> {
+ // We only need the arch.
+ let target = TargetArch::new(full_arch)?;
+
+ // Looks like msbuild isn't located in the same location as other tools like
+ // cl.exe and lib.exe.
+ if tool.contains("msbuild") {
+ return impl_::find_msbuild(target, env_getter);
+ }
+
+ // Looks like devenv isn't located in the same location as other tools like
+ // cl.exe and lib.exe.
+ if tool.contains("devenv") {
+ return impl_::find_devenv(target, env_getter);
+ }
+
+ // Ok, if we're here, now comes the fun part of the probing. Default shells
+ // or shells like MSYS aren't really configured to execute `cl.exe` and the
+ // various compiler tools shipped as part of Visual Studio. Here we try to
+ // first find the relevant tool, then we also have to be sure to fill in
+ // environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
+ // the tool is actually usable.
+
+ impl_::find_msvc_environment(tool, target, env_getter)
+ .or_else(|| impl_::find_msvc_15plus(tool, target, env_getter))
+ .or_else(|| impl_::find_msvc_14(tool, target, env_getter))
+}
+
+/// A version of Visual Studio
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+#[non_exhaustive]
+pub enum VsVers {
+ /// Visual Studio 12 (2013)
+ #[deprecated(
+ note = "Visual Studio 12 is no longer supported. cc will never return this value."
+ )]
+ Vs12,
+ /// Visual Studio 14 (2015)
+ Vs14,
+ /// Visual Studio 15 (2017)
+ Vs15,
+ /// Visual Studio 16 (2019)
+ Vs16,
+ /// Visual Studio 17 (2022)
+ Vs17,
+}
+
+/// Find the most recent installed version of Visual Studio
+///
+/// This is used by the cmake crate to figure out the correct
+/// generator.
+#[allow(clippy::disallowed_methods)]
+pub fn find_vs_version() -> Result<VsVers, String> {
+ fn has_msbuild_version(version: &str) -> bool {
+ impl_::has_msbuild_version(version, &StdEnvGetter)
+ }
+
+ match std::env::var("VisualStudioVersion") {
+ Ok(version) => match &version[..] {
+ "17.0" => Ok(VsVers::Vs17),
+ "16.0" => Ok(VsVers::Vs16),
+ "15.0" => Ok(VsVers::Vs15),
+ "14.0" => Ok(VsVers::Vs14),
+ vers => Err(format!(
+ "\n\n\
+ unsupported or unknown VisualStudio version: {}\n\
+ if another version is installed consider running \
+ the appropriate vcvars script before building this \
+ crate\n\
+ ",
+ vers
+ )),
+ },
+ _ => {
+ // Check for the presence of a specific registry key
+ // that indicates visual studio is installed.
+ if has_msbuild_version("17.0") {
+ Ok(VsVers::Vs17)
+ } else if has_msbuild_version("16.0") {
+ Ok(VsVers::Vs16)
+ } else if has_msbuild_version("15.0") {
+ Ok(VsVers::Vs15)
+ } else if has_msbuild_version("14.0") {
+ Ok(VsVers::Vs14)
+ } else {
+ Err("\n\n\
+ couldn't determine visual studio generator\n\
+ if VisualStudio is installed, however, consider \
+ running the appropriate vcvars script before building \
+ this crate\n\
+ "
+ .to_string())
+ }
+ }
+ }
+}
+
+/// Windows Implementation.
+#[cfg(windows)]
+mod impl_ {
+ use crate::windows::com;
+ use crate::windows::registry::{RegistryKey, LOCAL_MACHINE};
+ use crate::windows::setup_config::SetupConfiguration;
+ use crate::windows::vs_instances::{VsInstances, VswhereInstance};
+ use crate::windows::windows_sys::{
+ GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE,
+ IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK,
+ };
+ use std::convert::TryFrom;
+ use std::env;
+ use std::ffi::OsString;
+ use std::fs::File;
+ use std::io::Read;
+ use std::iter;
+ use std::mem;
+ use std::path::{Path, PathBuf};
+ use std::process::Command;
+ use std::str::FromStr;
+ use std::sync::atomic::{AtomicBool, Ordering};
+ use std::sync::Once;
+
+ use super::{EnvGetter, TargetArch, MSVC_FAMILY};
+ use crate::Tool;
+
+ struct MsvcTool {
+ tool: PathBuf,
+ libs: Vec<PathBuf>,
+ path: Vec<PathBuf>,
+ include: Vec<PathBuf>,
+ }
+
+ struct LibraryHandle(HMODULE);
+
+ impl LibraryHandle {
+ fn new(name: &[u8]) -> Option<Self> {
+ let handle = unsafe { LoadLibraryA(name.as_ptr() as _) };
+ (!handle.is_null()).then_some(Self(handle))
+ }
+
+ /// Get a function pointer to a function in the library.
+ /// # SAFETY
+ ///
+ /// The caller must ensure that the function signature matches the actual function.
+ /// The easiest way to do this is to add an entry to `windows_sys_no_link.list` and use the
+ /// generated function for `func_signature`.
+ ///
+ /// The function returned cannot be used after the handle is dropped.
+ unsafe fn get_proc_address<F>(&self, name: &[u8]) -> Option<F> {
+ let symbol = GetProcAddress(self.0, name.as_ptr() as _);
+ symbol.map(|symbol| mem::transmute_copy(&symbol))
+ }
+ }
+
+ type GetMachineTypeAttributesFuncType =
+ unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32;
+ const _: () = {
+ // Ensure that our hand-written signature matches the actual function signature.
+ // We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to
+ // it, which will fail to load on older versions of Windows.
+ let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes;
+ };
+
+ fn is_amd64_emulation_supported_inner() -> Option<bool> {
+ // GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it.
+ let kernel32 = LibraryHandle::new(b"kernel32.dll\0")?;
+ // SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature.
+ let get_machine_type_attributes = unsafe {
+ kernel32
+ .get_proc_address::<GetMachineTypeAttributesFuncType>(b"GetMachineTypeAttributes\0")
+ }?;
+ let mut attributes = Default::default();
+ if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) } == S_OK
+ {
+ Some((attributes & UserEnabled) != 0)
+ } else {
+ Some(false)
+ }
+ }
+
+ fn is_amd64_emulation_supported() -> bool {
+ // TODO: Replace with a OnceLock once MSRV is 1.70.
+ static LOAD_VALUE: Once = Once::new();
+ static IS_SUPPORTED: AtomicBool = AtomicBool::new(false);
+
+ // Using Relaxed ordering since the Once is providing synchronization.
+ LOAD_VALUE.call_once(|| {
+ IS_SUPPORTED.store(
+ is_amd64_emulation_supported_inner().unwrap_or(false),
+ Ordering::Relaxed,
+ );
+ });
+ IS_SUPPORTED.load(Ordering::Relaxed)
+ }
+
+ impl MsvcTool {
+ fn new(tool: PathBuf) -> MsvcTool {
+ MsvcTool {
+ tool,
+ libs: Vec::new(),
+ path: Vec::new(),
+ include: Vec::new(),
+ }
+ }
+
+ fn into_tool(self, env_getter: &dyn EnvGetter) -> Tool {
+ let MsvcTool {
+ tool,
+ libs,
+ path,
+ include,
+ } = self;
+ let mut tool = Tool::with_family(tool, MSVC_FAMILY);
+ add_env(&mut tool, "LIB", libs, env_getter);
+ add_env(&mut tool, "PATH", path, env_getter);
+ add_env(&mut tool, "INCLUDE", include, env_getter);
+ tool
+ }
+ }
+
+ /// Checks to see if the target's arch matches the VS environment. Returns `None` if the
+ /// environment is unknown.
+ fn is_vscmd_target(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<bool> {
+ is_vscmd_target_env(target, env_getter).or_else(|| is_vscmd_target_cl(target, env_getter))
+ }
+
+ /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
+ /// given target's arch. Returns `None` if the variable does not exist.
+ fn is_vscmd_target_env(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<bool> {
+ let vscmd_arch = env_getter.get_env("VSCMD_ARG_TGT_ARCH")?;
+ Some(target.as_vs_arch() == vscmd_arch.as_ref())
+ }
+
+ /// Checks if the cl.exe target matches the given target's arch. Returns `None` if anything
+ /// fails.
+ fn is_vscmd_target_cl(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<bool> {
+ let cmd_target = vscmd_target_cl(env_getter)?;
+ Some(target.as_vs_arch() == cmd_target)
+ }
+
+ /// Detect the target architecture of `cl.exe` in the current path, and return `None` if this
+ /// fails for any reason.
+ fn vscmd_target_cl(env_getter: &dyn EnvGetter) -> Option<&'static str> {
+ let cl_exe = env_getter.get_env("PATH").and_then(|path| {
+ env::split_paths(&path)
+ .map(|p| p.join("cl.exe"))
+ .find(|p| p.exists())
+ })?;
+ let mut cl = Command::new(cl_exe);
+ cl.stderr(std::process::Stdio::piped())
+ .stdout(std::process::Stdio::null());
+
+ let out = cl.output().ok()?;
+ let cl_arch = out
+ .stderr
+ .split(|&b| b == b'\n' || b == b'\r')
+ .next()?
+ .rsplit(|&b| b == b' ')
+ .next()?;
+
+ match cl_arch {
+ b"x64" => Some("x64"),
+ b"x86" => Some("x86"),
+ b"ARM64" => Some("arm64"),
+ b"ARM" => Some("arm"),
+ _ => None,
+ }
+ }
+
+ /// Attempt to find the tool using environment variables set by vcvars.
+ pub(super) fn find_msvc_environment(
+ tool: &str,
+ target: TargetArch,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ // Early return if the environment isn't one that is known to have compiler toolsets in PATH
+ // `VCINSTALLDIR` is set from vcvarsall.bat (developer command prompt)
+ // `VSTEL_MSBuildProjectFullPath` is set by msbuild when invoking custom build steps
+ // NOTE: `VisualStudioDir` used to be used but this isn't set when invoking msbuild from the commandline
+ if env_getter.get_env("VCINSTALLDIR").is_none()
+ && env_getter.get_env("VSTEL_MSBuildProjectFullPath").is_none()
+ {
+ return None;
+ }
+
+ // If the vscmd target differs from the requested target then
+ // attempt to get the tool using the VS install directory.
+ if is_vscmd_target(target, env_getter) == Some(false) {
+ // We will only get here with versions 15+.
+ let vs_install_dir: PathBuf = env_getter.get_env("VSINSTALLDIR")?.into();
+ tool_from_vs15plus_instance(tool, target, &vs_install_dir, env_getter)
+ } else {
+ // Fallback to simply using the current environment.
+ env_getter
+ .get_env("PATH")
+ .and_then(|path| {
+ env::split_paths(&path)
+ .map(|p| p.join(tool))
+ .find(|p| p.exists())
+ })
+ .map(|path| Tool::with_family(path, MSVC_FAMILY))
+ }
+ }
+
+ fn find_msbuild_vs17(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> {
+ find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "17", env_getter)
+ }
+
+ #[allow(bare_trait_objects)]
+ fn vs16plus_instances(
+ target: TargetArch,
+ version: &'static str,
+ env_getter: &dyn EnvGetter,
+ ) -> Box<Iterator<Item = PathBuf>> {
+ let instances = if let Some(instances) = vs15plus_instances(target, env_getter) {
+ instances
+ } else {
+ return Box::new(iter::empty());
+ };
+ Box::new(instances.into_iter().filter_map(move |instance| {
+ let installation_name = instance.installation_name()?;
+ if installation_name.starts_with(&format!("VisualStudio/{}.", version))
+ || installation_name.starts_with(&format!("VisualStudioPreview/{}.", version))
+ {
+ Some(instance.installation_path()?)
+ } else {
+ None
+ }
+ }))
+ }
+
+ fn find_tool_in_vs16plus_path(
+ tool: &str,
+ target: TargetArch,
+ version: &'static str,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ vs16plus_instances(target, version, env_getter)
+ .filter_map(|path| {
+ let path = path.join(tool);
+ if !path.is_file() {
+ return None;
+ }
+ let mut tool = Tool::with_family(path, MSVC_FAMILY);
+ if target == TargetArch::X64 {
+ tool.env.push(("Platform".into(), "X64".into()));
+ }
+ if matches!(target, TargetArch::Arm64 | TargetArch::Arm64ec) {
+ tool.env.push(("Platform".into(), "ARM64".into()));
+ }
+ Some(tool)
+ })
+ .next()
+ }
+
+ fn find_msbuild_vs16(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> {
+ find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16", env_getter)
+ }
+
+ // In MSVC 15 (2017) MS once again changed the scheme for locating
+ // the tooling. Now we must go through some COM interfaces, which
+ // is super fun for Rust.
+ //
+ // Note that much of this logic can be found [online] wrt paths, COM, etc.
+ //
+ // [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
+ //
+ // Returns MSVC 15+ instances (15, 16 right now), the order should be consider undefined.
+ //
+ // However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64.
+ // Hence, as the last resort we try to use vswhere.exe to list available instances.
+ fn vs15plus_instances(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<VsInstances> {
+ vs15plus_instances_using_com()
+ .or_else(|| vs15plus_instances_using_vswhere(target, env_getter))
+ }
+
+ fn vs15plus_instances_using_com() -> Option<VsInstances> {
+ com::initialize().ok()?;
+
+ let config = SetupConfiguration::new().ok()?;
+ let enum_setup_instances = config.enum_all_instances().ok()?;
+
+ Some(VsInstances::ComBased(enum_setup_instances))
+ }
+
+ fn vs15plus_instances_using_vswhere(
+ target: TargetArch,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<VsInstances> {
+ let program_files_path = env_getter
+ .get_env("ProgramFiles(x86)")
+ .or_else(|| env_getter.get_env("ProgramFiles"))?;
+
+ let program_files_path = Path::new(program_files_path.as_ref());
+
+ let vswhere_path =
+ program_files_path.join(r"Microsoft Visual Studio\Installer\vswhere.exe");
+
+ if !vswhere_path.exists() {
+ return None;
+ }
+
+ let tools_arch = match target {
+ TargetArch::X86 | TargetArch::X64 => Some("x86.x64"),
+ TargetArch::Arm => Some("ARM"),
+ TargetArch::Arm64 | TargetArch::Arm64ec => Some("ARM64"),
+ };
+
+ let vswhere_output = Command::new(vswhere_path)
+ .args([
+ "-latest",
+ "-products",
+ "*",
+ "-requires",
+ &format!("Microsoft.VisualStudio.Component.VC.Tools.{}", tools_arch?),
+ "-format",
+ "text",
+ "-nologo",
+ ])
+ .stderr(std::process::Stdio::inherit())
+ .output()
+ .ok()?;
+
+ let vs_instances =
+ VsInstances::VswhereBased(VswhereInstance::try_from(&vswhere_output.stdout).ok()?);
+
+ Some(vs_instances)
+ }
+
+ // Inspired from official microsoft/vswhere ParseVersionString
+ // i.e. at most four u16 numbers separated by '.'
+ fn parse_version(version: &str) -> Option<Vec<u16>> {
+ version
+ .split('.')
+ .map(|chunk| u16::from_str(chunk).ok())
+ .collect()
+ }
+
+ pub(super) fn find_msvc_15plus(
+ tool: &str,
+ target: TargetArch,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ let iter = vs15plus_instances(target, env_getter)?;
+ iter.into_iter()
+ .filter_map(|instance| {
+ let version = parse_version(&instance.installation_version()?)?;
+ let instance_path = instance.installation_path()?;
+ let tool = tool_from_vs15plus_instance(tool, target, &instance_path, env_getter)?;
+ Some((version, tool))
+ })
+ .max_by(|(a_version, _), (b_version, _)| a_version.cmp(b_version))
+ .map(|(_version, tool)| tool)
+ }
+
+ // While the paths to Visual Studio 2017's devenv and MSBuild could
+ // potentially be retrieved from the registry, finding them via
+ // SetupConfiguration has shown to be [more reliable], and is preferred
+ // according to Microsoft. To help head off potential regressions though,
+ // we keep the registry method as a fallback option.
+ //
+ // [more reliable]: https://github.com/rust-lang/cc-rs/pull/331
+ fn find_tool_in_vs15_path(
+ tool: &str,
+ target: TargetArch,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ let mut path = match vs15plus_instances(target, env_getter) {
+ Some(instances) => instances
+ .into_iter()
+ .filter_map(|instance| instance.installation_path())
+ .map(|path| path.join(tool))
+ .find(|path| path.is_file()),
+ None => None,
+ };
+
+ if path.is_none() {
+ let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
+ path = LOCAL_MACHINE
+ .open(key.as_ref())
+ .ok()
+ .and_then(|key| key.query_str("15.0").ok())
+ .map(|path| PathBuf::from(path).join(tool))
+ .and_then(|path| if path.is_file() { Some(path) } else { None });
+ }
+
+ path.map(|path| {
+ let mut tool = Tool::with_family(path, MSVC_FAMILY);
+ if target == TargetArch::X64 {
+ tool.env.push(("Platform".into(), "X64".into()));
+ } else if matches!(target, TargetArch::Arm64 | TargetArch::Arm64ec) {
+ tool.env.push(("Platform".into(), "ARM64".into()));
+ }
+ tool
+ })
+ }
+
+ fn tool_from_vs15plus_instance(
+ tool: &str,
+ target: TargetArch,
+ instance_path: &Path,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) =
+ vs15plus_vc_paths(target, instance_path, env_getter)?;
+ let tool_path = bin_path.join(tool);
+ if !tool_path.exists() {
+ return None;
+ };
+
+ let mut tool = MsvcTool::new(tool_path);
+ tool.path.push(bin_path.clone());
+ tool.path.push(host_dylib_path);
+ if let Some(alt_lib_path) = alt_lib_path {
+ tool.libs.push(alt_lib_path);
+ }
+ tool.libs.push(lib_path);
+ tool.include.push(include_path);
+
+ if let Some((atl_lib_path, atl_include_path)) = atl_paths(target, &root_path) {
+ tool.libs.push(atl_lib_path);
+ tool.include.push(atl_include_path);
+ }
+
+ add_sdks(&mut tool, target, env_getter)?;
+
+ Some(tool.into_tool(env_getter))
+ }
+
+ fn vs15plus_vc_paths(
+ target_arch: TargetArch,
+ instance_path: &Path,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option<PathBuf>, PathBuf)> {
+ let version = vs15plus_vc_read_version(instance_path)?;
+
+ let hosts = match host_arch() {
+ X86 => &["X86"],
+ X86_64 => &["X64"],
+ // Starting with VS 17.4, there is a natively hosted compiler on ARM64:
+ // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
+ // On older versions of VS, we use x64 if running under emulation is supported,
+ // otherwise use x86.
+ AARCH64 => {
+ if is_amd64_emulation_supported() {
+ &["ARM64", "X64", "X86"][..]
+ } else {
+ &["ARM64", "X86"]
+ }
+ }
+ _ => return None,
+ };
+ let target_dir = target_arch.as_vs_arch();
+ // The directory layout here is MSVC/bin/Host$host/$target/
+ let path = instance_path.join(r"VC\Tools\MSVC").join(version);
+ // We use the first available host architecture that can build for the target
+ let (host_path, host) = hosts.iter().find_map(|&x| {
+ let candidate = path.join("bin").join(format!("Host{}", x));
+ if candidate.join(target_dir).exists() {
+ Some((candidate, x))
+ } else {
+ None
+ }
+ })?;
+ // This is the path to the toolchain for a particular target, running
+ // on a given host
+ let bin_path = host_path.join(target_dir);
+ // But! we also need PATH to contain the target directory for the host
+ // architecture, because it contains dlls like mspdb140.dll compiled for
+ // the host architecture.
+ let host_dylib_path = host_path.join(host.to_lowercase());
+ let lib_fragment = if use_spectre_mitigated_libs(env_getter) {
+ r"lib\spectre"
+ } else {
+ "lib"
+ };
+ let lib_path = path.join(lib_fragment).join(target_dir);
+ let alt_lib_path =
+ (target_arch == TargetArch::Arm64ec).then(|| path.join(lib_fragment).join("arm64ec"));
+ let include_path = path.join("include");
+ Some((
+ path,
+ bin_path,
+ host_dylib_path,
+ lib_path,
+ alt_lib_path,
+ include_path,
+ ))
+ }
+
+ fn vs15plus_vc_read_version(dir: &Path) -> Option<String> {
+ // Try to open the default version file.
+ let mut version_path: PathBuf =
+ dir.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
+ let mut version_file = if let Ok(f) = File::open(&version_path) {
+ f
+ } else {
+ // If the default doesn't exist, search for other version files.
+ // These are in the form Microsoft.VCToolsVersion.v143.default.txt
+ // where `143` is any three decimal digit version number.
+ // This sorts versions by lexical order and selects the highest version.
+ let mut version_file = String::new();
+ version_path.pop();
+ for file in version_path.read_dir().ok()? {
+ let name = file.ok()?.file_name();
+ let name = name.to_str()?;
+ if name.starts_with("Microsoft.VCToolsVersion.v")
+ && name.ends_with(".default.txt")
+ && name > &version_file
+ {
+ version_file.replace_range(.., name);
+ }
+ }
+ if version_file.is_empty() {
+ return None;
+ }
+ version_path.push(version_file);
+ File::open(version_path).ok()?
+ };
+
+ // Get the version string from the file we found.
+ let mut version = String::new();
+ version_file.read_to_string(&mut version).ok()?;
+ version.truncate(version.trim_end().len());
+ Some(version)
+ }
+
+ fn use_spectre_mitigated_libs(env_getter: &dyn EnvGetter) -> bool {
+ env_getter
+ .get_env("VSCMD_ARG_VCVARS_SPECTRE")
+ .map(|env| env.as_ref() == "spectre")
+ .unwrap_or_default()
+ }
+
+ fn atl_paths(target: TargetArch, path: &Path) -> Option<(PathBuf, PathBuf)> {
+ let atl_path = path.join("atlmfc");
+ let sub = target.as_vs_arch();
+ if atl_path.exists() {
+ Some((atl_path.join("lib").join(sub), atl_path.join("include")))
+ } else {
+ None
+ }
+ }
+
+ // For MSVC 14 we need to find the Universal CRT as well as either
+ // the Windows 10 SDK or Windows 8.1 SDK.
+ pub(super) fn find_msvc_14(
+ tool: &str,
+ target: TargetArch,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ let vcdir = get_vc_dir("14.0")?;
+ let mut tool = get_tool(tool, &vcdir, target)?;
+ add_sdks(&mut tool, target, env_getter)?;
+ Some(tool.into_tool(env_getter))
+ }
+
+ fn add_sdks(tool: &mut MsvcTool, target: TargetArch, env_getter: &dyn EnvGetter) -> Option<()> {
+ let sub = target.as_vs_arch();
+ let (ucrt, ucrt_version) = get_ucrt_dir()?;
+
+ let host = match host_arch() {
+ X86 => "x86",
+ X86_64 => "x64",
+ AARCH64 => "arm64",
+ _ => return None,
+ };
+
+ tool.path
+ .push(ucrt.join("bin").join(&ucrt_version).join(host));
+
+ let ucrt_include = ucrt.join("include").join(&ucrt_version);
+ tool.include.push(ucrt_include.join("ucrt"));
+
+ let ucrt_lib = ucrt.join("lib").join(&ucrt_version);
+ tool.libs.push(ucrt_lib.join("ucrt").join(sub));
+
+ if let Some((sdk, version)) = get_sdk10_dir(env_getter) {
+ tool.path.push(sdk.join("bin").join(host));
+ let sdk_lib = sdk.join("lib").join(&version);
+ tool.libs.push(sdk_lib.join("um").join(sub));
+ let sdk_include = sdk.join("include").join(&version);
+ tool.include.push(sdk_include.join("um"));
+ tool.include.push(sdk_include.join("cppwinrt"));
+ tool.include.push(sdk_include.join("winrt"));
+ tool.include.push(sdk_include.join("shared"));
+ } else if let Some(sdk) = get_sdk81_dir() {
+ tool.path.push(sdk.join("bin").join(host));
+ let sdk_lib = sdk.join("lib").join("winv6.3");
+ tool.libs.push(sdk_lib.join("um").join(sub));
+ let sdk_include = sdk.join("include");
+ tool.include.push(sdk_include.join("um"));
+ tool.include.push(sdk_include.join("winrt"));
+ tool.include.push(sdk_include.join("shared"));
+ }
+
+ Some(())
+ }
+
+ fn add_env(
+ tool: &mut Tool,
+ env: &'static str,
+ paths: Vec<PathBuf>,
+ env_getter: &dyn EnvGetter,
+ ) {
+ let prev = env_getter.get_env(env);
+ let prev = prev.as_ref().map(AsRef::as_ref).unwrap_or_default();
+ let prev = env::split_paths(&prev);
+ let new = paths.into_iter().chain(prev);
+ tool.env
+ .push((env.to_string().into(), env::join_paths(new).unwrap()));
+ }
+
+ // Given a possible MSVC installation directory, we look for the linker and
+ // then add the MSVC library path.
+ fn get_tool(tool: &str, path: &Path, target: TargetArch) -> Option<MsvcTool> {
+ bin_subdir(target)
+ .into_iter()
+ .map(|(sub, host)| {
+ (
+ path.join("bin").join(sub).join(tool),
+ path.join("bin").join(host),
+ )
+ })
+ .filter(|(path, _)| path.is_file())
+ .map(|(path, host)| {
+ let mut tool = MsvcTool::new(path);
+ tool.path.push(host);
+ tool
+ })
+ .filter_map(|mut tool| {
+ let sub = vc_lib_subdir(target);
+ tool.libs.push(path.join("lib").join(sub));
+ tool.include.push(path.join("include"));
+ let atlmfc_path = path.join("atlmfc");
+ if atlmfc_path.exists() {
+ tool.libs.push(atlmfc_path.join("lib").join(sub));
+ tool.include.push(atlmfc_path.join("include"));
+ }
+ Some(tool)
+ })
+ .next()
+ }
+
+ // To find MSVC we look in a specific registry key for the version we are
+ // trying to find.
+ fn get_vc_dir(ver: &str) -> Option<PathBuf> {
+ let key = r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7";
+ let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
+ let path = key.query_str(ver).ok()?;
+ Some(path.into())
+ }
+
+ // To find the Universal CRT we look in a specific registry key for where
+ // all the Universal CRTs are located and then sort them asciibetically to
+ // find the newest version. While this sort of sorting isn't ideal, it is
+ // what vcvars does so that's good enough for us.
+ //
+ // Returns a pair of (root, version) for the ucrt dir if found
+ fn get_ucrt_dir() -> Option<(PathBuf, String)> {
+ let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
+ let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
+ let root = key.query_str("KitsRoot10").ok()?;
+ let readdir = Path::new(&root).join("lib").read_dir().ok()?;
+ let max_libdir = readdir
+ .filter_map(|dir| dir.ok())
+ .map(|dir| dir.path())
+ .filter(|dir| {
+ dir.components()
+ .last()
+ .and_then(|c| c.as_os_str().to_str())
+ .map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir())
+ .unwrap_or(false)
+ })
+ .max()?;
+ let version = max_libdir.components().last().unwrap();
+ let version = version.as_os_str().to_str().unwrap().to_string();
+ Some((root.into(), version))
+ }
+
+ // Vcvars finds the correct version of the Windows 10 SDK by looking
+ // for the include `um\Windows.h` because sometimes a given version will
+ // only have UCRT bits without the rest of the SDK. Since we only care about
+ // libraries and not includes, we instead look for `um\x64\kernel32.lib`.
+ // Since the 32-bit and 64-bit libraries are always installed together we
+ // only need to bother checking x64, making this code a tiny bit simpler.
+ // Like we do for the Universal CRT, we sort the possibilities
+ // asciibetically to find the newest one as that is what vcvars does.
+ // Before doing that, we check the "WindowsSdkDir" and "WindowsSDKVersion"
+ // environment variables set by vcvars to use the environment sdk version
+ // if one is already configured.
+ fn get_sdk10_dir(env_getter: &dyn EnvGetter) -> Option<(PathBuf, String)> {
+ if let (Some(root), Some(version)) = (
+ env_getter.get_env("WindowsSdkDir"),
+ env_getter
+ .get_env("WindowsSDKVersion")
+ .as_ref()
+ .and_then(|version| version.as_ref().to_str()),
+ ) {
+ return Some((
+ PathBuf::from(root),
+ version.trim_end_matches('\\').to_string(),
+ ));
+ }
+
+ let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0";
+ let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
+ let root = key.query_str("InstallationFolder").ok()?;
+ let readdir = Path::new(&root).join("lib").read_dir().ok()?;
+ let mut dirs = readdir
+ .filter_map(|dir| dir.ok())
+ .map(|dir| dir.path())
+ .collect::<Vec<_>>();
+ dirs.sort();
+ let dir = dirs
+ .into_iter()
+ .rev()
+ .find(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file())?;
+ let version = dir.components().last().unwrap();
+ let version = version.as_os_str().to_str().unwrap().to_string();
+ Some((root.into(), version))
+ }
+
+ // Interestingly there are several subdirectories, `win7` `win8` and
+ // `winv6.3`. Vcvars seems to only care about `winv6.3` though, so the same
+ // applies to us. Note that if we were targeting kernel mode drivers
+ // instead of user mode applications, we would care.
+ fn get_sdk81_dir() -> Option<PathBuf> {
+ let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1";
+ let key = LOCAL_MACHINE.open(key.as_ref()).ok()?;
+ let root = key.query_str("InstallationFolder").ok()?;
+ Some(root.into())
+ }
+
+ const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0;
+ const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9;
+ const PROCESSOR_ARCHITECTURE_ARM64: u16 = 12;
+ const X86: u16 = PROCESSOR_ARCHITECTURE_INTEL;
+ const X86_64: u16 = PROCESSOR_ARCHITECTURE_AMD64;
+ const AARCH64: u16 = PROCESSOR_ARCHITECTURE_ARM64;
+
+ // When choosing the tool to use, we have to choose the one which matches
+ // the target architecture. Otherwise we end up in situations where someone
+ // on 32-bit Windows is trying to cross compile to 64-bit and it tries to
+ // invoke the native 64-bit compiler which won't work.
+ //
+ // For the return value of this function, the first member of the tuple is
+ // the folder of the tool we will be invoking, while the second member is
+ // the folder of the host toolchain for that tool which is essential when
+ // using a cross linker. We return a Vec since on x64 there are often two
+ // linkers that can target the architecture we desire. The 64-bit host
+ // linker is preferred, and hence first, due to 64-bit allowing it more
+ // address space to work with and potentially being faster.
+ fn bin_subdir(target: TargetArch) -> Vec<(&'static str, &'static str)> {
+ match (target, host_arch()) {
+ (TargetArch::X86, X86) => vec![("", "")],
+ (TargetArch::X86, X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
+ (TargetArch::X64, X86) => vec![("x86_amd64", "")],
+ (TargetArch::X64, X86_64) => vec![("amd64", "amd64"), ("x86_amd64", "")],
+ (TargetArch::Arm, X86) => vec![("x86_arm", "")],
+ (TargetArch::Arm, X86_64) => vec![("amd64_arm", "amd64"), ("x86_arm", "")],
+ _ => vec![],
+ }
+ }
+
+ // MSVC's x86 libraries are not in a subfolder
+ fn vc_lib_subdir(target: TargetArch) -> &'static str {
+ match target {
+ TargetArch::X86 => "",
+ TargetArch::X64 => "amd64",
+ TargetArch::Arm => "arm",
+ TargetArch::Arm64 | TargetArch::Arm64ec => "arm64",
+ }
+ }
+
+ #[allow(bad_style)]
+ fn host_arch() -> u16 {
+ type DWORD = u32;
+ type WORD = u16;
+ type LPVOID = *mut u8;
+ type DWORD_PTR = usize;
+
+ #[repr(C)]
+ struct SYSTEM_INFO {
+ wProcessorArchitecture: WORD,
+ _wReserved: WORD,
+ _dwPageSize: DWORD,
+ _lpMinimumApplicationAddress: LPVOID,
+ _lpMaximumApplicationAddress: LPVOID,
+ _dwActiveProcessorMask: DWORD_PTR,
+ _dwNumberOfProcessors: DWORD,
+ _dwProcessorType: DWORD,
+ _dwAllocationGranularity: DWORD,
+ _wProcessorLevel: WORD,
+ _wProcessorRevision: WORD,
+ }
+
+ extern "system" {
+ fn GetNativeSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
+ }
+
+ unsafe {
+ let mut info = mem::zeroed();
+ GetNativeSystemInfo(&mut info);
+ info.wProcessorArchitecture
+ }
+ }
+
+ // Given a registry key, look at all the sub keys and find the one which has
+ // the maximal numeric value.
+ //
+ // Returns the name of the maximal key as well as the opened maximal key.
+ fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
+ let mut max_vers = 0;
+ let mut max_key = None;
+ for subkey in key.iter().filter_map(|k| k.ok()) {
+ let val = subkey
+ .to_str()
+ .and_then(|s| s.trim_start_matches('v').replace('.', "").parse().ok());
+ let val = match val {
+ Some(s) => s,
+ None => continue,
+ };
+ if val > max_vers {
+ if let Ok(k) = key.open(&subkey) {
+ max_vers = val;
+ max_key = Some((subkey, k));
+ }
+ }
+ }
+ max_key
+ }
+
+ #[inline(always)]
+ pub(super) fn has_msbuild_version(version: &str, env_getter: &dyn EnvGetter) -> bool {
+ match version {
+ "17.0" => {
+ find_msbuild_vs17(TargetArch::X64, env_getter).is_some()
+ || find_msbuild_vs17(TargetArch::X86, env_getter).is_some()
+ || find_msbuild_vs17(TargetArch::Arm64, env_getter).is_some()
+ }
+ "16.0" => {
+ find_msbuild_vs16(TargetArch::X64, env_getter).is_some()
+ || find_msbuild_vs16(TargetArch::X86, env_getter).is_some()
+ || find_msbuild_vs16(TargetArch::Arm64, env_getter).is_some()
+ }
+ "15.0" => {
+ find_msbuild_vs15(TargetArch::X64, env_getter).is_some()
+ || find_msbuild_vs15(TargetArch::X86, env_getter).is_some()
+ || find_msbuild_vs15(TargetArch::Arm64, env_getter).is_some()
+ }
+ "14.0" => LOCAL_MACHINE
+ .open(&OsString::from(format!(
+ "SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}",
+ version
+ )))
+ .is_ok(),
+ _ => false,
+ }
+ }
+
+ pub(super) fn find_devenv(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> {
+ find_devenv_vs15(target, env_getter)
+ }
+
+ fn find_devenv_vs15(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> {
+ find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target, env_getter)
+ }
+
+ // see http://stackoverflow.com/questions/328017/path-to-msbuild
+ pub(super) fn find_msbuild(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> {
+ // VS 15 (2017) changed how to locate msbuild
+ if let Some(r) = find_msbuild_vs17(target, env_getter) {
+ Some(r)
+ } else if let Some(r) = find_msbuild_vs16(target, env_getter) {
+ return Some(r);
+ } else if let Some(r) = find_msbuild_vs15(target, env_getter) {
+ return Some(r);
+ } else {
+ find_old_msbuild(target)
+ }
+ }
+
+ fn find_msbuild_vs15(target: TargetArch, env_getter: &dyn EnvGetter) -> Option<Tool> {
+ find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target, env_getter)
+ }
+
+ fn find_old_msbuild(target: TargetArch) -> Option<Tool> {
+ let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
+ LOCAL_MACHINE
+ .open(key.as_ref())
+ .ok()
+ .and_then(|key| {
+ max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok())
+ })
+ .map(|path| {
+ let mut path = PathBuf::from(path);
+ path.push("MSBuild.exe");
+ let mut tool = Tool::with_family(path, MSVC_FAMILY);
+ if target == TargetArch::X64 {
+ tool.env.push(("Platform".into(), "X64".into()));
+ }
+ tool
+ })
+ }
+}
+
+/// Non-Windows Implementation.
+#[cfg(not(windows))]
+mod impl_ {
+ use std::{env, ffi::OsStr};
+
+ use super::{EnvGetter, TargetArch, MSVC_FAMILY};
+ use crate::Tool;
+
+ /// Finding msbuild.exe tool under unix system is not currently supported.
+ /// Maybe can check it using an environment variable looks like `MSBUILD_BIN`.
+ #[inline(always)]
+ pub(super) fn find_msbuild(_target: TargetArch, _: &dyn EnvGetter) -> Option<Tool> {
+ None
+ }
+
+ // Finding devenv.exe tool under unix system is not currently supported.
+ // Maybe can check it using an environment variable looks like `DEVENV_BIN`.
+ #[inline(always)]
+ pub(super) fn find_devenv(_target: TargetArch, _: &dyn EnvGetter) -> Option<Tool> {
+ None
+ }
+
+ /// Attempt to find the tool using environment variables set by vcvars.
+ pub(super) fn find_msvc_environment(
+ tool: &str,
+ _target: TargetArch,
+ env_getter: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ // Early return if the environment doesn't contain a VC install.
+ let vc_install_dir = env_getter.get_env("VCINSTALLDIR")?;
+ let vs_install_dir = env_getter.get_env("VSINSTALLDIR")?;
+
+ let get_tool = |install_dir: &OsStr| {
+ env::split_paths(install_dir)
+ .map(|p| p.join(tool))
+ .find(|p| p.exists())
+ .map(|path| Tool::with_family(path, MSVC_FAMILY))
+ };
+
+ // Take the path of tool for the vc install directory.
+ get_tool(vc_install_dir.as_ref())
+ // Take the path of tool for the vs install directory.
+ .or_else(|| get_tool(vs_install_dir.as_ref()))
+ // Take the path of tool for the current path environment.
+ .or_else(|| {
+ env_getter
+ .get_env("PATH")
+ .as_ref()
+ .map(|path| path.as_ref())
+ .and_then(get_tool)
+ })
+ }
+
+ #[inline(always)]
+ pub(super) fn find_msvc_15plus(
+ _tool: &str,
+ _target: TargetArch,
+ _: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ None
+ }
+
+ // For MSVC 14 we need to find the Universal CRT as well as either
+ // the Windows 10 SDK or Windows 8.1 SDK.
+ #[inline(always)]
+ pub(super) fn find_msvc_14(
+ _tool: &str,
+ _target: TargetArch,
+ _: &dyn EnvGetter,
+ ) -> Option<Tool> {
+ None
+ }
+
+ #[inline(always)]
+ pub(super) fn has_msbuild_version(_version: &str, _: &dyn EnvGetter) -> bool {
+ false
+ }
+}
diff --git a/vendor/cc/src/windows/mod.rs b/vendor/cc/src/windows/mod.rs
new file mode 100644
index 00000000..07b3e7b1
--- /dev/null
+++ b/vendor/cc/src/windows/mod.rs
@@ -0,0 +1,22 @@
+//! These modules are all glue to support reading the MSVC version from
+//! the registry and from COM interfaces.
+
+// This is used in the crate's public API, so don't use #[cfg(windows)]
+pub mod find_tools;
+
+#[cfg(windows)]
+mod windows_link;
+#[cfg(windows)]
+pub(crate) mod windows_sys;
+
+#[cfg(windows)]
+mod registry;
+#[cfg(windows)]
+#[macro_use]
+mod winapi;
+#[cfg(windows)]
+mod com;
+#[cfg(windows)]
+mod setup_config;
+#[cfg(windows)]
+mod vs_instances;
diff --git a/vendor/cc/src/windows/registry.rs b/vendor/cc/src/windows/registry.rs
new file mode 100644
index 00000000..83983032
--- /dev/null
+++ b/vendor/cc/src/windows/registry.rs
@@ -0,0 +1,191 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::windows::windows_sys::{
+ RegCloseKey, RegEnumKeyExW, RegOpenKeyExW, RegQueryValueExW, ERROR_NO_MORE_ITEMS,
+ ERROR_SUCCESS, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_32KEY, REG_SZ,
+};
+use std::{
+ ffi::{OsStr, OsString},
+ io,
+ ops::RangeFrom,
+ os::windows::prelude::*,
+ ptr::null_mut,
+};
+
+/// Must never be `HKEY_PERFORMANCE_DATA`.
+pub(crate) struct RegistryKey(Repr);
+
+#[allow(clippy::upper_case_acronyms)]
+type DWORD = u32;
+
+struct OwnedKey(HKEY);
+
+/// Note: must not encode `HKEY_PERFORMANCE_DATA` or one of its subkeys.
+enum Repr {
+ /// `HKEY_LOCAL_MACHINE`.
+ LocalMachine,
+ /// A subkey of `HKEY_LOCAL_MACHINE`.
+ Owned(OwnedKey),
+}
+
+pub struct Iter<'a> {
+ idx: RangeFrom<DWORD>,
+ key: &'a RegistryKey,
+}
+
+unsafe impl Sync for Repr {}
+unsafe impl Send for Repr {}
+
+pub(crate) const LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::LocalMachine);
+
+impl RegistryKey {
+ fn raw(&self) -> HKEY {
+ match self.0 {
+ Repr::LocalMachine => HKEY_LOCAL_MACHINE,
+ Repr::Owned(ref val) => val.0,
+ }
+ }
+
+ /// Open a sub-key of `self`.
+ pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
+ let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
+ let mut ret = null_mut();
+ let err = unsafe {
+ RegOpenKeyExW(
+ self.raw(),
+ key.as_ptr(),
+ 0,
+ KEY_READ | KEY_WOW64_32KEY,
+ &mut ret,
+ )
+ };
+ if err == ERROR_SUCCESS {
+ Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
+ } else {
+ Err(io::Error::from_raw_os_error(err as i32))
+ }
+ }
+
+ pub fn iter(&self) -> Iter {
+ Iter {
+ idx: 0..,
+ key: self,
+ }
+ }
+
+ pub fn query_str(&self, name: &str) -> io::Result<OsString> {
+ let name: &OsStr = name.as_ref();
+ let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
+ let mut len = 0;
+ let mut kind = 0;
+ unsafe {
+ let err = RegQueryValueExW(
+ self.raw(),
+ name.as_ptr(),
+ null_mut(),
+ &mut kind,
+ null_mut(),
+ &mut len,
+ );
+ if err != ERROR_SUCCESS {
+ return Err(io::Error::from_raw_os_error(err as i32));
+ }
+ if kind != REG_SZ {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "registry key wasn't a string",
+ ));
+ }
+
+ // The length here is the length in bytes, but we're using wide
+ // characters so we need to be sure to halve it for the length
+ // passed in.
+ assert!(len % 2 == 0, "impossible wide string size: {} bytes", len);
+ let vlen = len as usize / 2;
+ // Defensively initialized, see comment about
+ // `HKEY_PERFORMANCE_DATA` below.
+ let mut v = vec![0u16; vlen];
+ let err = RegQueryValueExW(
+ self.raw(),
+ name.as_ptr(),
+ null_mut(),
+ null_mut(),
+ v.as_mut_ptr() as *mut _,
+ &mut len,
+ );
+ // We don't check for `ERROR_MORE_DATA` (which would if the value
+ // grew between the first and second call to `RegQueryValueExW`),
+ // both because it's extremely unlikely, and this is a bit more
+ // defensive more defensive against weird types of registry keys.
+ if err != ERROR_SUCCESS {
+ return Err(io::Error::from_raw_os_error(err as i32));
+ }
+ // The length is allowed to change, but should still be even, as
+ // well as smaller.
+ assert!(len % 2 == 0, "impossible wide string size: {} bytes", len);
+ // If the length grew but returned a success code, it *probably*
+ // indicates we're `HKEY_PERFORMANCE_DATA` or a subkey(?). We
+ // consider this UB, since those keys write "undefined" or
+ // "unpredictable" values to len, and need to use a completely
+ // different loop structure. This should be impossible (and enforce
+ // it in the API to the best of our ability), but to mitigate the
+ // damage we do some smoke-checks on the len, and ensure `v` has
+ // been fully initialized (rather than trusting the result of
+ // `RegQueryValueExW`).
+ let actual_len = len as usize / 2;
+ assert!(actual_len <= v.len());
+ v.truncate(actual_len);
+ // Some registry keys may have a terminating nul character, but
+ // we're not interested in that, so chop it off if it's there.
+ if !v.is_empty() && v[v.len() - 1] == 0 {
+ v.pop();
+ }
+ Ok(OsString::from_wide(&v))
+ }
+ }
+}
+
+impl Drop for OwnedKey {
+ fn drop(&mut self) {
+ unsafe {
+ RegCloseKey(self.0);
+ }
+ }
+}
+
+impl<'a> Iterator for Iter<'a> {
+ type Item = io::Result<OsString>;
+
+ fn next(&mut self) -> Option<io::Result<OsString>> {
+ self.idx.next().and_then(|i| unsafe {
+ let mut v = Vec::with_capacity(256);
+ let mut len = v.capacity() as DWORD;
+ let ret = RegEnumKeyExW(
+ self.key.raw(),
+ i,
+ v.as_mut_ptr(),
+ &mut len,
+ null_mut(),
+ null_mut(),
+ null_mut(),
+ null_mut(),
+ );
+ if ret == ERROR_NO_MORE_ITEMS {
+ None
+ } else if ret != ERROR_SUCCESS {
+ Some(Err(io::Error::from_raw_os_error(ret as i32)))
+ } else {
+ v.set_len(len as usize);
+ Some(Ok(OsString::from_wide(&v)))
+ }
+ })
+ }
+}
diff --git a/vendor/cc/src/windows/setup_config.rs b/vendor/cc/src/windows/setup_config.rs
new file mode 100644
index 00000000..5739ecf7
--- /dev/null
+++ b/vendor/cc/src/windows/setup_config.rs
@@ -0,0 +1,283 @@
+// Copyright © 2017 winapi-rs developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(bad_style)]
+#![allow(unused)]
+
+use crate::windows::{
+ com::{BStr, ComPtr},
+ winapi::{
+ IUnknown, IUnknownVtbl, Interface, LCID, LPCOLESTR, LPCWSTR, LPFILETIME, LPSAFEARRAY,
+ PULONGLONG, ULONG,
+ },
+ windows_sys::{CoCreateInstance, BSTR, CLSCTX_ALL, HRESULT, S_FALSE},
+};
+
+use std::{
+ ffi::OsString,
+ ptr::{null, null_mut},
+};
+
+// Bindings to the Setup.Configuration stuff
+pub type InstanceState = u32;
+
+pub const eNone: InstanceState = 0;
+pub const eLocal: InstanceState = 1;
+pub const eRegistered: InstanceState = 2;
+pub const eNoRebootRequired: InstanceState = 4;
+pub const eComplete: InstanceState = -1i32 as u32;
+
+RIDL! {#[uuid(0xb41463c3, 0x8866, 0x43b5, 0xbc, 0x33, 0x2b, 0x06, 0x76, 0xf7, 0xf4, 0x2e)]
+interface ISetupInstance(ISetupInstanceVtbl): IUnknown(IUnknownVtbl) {
+ fn GetInstanceId(
+ pbstrInstanceId: *mut BSTR,
+ ) -> HRESULT,
+ fn GetInstallDate(
+ pInstallDate: LPFILETIME,
+ ) -> HRESULT,
+ fn GetInstallationName(
+ pbstrInstallationName: *mut BSTR,
+ ) -> HRESULT,
+ fn GetInstallationPath(
+ pbstrInstallationPath: *mut BSTR,
+ ) -> HRESULT,
+ fn GetInstallationVersion(
+ pbstrInstallationVersion: *mut BSTR,
+ ) -> HRESULT,
+ fn GetDisplayName(
+ lcid: LCID,
+ pbstrDisplayName: *mut BSTR,
+ ) -> HRESULT,
+ fn GetDescription(
+ lcid: LCID,
+ pbstrDescription: *mut BSTR,
+ ) -> HRESULT,
+ fn ResolvePath(
+ pwszRelativePath: LPCOLESTR,
+ pbstrAbsolutePath: *mut BSTR,
+ ) -> HRESULT,
+}}
+
+RIDL! {#[uuid(0x89143c9a, 0x05af, 0x49b0, 0xb7, 0x17, 0x72, 0xe2, 0x18, 0xa2, 0x18, 0x5c)]
+interface ISetupInstance2(ISetupInstance2Vtbl): ISetupInstance(ISetupInstanceVtbl) {
+ fn GetState(
+ pState: *mut InstanceState,
+ ) -> HRESULT,
+ fn GetPackages(
+ ppsaPackages: *mut LPSAFEARRAY,
+ ) -> HRESULT,
+ fn GetProduct(
+ ppPackage: *mut *mut ISetupPackageReference,
+ ) -> HRESULT,
+ fn GetProductPath(
+ pbstrProductPath: *mut BSTR,
+ ) -> HRESULT,
+}}
+
+RIDL! {#[uuid(0x6380bcff, 0x41d3, 0x4b2e, 0x8b, 0x2e, 0xbf, 0x8a, 0x68, 0x10, 0xc8, 0x48)]
+interface IEnumSetupInstances(IEnumSetupInstancesVtbl): IUnknown(IUnknownVtbl) {
+ fn Next(
+ celt: ULONG,
+ rgelt: *mut *mut ISetupInstance,
+ pceltFetched: *mut ULONG,
+ ) -> HRESULT,
+ fn Skip(
+ celt: ULONG,
+ ) -> HRESULT,
+ fn Reset() -> HRESULT,
+ fn Clone(
+ ppenum: *mut *mut IEnumSetupInstances,
+ ) -> HRESULT,
+}}
+
+RIDL! {#[uuid(0x42843719, 0xdb4c, 0x46c2, 0x8e, 0x7c, 0x64, 0xf1, 0x81, 0x6e, 0xfd, 0x5b)]
+interface ISetupConfiguration(ISetupConfigurationVtbl): IUnknown(IUnknownVtbl) {
+ fn EnumInstances(
+ ppEnumInstances: *mut *mut IEnumSetupInstances,
+ ) -> HRESULT,
+ fn GetInstanceForCurrentProcess(
+ ppInstance: *mut *mut ISetupInstance,
+ ) -> HRESULT,
+ fn GetInstanceForPath(
+ wzPath: LPCWSTR,
+ ppInstance: *mut *mut ISetupInstance,
+ ) -> HRESULT,
+}}
+
+RIDL! {#[uuid(0x26aab78c, 0x4a60, 0x49d6, 0xaf, 0x3b, 0x3c, 0x35, 0xbc, 0x93, 0x36, 0x5d)]
+interface ISetupConfiguration2(ISetupConfiguration2Vtbl):
+ ISetupConfiguration(ISetupConfigurationVtbl) {
+ fn EnumAllInstances(
+ ppEnumInstances: *mut *mut IEnumSetupInstances,
+ ) -> HRESULT,
+}}
+
+RIDL! {#[uuid(0xda8d8a16, 0xb2b6, 0x4487, 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5)]
+interface ISetupPackageReference(ISetupPackageReferenceVtbl): IUnknown(IUnknownVtbl) {
+ fn GetId(
+ pbstrId: *mut BSTR,
+ ) -> HRESULT,
+ fn GetVersion(
+ pbstrVersion: *mut BSTR,
+ ) -> HRESULT,
+ fn GetChip(
+ pbstrChip: *mut BSTR,
+ ) -> HRESULT,
+ fn GetLanguage(
+ pbstrLanguage: *mut BSTR,
+ ) -> HRESULT,
+ fn GetBranch(
+ pbstrBranch: *mut BSTR,
+ ) -> HRESULT,
+ fn GetType(
+ pbstrType: *mut BSTR,
+ ) -> HRESULT,
+ fn GetUniqueId(
+ pbstrUniqueId: *mut BSTR,
+ ) -> HRESULT,
+}}
+
+RIDL! {#[uuid(0x42b21b78, 0x6192, 0x463e, 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c)]
+interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) {
+ fn ParseVersion(
+ pwszVersion: LPCOLESTR,
+ pullVersion: PULONGLONG,
+ ) -> HRESULT,
+ fn ParseVersionRange(
+ pwszVersionRange: LPCOLESTR,
+ pullMinVersion: PULONGLONG,
+ pullMaxVersion: PULONGLONG,
+ ) -> HRESULT,
+}}
+
+DEFINE_GUID! {CLSID_SetupConfiguration,
+0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d}
+
+// Safe wrapper around the COM interfaces
+pub struct SetupConfiguration(ComPtr<ISetupConfiguration>);
+
+impl SetupConfiguration {
+ pub fn new() -> Result<SetupConfiguration, i32> {
+ let mut obj = null_mut();
+ let err = unsafe {
+ CoCreateInstance(
+ &CLSID_SetupConfiguration,
+ null_mut(),
+ CLSCTX_ALL,
+ &ISetupConfiguration::uuidof(),
+ &mut obj,
+ )
+ };
+ if err < 0 {
+ return Err(err);
+ }
+ let obj = unsafe { ComPtr::from_raw(obj as *mut ISetupConfiguration) };
+ Ok(SetupConfiguration(obj))
+ }
+ pub fn get_instance_for_current_process(&self) -> Result<SetupInstance, i32> {
+ let mut obj = null_mut();
+ let err = unsafe { self.0.GetInstanceForCurrentProcess(&mut obj) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(unsafe { SetupInstance::from_raw(obj) })
+ }
+ pub fn enum_instances(&self) -> Result<EnumSetupInstances, i32> {
+ let mut obj = null_mut();
+ let err = unsafe { self.0.EnumInstances(&mut obj) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(unsafe { EnumSetupInstances::from_raw(obj) })
+ }
+ pub fn enum_all_instances(&self) -> Result<EnumSetupInstances, i32> {
+ let mut obj = null_mut();
+ let this = self.0.cast::<ISetupConfiguration2>()?;
+ let err = unsafe { this.EnumAllInstances(&mut obj) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(unsafe { EnumSetupInstances::from_raw(obj) })
+ }
+}
+
+pub struct SetupInstance(ComPtr<ISetupInstance>);
+
+impl SetupInstance {
+ pub unsafe fn from_raw(obj: *mut ISetupInstance) -> SetupInstance {
+ SetupInstance(ComPtr::from_raw(obj))
+ }
+ pub fn instance_id(&self) -> Result<OsString, i32> {
+ let mut s = null();
+ let err = unsafe { self.0.GetInstanceId(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(bstr.to_osstring())
+ }
+ pub fn installation_name(&self) -> Result<OsString, i32> {
+ let mut s = null();
+ let err = unsafe { self.0.GetInstallationName(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(bstr.to_osstring())
+ }
+ pub fn installation_path(&self) -> Result<OsString, i32> {
+ let mut s = null();
+ let err = unsafe { self.0.GetInstallationPath(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(bstr.to_osstring())
+ }
+ pub fn installation_version(&self) -> Result<OsString, i32> {
+ let mut s = null();
+ let err = unsafe { self.0.GetInstallationVersion(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(bstr.to_osstring())
+ }
+ pub fn product_path(&self) -> Result<OsString, i32> {
+ let mut s = null();
+ let this = self.0.cast::<ISetupInstance2>()?;
+ let err = unsafe { this.GetProductPath(&mut s) };
+ let bstr = unsafe { BStr::from_raw(s) };
+ if err < 0 {
+ return Err(err);
+ }
+ Ok(bstr.to_osstring())
+ }
+}
+
+pub struct EnumSetupInstances(ComPtr<IEnumSetupInstances>);
+
+impl EnumSetupInstances {
+ pub unsafe fn from_raw(obj: *mut IEnumSetupInstances) -> EnumSetupInstances {
+ EnumSetupInstances(ComPtr::from_raw(obj))
+ }
+}
+
+impl Iterator for EnumSetupInstances {
+ type Item = Result<SetupInstance, i32>;
+ fn next(&mut self) -> Option<Result<SetupInstance, i32>> {
+ let mut obj = null_mut();
+ let err = unsafe { self.0.Next(1, &mut obj, null_mut()) };
+ if err < 0 {
+ return Some(Err(err));
+ }
+ if err == S_FALSE {
+ return None;
+ }
+ Some(Ok(unsafe { SetupInstance::from_raw(obj) }))
+ }
+}
diff --git a/vendor/cc/src/windows/vs_instances.rs b/vendor/cc/src/windows/vs_instances.rs
new file mode 100644
index 00000000..3e6eeed9
--- /dev/null
+++ b/vendor/cc/src/windows/vs_instances.rs
@@ -0,0 +1,199 @@
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::convert::TryFrom;
+use std::io::BufRead;
+use std::path::PathBuf;
+
+use crate::windows::setup_config::{EnumSetupInstances, SetupInstance};
+
+pub enum VsInstance {
+ Com(SetupInstance),
+ Vswhere(VswhereInstance),
+}
+
+impl VsInstance {
+ pub fn installation_name(&self) -> Option<Cow<str>> {
+ match self {
+ VsInstance::Com(s) => s
+ .installation_name()
+ .ok()
+ .and_then(|s| s.into_string().ok())
+ .map(Cow::from),
+ VsInstance::Vswhere(v) => v.map.get("installationName").map(Cow::from),
+ }
+ }
+
+ pub fn installation_path(&self) -> Option<PathBuf> {
+ match self {
+ VsInstance::Com(s) => s.installation_path().ok().map(PathBuf::from),
+ VsInstance::Vswhere(v) => v.map.get("installationPath").map(PathBuf::from),
+ }
+ }
+
+ pub fn installation_version(&self) -> Option<Cow<str>> {
+ match self {
+ VsInstance::Com(s) => s
+ .installation_version()
+ .ok()
+ .and_then(|s| s.into_string().ok())
+ .map(Cow::from),
+ VsInstance::Vswhere(v) => v.map.get("installationVersion").map(Cow::from),
+ }
+ }
+}
+
+pub enum VsInstances {
+ ComBased(EnumSetupInstances),
+ VswhereBased(VswhereInstance),
+}
+
+impl IntoIterator for VsInstances {
+ type Item = VsInstance;
+ #[allow(bare_trait_objects)]
+ type IntoIter = Box<Iterator<Item = Self::Item>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ match self {
+ VsInstances::ComBased(e) => {
+ Box::new(e.into_iter().filter_map(Result::ok).map(VsInstance::Com))
+ }
+ VsInstances::VswhereBased(v) => Box::new(std::iter::once(VsInstance::Vswhere(v))),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct VswhereInstance {
+ map: HashMap<String, String>,
+}
+
+impl TryFrom<&Vec<u8>> for VswhereInstance {
+ type Error = &'static str;
+
+ fn try_from(output: &Vec<u8>) -> Result<Self, Self::Error> {
+ let map: HashMap<_, _> = output
+ .lines()
+ .map_while(Result::ok)
+ .filter_map(|s| {
+ let mut splitn = s.splitn(2, ": ");
+ Some((splitn.next()?.to_owned(), splitn.next()?.to_owned()))
+ })
+ .collect();
+
+ if !map.contains_key("installationName")
+ || !map.contains_key("installationPath")
+ || !map.contains_key("installationVersion")
+ {
+ return Err("required properties not found");
+ }
+
+ Ok(Self { map })
+ }
+}
+
+#[cfg(test)]
+mod tests_ {
+ use std::borrow::Cow;
+ use std::convert::TryFrom;
+ use std::path::PathBuf;
+
+ #[test]
+ fn it_parses_vswhere_output_correctly() {
+ let output = br"instanceId: 58104422
+installDate: 21/02/2021 21:50:33
+installationName: VisualStudio/16.9.2+31112.23
+installationPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools
+installationVersion: 16.9.31112.23
+productId: Microsoft.VisualStudio.Product.BuildTools
+productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat
+state: 4294967295
+isComplete: 1
+isLaunchable: 1
+isPrerelease: 0
+isRebootRequired: 0
+displayName: Visual Studio Build Tools 2019
+description: The Visual Studio Build Tools allows you to build native and managed MSBuild-based applications without requiring the Visual Studio IDE. There are options to install the Visual C++ compilers and libraries, MFC, ATL, and C++/CLI support.
+channelId: VisualStudio.16.Release
+channelUri: https://aka.ms/vs/16/release/channel
+enginePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service
+releaseNotes: https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-v16.9#16.9.2
+thirdPartyNotices: https://go.microsoft.com/fwlink/?LinkId=660909
+updateDate: 2021-03-17T21:16:46.5963702Z
+catalog_buildBranch: d16.9
+catalog_buildVersion: 16.9.31112.23
+catalog_id: VisualStudio/16.9.2+31112.23
+catalog_localBuild: build-lab
+catalog_manifestName: VisualStudio
+catalog_manifestType: installer
+catalog_productDisplayVersion: 16.9.2
+catalog_productLine: Dev16
+catalog_productLineVersion: 2019
+catalog_productMilestone: RTW
+catalog_productMilestoneIsPreRelease: False
+catalog_productName: Visual Studio
+catalog_productPatchVersion: 2
+catalog_productPreReleaseMilestoneSuffix: 1.0
+catalog_productSemanticVersion: 16.9.2+31112.23
+catalog_requiredEngineVersion: 2.9.3365.38425
+properties_campaignId: 156063665.1613940062
+properties_channelManifestId: VisualStudio.16.Release/16.9.2+31112.23
+properties_nickname:
+properties_setupEngineFilePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installershell.exe
+"
+ .to_vec();
+
+ let vswhere_instance = super::VswhereInstance::try_from(&output);
+ assert!(vswhere_instance.is_ok());
+
+ let vs_instance = super::VsInstance::Vswhere(vswhere_instance.unwrap());
+ assert_eq!(
+ vs_instance.installation_name(),
+ Some(Cow::from("VisualStudio/16.9.2+31112.23"))
+ );
+ assert_eq!(
+ vs_instance.installation_path(),
+ Some(PathBuf::from(
+ r"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools"
+ ))
+ );
+ assert_eq!(
+ vs_instance.installation_version(),
+ Some(Cow::from("16.9.31112.23"))
+ );
+ }
+
+ #[test]
+ fn it_returns_an_error_for_empty_output() {
+ let output = b"".to_vec();
+
+ let vswhere_instance = super::VswhereInstance::try_from(&output);
+
+ assert!(vswhere_instance.is_err());
+ }
+
+ #[test]
+ fn it_returns_an_error_for_output_consisting_of_empty_lines() {
+ let output = br"
+
+"
+ .to_vec();
+
+ let vswhere_instance = super::VswhereInstance::try_from(&output);
+
+ assert!(vswhere_instance.is_err());
+ }
+
+ #[test]
+ fn it_returns_an_error_for_output_without_required_properties() {
+ let output = br"instanceId: 58104422
+installDate: 21/02/2021 21:50:33
+productId: Microsoft.VisualStudio.Product.BuildTools
+productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat
+"
+ .to_vec();
+
+ let vswhere_instance = super::VswhereInstance::try_from(&output);
+
+ assert!(vswhere_instance.is_err());
+ }
+}
diff --git a/vendor/cc/src/windows/winapi.rs b/vendor/cc/src/windows/winapi.rs
new file mode 100644
index 00000000..09965daa
--- /dev/null
+++ b/vendor/cc/src/windows/winapi.rs
@@ -0,0 +1,146 @@
+// Copyright © 2015-2017 winapi-rs developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// All files in the project carrying such notice may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(bad_style, clippy::upper_case_acronyms)]
+
+use std::os::raw;
+
+pub type wchar_t = u16;
+
+pub use crate::windows::windows_sys::{FILETIME, GUID, HRESULT, SAFEARRAY};
+
+pub type REFIID = *const IID;
+pub type IID = GUID;
+pub type ULONG = raw::c_ulong;
+pub type DWORD = u32;
+pub type LPFILETIME = *mut FILETIME;
+pub type OLECHAR = WCHAR;
+pub type WCHAR = wchar_t;
+pub type LPCOLESTR = *const OLECHAR;
+pub type LCID = DWORD;
+pub type LPCWSTR = *const WCHAR;
+pub type PULONGLONG = *mut ULONGLONG;
+pub type ULONGLONG = u64;
+
+pub trait Interface {
+ fn uuidof() -> GUID;
+}
+
+pub type LPSAFEARRAY = *mut SAFEARRAY;
+
+macro_rules! DEFINE_GUID {
+ (
+ $name:ident, $l:expr, $w1:expr, $w2:expr,
+ $b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
+ ) => {
+ pub const $name: $crate::windows::winapi::GUID = $crate::windows::winapi::GUID {
+ data1: $l,
+ data2: $w1,
+ data3: $w2,
+ data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
+ };
+ };
+}
+
+macro_rules! RIDL {
+ (#[uuid($($uuid:expr),+)]
+ interface $interface:ident ($vtbl:ident) {$(
+ fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
+ )+}) => (
+ #[repr(C)]
+ pub struct $vtbl {
+ $(pub $method: unsafe extern "system" fn(
+ This: *mut $interface,
+ $($p: $t),*
+ ) -> $rtr,)+
+ }
+ #[repr(C)]
+ pub struct $interface {
+ pub lpVtbl: *const $vtbl,
+ }
+ RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
+ RIDL!{@uuid $interface $($uuid),+}
+ );
+ (#[uuid($($uuid:expr),+)]
+ interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {
+ }) => (
+ #[repr(C)]
+ pub struct $vtbl {
+ pub parent: $pvtbl,
+ }
+ #[repr(C)]
+ pub struct $interface {
+ pub lpVtbl: *const $vtbl,
+ }
+ RIDL!{@deref $interface $pinterface}
+ RIDL!{@uuid $interface $($uuid),+}
+ );
+ (#[uuid($($uuid:expr),+)]
+ interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {$(
+ fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
+ )+}) => (
+ #[repr(C)]
+ pub struct $vtbl {
+ pub parent: $pvtbl,
+ $(pub $method: unsafe extern "system" fn(
+ This: *mut $interface,
+ $($p: $t,)*
+ ) -> $rtr,)+
+ }
+ #[repr(C)]
+ pub struct $interface {
+ pub lpVtbl: *const $vtbl,
+ }
+ RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
+ RIDL!{@deref $interface $pinterface}
+ RIDL!{@uuid $interface $($uuid),+}
+ );
+ (@deref $interface:ident $pinterface:ident) => (
+ impl ::std::ops::Deref for $interface {
+ type Target = $pinterface;
+ #[inline]
+ fn deref(&self) -> &$pinterface {
+ unsafe { &*(self as *const $interface as *const $pinterface) }
+ }
+ }
+ );
+ (@impl $interface:ident {$(
+ fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
+ )+}) => (
+ impl $interface {
+ $(#[inline] pub unsafe fn $method(&self, $($p: $t,)*) -> $rtr {
+ ((*self.lpVtbl).$method)(self as *const _ as *mut _, $($p,)*)
+ })+
+ }
+ );
+ (@uuid $interface:ident
+ $l:expr, $w1:expr, $w2:expr,
+ $b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
+ ) => (
+ impl $crate::windows::winapi::Interface for $interface {
+ #[inline]
+ fn uuidof() -> $crate::windows::winapi::GUID {
+ $crate::windows::winapi::GUID {
+ data1: $l,
+ data2: $w1,
+ data3: $w2,
+ data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
+ }
+ }
+ }
+ );
+}
+
+RIDL! {#[uuid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)]
+interface IUnknown(IUnknownVtbl) {
+ fn QueryInterface(
+ riid: REFIID,
+ ppvObject: *mut *mut raw::c_void,
+ ) -> HRESULT,
+ fn AddRef() -> ULONG,
+ fn Release() -> ULONG,
+}}
diff --git a/vendor/cc/src/windows/windows_link.rs b/vendor/cc/src/windows/windows_link.rs
new file mode 100644
index 00000000..d08affe0
--- /dev/null
+++ b/vendor/cc/src/windows/windows_link.rs
@@ -0,0 +1,19 @@
+//! Provides the `link!` macro used by the generated windows bindings.
+//!
+//! This is a simple wrapper around an `extern` block with a `#[link]` attribute.
+//! It's very roughly equivalent to the windows-targets crate.
+
+macro_rules! link_macro {
+ ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => (
+ // Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't
+ // have in this repo. So instead we always link kernel32.lib and add the rest of the import
+ // libraries below by using an empty extern block. This works because extern blocks are not
+ // connected to the library given in the #[link] attribute.
+ #[link(name = "kernel32")]
+ extern $abi {
+ $(#[link_name=$link_name])?
+ pub fn $($function)*;
+ }
+ )
+}
+pub(crate) use link_macro as link;
diff --git a/vendor/cc/src/windows/windows_sys.rs b/vendor/cc/src/windows/windows_sys.rs
new file mode 100644
index 00000000..c871727d
--- /dev/null
+++ b/vendor/cc/src/windows/windows_sys.rs
@@ -0,0 +1,139 @@
+// This file is autogenerated.
+//
+// To add bindings, edit windows_sys.lst then run:
+//
+// ```
+// cd generate-windows-sys/
+// cargo run
+// ```
+// Bindings generated by `windows-bindgen` 0.62.1
+
+#![allow(
+ non_snake_case,
+ non_upper_case_globals,
+ non_camel_case_types,
+ dead_code,
+ clippy::all
+)]
+
+windows_link::link!("ole32.dll" "system" fn CoCreateInstance(rclsid : *const GUID, punkouter : * mut core::ffi::c_void, dwclscontext : CLSCTX, riid : *const GUID, ppv : *mut *mut core::ffi::c_void) -> HRESULT);
+windows_link::link!("ole32.dll" "system" fn CoInitializeEx(pvreserved : *const core::ffi::c_void, dwcoinit : u32) -> HRESULT);
+windows_link::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetMachineTypeAttributes(machine : u16, machinetypeattributes : *mut MACHINE_ATTRIBUTES) -> HRESULT);
+windows_link::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
+windows_link::link!("kernel32.dll" "system" fn LoadLibraryA(lplibfilename : PCSTR) -> HMODULE);
+windows_link::link!("kernel32.dll" "system" fn OpenSemaphoreA(dwdesiredaccess : u32, binherithandle : BOOL, lpname : PCSTR) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn PeekNamedPipe(hnamedpipe : HANDLE, lpbuffer : *mut core::ffi::c_void, nbuffersize : u32, lpbytesread : *mut u32, lptotalbytesavail : *mut u32, lpbytesleftthismessage : *mut u32) -> BOOL);
+windows_link::link!("advapi32.dll" "system" fn RegCloseKey(hkey : HKEY) -> WIN32_ERROR);
+windows_link::link!("advapi32.dll" "system" fn RegEnumKeyExW(hkey : HKEY, dwindex : u32, lpname : PWSTR, lpcchname : *mut u32, lpreserved : *const u32, lpclass : PWSTR, lpcchclass : *mut u32, lpftlastwritetime : *mut FILETIME) -> WIN32_ERROR);
+windows_link::link!("advapi32.dll" "system" fn RegOpenKeyExW(hkey : HKEY, lpsubkey : PCWSTR, uloptions : u32, samdesired : REG_SAM_FLAGS, phkresult : *mut HKEY) -> WIN32_ERROR);
+windows_link::link!("advapi32.dll" "system" fn RegQueryValueExW(hkey : HKEY, lpvaluename : PCWSTR, lpreserved : *const u32, lptype : *mut REG_VALUE_TYPE, lpdata : *mut u8, lpcbdata : *mut u32) -> WIN32_ERROR);
+windows_link::link!("kernel32.dll" "system" fn ReleaseSemaphore(hsemaphore : HANDLE, lreleasecount : i32, lppreviouscount : *mut i32) -> BOOL);
+windows_link::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR));
+windows_link::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32);
+windows_link::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
+pub type ADVANCED_FEATURE_FLAGS = u16;
+pub type BOOL = i32;
+pub type BSTR = *const u16;
+pub type CLSCTX = u32;
+pub const CLSCTX_ALL: CLSCTX = 23u32;
+pub type COINIT = i32;
+pub const COINIT_MULTITHREADED: COINIT = 0i32;
+pub const ERROR_NO_MORE_ITEMS: WIN32_ERROR = 259u32;
+pub const ERROR_SUCCESS: WIN32_ERROR = 0u32;
+pub const FALSE: BOOL = 0i32;
+pub type FARPROC = Option<unsafe extern "system" fn() -> isize>;
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+pub struct FILETIME {
+ pub dwLowDateTime: u32,
+ pub dwHighDateTime: u32,
+}
+pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32;
+pub type FILE_FLAGS_AND_ATTRIBUTES = u32;
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct GUID {
+ pub data1: u32,
+ pub data2: u16,
+ pub data3: u16,
+ pub data4: [u8; 8],
+}
+impl GUID {
+ pub const fn from_u128(uuid: u128) -> Self {
+ Self {
+ data1: (uuid >> 96) as u32,
+ data2: (uuid >> 80 & 0xffff) as u16,
+ data3: (uuid >> 64 & 0xffff) as u16,
+ data4: (uuid as u64).to_be_bytes(),
+ }
+ }
+}
+pub type HANDLE = *mut core::ffi::c_void;
+pub type HINSTANCE = *mut core::ffi::c_void;
+pub type HKEY = *mut core::ffi::c_void;
+pub const HKEY_LOCAL_MACHINE: HKEY = -2147483646i32 as _;
+pub type HMODULE = *mut core::ffi::c_void;
+pub type HRESULT = i32;
+pub type IMAGE_FILE_MACHINE = u16;
+pub const IMAGE_FILE_MACHINE_AMD64: IMAGE_FILE_MACHINE = 34404u16;
+pub const IID_IUnknown: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046);
+#[repr(C)]
+pub struct IUnknown_Vtbl {
+ pub QueryInterface: unsafe extern "system" fn(
+ this: *mut core::ffi::c_void,
+ iid: *const GUID,
+ interface: *mut *mut core::ffi::c_void,
+ ) -> HRESULT,
+ pub AddRef: unsafe extern "system" fn(this: *mut core::ffi::c_void) -> u32,
+ pub Release: unsafe extern "system" fn(this: *mut core::ffi::c_void) -> u32,
+}
+pub const KEY_READ: REG_SAM_FLAGS = 131097u32;
+pub const KEY_WOW64_32KEY: REG_SAM_FLAGS = 512u32;
+pub type MACHINE_ATTRIBUTES = i32;
+pub type PCSTR = *const u8;
+pub type PCWSTR = *const u16;
+pub type PWSTR = *mut u16;
+pub type REG_SAM_FLAGS = u32;
+pub const REG_SZ: REG_VALUE_TYPE = 1u32;
+pub type REG_VALUE_TYPE = u32;
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct SAFEARRAY {
+ pub cDims: u16,
+ pub fFeatures: ADVANCED_FEATURE_FLAGS,
+ pub cbElements: u32,
+ pub cLocks: u32,
+ pub pvData: *mut core::ffi::c_void,
+ pub rgsabound: [SAFEARRAYBOUND; 1],
+}
+impl Default for SAFEARRAY {
+ fn default() -> Self {
+ unsafe { core::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+pub struct SAFEARRAYBOUND {
+ pub cElements: u32,
+ pub lLbound: i32,
+}
+pub const SEMAPHORE_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32;
+pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32;
+pub const S_FALSE: HRESULT = 0x1_u32 as _;
+pub const S_OK: HRESULT = 0x0_u32 as _;
+pub type THREAD_ACCESS_RIGHTS = u32;
+pub const THREAD_SYNCHRONIZE: THREAD_ACCESS_RIGHTS = 1048576u32;
+pub const UserEnabled: MACHINE_ATTRIBUTES = 1i32;
+pub const WAIT_ABANDONED: WAIT_EVENT = 128u32;
+pub type WAIT_EVENT = u32;
+pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32;
+pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32;
+pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32;
+pub type WIN32_ERROR = u32;
+
+#[link(name = "advapi32")]
+#[link(name = "ole32")]
+#[link(name = "oleaut32")]
+extern "C" {}
+use super::windows_link;