diff options
Diffstat (limited to 'vendor/cc/src/windows')
| -rw-r--r-- | vendor/cc/src/windows/com.rs | 110 | ||||
| -rw-r--r-- | vendor/cc/src/windows/find_tools.rs | 1238 | ||||
| -rw-r--r-- | vendor/cc/src/windows/mod.rs | 22 | ||||
| -rw-r--r-- | vendor/cc/src/windows/registry.rs | 191 | ||||
| -rw-r--r-- | vendor/cc/src/windows/setup_config.rs | 283 | ||||
| -rw-r--r-- | vendor/cc/src/windows/vs_instances.rs | 199 | ||||
| -rw-r--r-- | vendor/cc/src/windows/winapi.rs | 146 | ||||
| -rw-r--r-- | vendor/cc/src/windows/windows_link.rs | 19 | ||||
| -rw-r--r-- | vendor/cc/src/windows/windows_sys.rs | 139 |
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; |
