find-msvc-tools-0.1.5/.cargo_vcs_info.json0000644000000001550000000000100140640ustar { "git": { "sha1": "15145d16f729182b003f5701b7c93f4685da952b" }, "path_in_vcs": "find-msvc-tools" }find-msvc-tools-0.1.5/CHANGELOG.md000064400000000000000000000030741046102023000144700ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.1.5](https://github.com/rust-lang/cc-rs/compare/find-msvc-tools-v0.1.4...find-msvc-tools-v0.1.5) - 2025-11-14 ### Other - Add Visual Studio 2026 support ([#1609](https://github.com/rust-lang/cc-rs/pull/1609)) ## [0.1.4](https://github.com/rust-lang/cc-rs/compare/find-msvc-tools-v0.1.3...find-msvc-tools-v0.1.4) - 2025-10-10 ### Other - Allow using VCToolsVersion to request a specific msvc version ([#1589](https://github.com/rust-lang/cc-rs/pull/1589)) - Regenerate windows sys bindings ([#1591](https://github.com/rust-lang/cc-rs/pull/1591)) ## [0.1.3](https://github.com/rust-lang/cc-rs/compare/find-msvc-tools-v0.1.2...find-msvc-tools-v0.1.3) - 2025-10-03 ### Other - Regenerate windows sys bindings ([#1572](https://github.com/rust-lang/cc-rs/pull/1572)) ## [0.1.2](https://github.com/rust-lang/cc-rs/compare/find-msvc-tools-v0.1.1...find-msvc-tools-v0.1.2) - 2025-09-19 ### Other - [win] Search the Windows SDK for tools as well ([#1553](https://github.com/rust-lang/cc-rs/pull/1553)) ## [0.1.1](https://github.com/rust-lang/cc-rs/compare/find-msvc-tools-v0.1.0...find-msvc-tools-v0.1.1) - 2025-09-05 ### Other - Regenerate windows sys bindings ([#1548](https://github.com/rust-lang/cc-rs/pull/1548)) - Add fn get_ucrt_dir for find-msvc-tools ([#1546](https://github.com/rust-lang/cc-rs/pull/1546)) find-msvc-tools-0.1.5/Cargo.lock0000644000000002370000000000100120400ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "find-msvc-tools" version = "0.1.5" find-msvc-tools-0.1.5/Cargo.toml0000644000000022270000000000100120640ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.63" name = "find-msvc-tools" version = "0.1.5" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Find windows-specific tools, read MSVC versions from the registry and from COM interfaces" documentation = "https://docs.rs/find-msvc-tools" readme = "README.md" keywords = ["build-dependencies"] categories = ["development-tools::build-utils"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cc-rs" [lib] name = "find_msvc_tools" path = "src/lib.rs" [dependencies] [lints.rust.unexpected_cfgs] level = "allow" priority = 0 check-cfg = ["cfg(disable_clang_cl_tests)"] find-msvc-tools-0.1.5/Cargo.toml.orig000064400000000000000000000010151046102023000155370ustar 00000000000000[package] name = "find-msvc-tools" version = "0.1.5" edition = "2018" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cc-rs" documentation = "https://docs.rs/find-msvc-tools" description = "Find windows-specific tools, read MSVC versions from the registry and from COM interfaces" keywords = ["build-dependencies"] categories = ["development-tools::build-utils"] rust-version = "1.63" [dependencies] [lints.rust] unexpected_cfgs = { level = "allow", check-cfg = ['cfg(disable_clang_cl_tests)'] } find-msvc-tools-0.1.5/LICENSE-APACHE000064400000000000000000000251371046102023000146070ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. find-msvc-tools-0.1.5/LICENSE-MIT000064400000000000000000000020411046102023000143040ustar 00000000000000Copyright (c) 2014 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. find-msvc-tools-0.1.5/README.md000064400000000000000000000017741046102023000141430ustar 00000000000000# find-msvc-tools > This crate is maintained by the library team, primarily for use by the `cc` crate and not intended for external use (except as a transitive dependency). This crate may make major changes to its APIs or be deprecated without warning. An internal use library for finding windows-specific tools, reading MSVC versions from the registry and from COM interfaces. Refer to the [documentation](https://docs.rs/find-msvc-tools) for detailed usage instructions. ## License This project is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution transitively intentionally submitted for inclusion in find-msvc-tools by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. find-msvc-tools-0.1.5/src/com.rs000064400000000000000000000054201046102023000145670ustar 00000000000000// Copyright © 2017 winapi-rs developers // Licensed under the Apache License, Version 2.0 // or the MIT license // , 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::{ 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(*mut T) where T: Interface; impl ComPtr 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 { 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(&self) -> Result, 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 Deref for ComPtr where T: Interface, { type Target = T; fn deref(&self) -> &T { unsafe { &*self.0 } } } impl Clone for ComPtr where T: Interface, { fn clone(&self) -> Self { unsafe { self.as_unknown().AddRef(); ComPtr::from_raw(self.0) } } } impl Drop for ComPtr 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) }; } } find-msvc-tools-0.1.5/src/find_tools.rs000064400000000000000000001645441046102023000161660ustar 00000000000000// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! An internal use crate 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; /// 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 { // 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", } } } #[derive(Debug, Clone)] #[non_exhaustive] pub enum Env { Owned(OsString), Arced(Arc), } impl AsRef 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 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 trait EnvGetter { fn get_env(&self, name: &'static str) -> Option; } struct StdEnvGetter; impl EnvGetter for StdEnvGetter { #[allow(clippy::disallowed_methods)] fn get_env(&self, name: &'static str) -> Option { 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. Supported tools include: /// - MSVC tools: `cl.exe`, `link.exe`, `lib.exe`, etc. /// - `MSBuild`: `msbuild.exe` /// - Visual Studio IDE: `devenv.exe` /// - Clang/LLVM tools: `clang.exe`, `clang++.exe`, `clang-*.exe`, `llvm-*.exe`, `lld.exe`, etc. /// /// 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. /// /// To find MSVC tools, this function will first attempt to detect if we are /// running in the context of a developer command prompt, and then use the tools /// as found in the current `PATH`. If that fails, it will attempt to locate /// the newest MSVC toolset in the newest installed version of Visual Studio. /// To limit the search to a specific version of the MSVC toolset, set the /// VCToolsVersion environment variable to the desired version (e.g. "14.44.35207"). /// /// 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 { 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 { 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_with_env(full_arch, tool, &StdEnvGetter) } pub fn find_tool_with_env(full_arch: &str, tool: &str, env_getter: &dyn EnvGetter) -> Option { // 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); } // Clang/LLVM isn't located in the same location as other tools like // cl.exe and lib.exe. if ["clang", "lldb", "llvm", "ld", "lld"] .iter() .any(|&t| tool.contains(t)) { return impl_::find_llvm_tool(tool, 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, /// Visual Studio 18 (2026) Vs18, } /// 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 { fn has_msbuild_version(version: &str) -> bool { impl_::has_msbuild_version(version, &StdEnvGetter) } match std::env::var("VisualStudioVersion") { Ok(version) => match &version[..] { "18.0" => Ok(VsVers::Vs18), "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: {vers}\n\ if another version is installed consider running \ the appropriate vcvars script before building this \ crate\n\ " )), }, _ => { // Check for the presence of a specific registry key // that indicates visual studio is installed. if has_msbuild_version("18.0") { Ok(VsVers::Vs18) } else 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()) } } } } /// 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 pub fn get_ucrt_dir() -> Option<(PathBuf, String)> { impl_::get_ucrt_dir() } /// Windows Implementation. #[cfg(windows)] mod impl_ { use crate::com; use crate::registry::{RegistryKey, LOCAL_MACHINE}; use crate::setup_config::SetupConfiguration; use crate::vs_instances::{VsInstances, VswhereInstance}; use crate::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}; use crate::Tool; struct MsvcTool { tool: PathBuf, libs: Vec, path: Vec, include: Vec, } #[derive(Default)] struct SdkInfo { libs: Vec, path: Vec, include: Vec, } struct LibraryHandle(HMODULE); impl LibraryHandle { fn new(name: &[u8]) -> Option { 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(&self, name: &[u8]) -> Option { 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 { // 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::(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 add_sdk(&mut self, sdk_info: SdkInfo) { self.libs.extend(sdk_info.libs); self.path.extend(sdk_info.path); self.include.extend(sdk_info.include); } fn into_tool(self, env_getter: &dyn EnvGetter) -> Tool { let MsvcTool { tool, libs, path, include, } = self; let mut tool = Tool { tool, is_clang_cl: false, env: Vec::new(), }; 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 } } impl SdkInfo { fn find_tool(&self, tool: &str) -> Option { self.path.iter().map(|p| p.join(tool)).find(|p| p.exists()) } } /// 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 { 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 { 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 { 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 { // 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 { tool: path, is_clang_cl: false, env: Vec::new(), }) } } fn find_msbuild_vs18(target: TargetArch, env_getter: &dyn EnvGetter) -> Option { find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "18", env_getter) } fn find_msbuild_vs17(target: TargetArch, env_getter: &dyn EnvGetter) -> Option { 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> { 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 { vs16plus_instances(target, version, env_getter) .filter_map(|path| { let path = path.join(tool); if !path.is_file() { return None; } let mut tool = Tool { tool: path, is_clang_cl: false, env: Vec::new(), }; 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 { find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16", env_getter) } pub(super) fn find_llvm_tool( tool: &str, target: TargetArch, env_getter: &dyn EnvGetter, ) -> Option { find_llvm_tool_vs17plus(tool, target, env_getter, "18") .or_else(|| find_llvm_tool_vs17plus(tool, target, env_getter, "17")) } fn find_llvm_tool_vs17plus( tool: &str, target: TargetArch, env_getter: &dyn EnvGetter, version: &'static str, ) -> Option { vs16plus_instances(target, version, env_getter) .filter_map(|mut base_path| { base_path.push(r"VC\Tools\LLVM"); let host_folder = match host_arch() { // The default LLVM bin folder is x86, and there's separate subfolders // for the x64 and ARM64 host tools. X86 => "", X86_64 => "x64", AARCH64 => "ARM64", _ => return None, }; if host_folder != "" { // E.g. C:\...\VC\Tools\LLVM\x64 base_path.push(host_folder); } // E.g. C:\...\VC\Tools\LLVM\x64\bin\clang.exe base_path.push("bin"); base_path.push(tool); let is_clang_cl = tool.contains("clang-cl"); base_path.is_file().then(|| Tool { tool: base_path, is_clang_cl, env: Vec::new(), }) }) .next() } // 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 { vs15plus_instances_using_com() .or_else(|| vs15plus_instances_using_vswhere(target, env_getter)) } fn vs15plus_instances_using_com() -> Option { 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 { 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<[u16; 4]> { let mut iter = version.split('.').map(u16::from_str).fuse(); let mut get_next_number = move || match iter.next() { Some(Ok(version_part)) => Some(version_part), Some(Err(_)) => None, None => Some(0), }; Some([ get_next_number()?, get_next_number()?, get_next_number()?, get_next_number()?, ]) } pub(super) fn find_msvc_15plus( tool: &str, target: TargetArch, env_getter: &dyn EnvGetter, ) -> Option { 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 { 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 { tool: path, is_clang_cl: false, env: Vec::new(), }; 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 { let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) = vs15plus_vc_paths(target, instance_path, env_getter)?; let sdk_info = get_sdks(target, env_getter)?; let mut tool_path = bin_path.join(tool); if !tool_path.exists() { tool_path = sdk_info.find_tool(tool)?; }; 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); } tool.add_sdk(sdk_info); 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)> { let version = vs15plus_vc_read_version(instance_path, env_getter)?; 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, env_getter: &dyn EnvGetter) -> Option { if let Some(version) = env_getter.get_env("VCToolsVersion") { // Restrict the search to a specific msvc version; if it doesn't exist then // our caller will fail to find the tool for this instance and move on. return version.to_str().map(ToString::to_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() { // If all else fails, manually search for bin directories. let tools_dir: PathBuf = dir.join(r"VC\Tools\MSVC"); return tools_dir .read_dir() .ok()? .filter_map(|file| { let file = file.ok()?; let name = file.file_name().into_string().ok()?; file.path().join("bin").exists().then(|| { let version = parse_version(&name); (name, version) }) }) .max_by(|(_, a), (_, b)| a.cmp(b)) .map(|(version, _)| version); } 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 { if env_getter.get_env("VCToolsVersion").is_some() { // VCToolsVersion is not set/supported for MSVC 14 return None; } let vcdir = get_vc_dir("14.0")?; let sdk_info = get_sdks(target, env_getter)?; let mut tool = get_tool(tool, &vcdir, target, &sdk_info)?; tool.add_sdk(sdk_info); Some(tool.into_tool(env_getter)) } fn get_sdks(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, }; let mut info = SdkInfo::default(); info.path .push(ucrt.join("bin").join(&ucrt_version).join(host)); let ucrt_include = ucrt.join("include").join(&ucrt_version); info.include.push(ucrt_include.join("ucrt")); let ucrt_lib = ucrt.join("lib").join(&ucrt_version); info.libs.push(ucrt_lib.join("ucrt").join(sub)); if let Some((sdk, version)) = get_sdk10_dir(env_getter) { info.path.push(sdk.join("bin").join(host)); let sdk_lib = sdk.join("lib").join(&version); info.libs.push(sdk_lib.join("um").join(sub)); let sdk_include = sdk.join("include").join(&version); info.include.push(sdk_include.join("um")); info.include.push(sdk_include.join("cppwinrt")); info.include.push(sdk_include.join("winrt")); info.include.push(sdk_include.join("shared")); } else if let Some(sdk) = get_sdk81_dir() { info.path.push(sdk.join("bin").join(host)); let sdk_lib = sdk.join("lib").join("winv6.3"); info.libs.push(sdk_lib.join("um").join(sub)); let sdk_include = sdk.join("include"); info.include.push(sdk_include.join("um")); info.include.push(sdk_include.join("winrt")); info.include.push(sdk_include.join("shared")); } Some(info) } fn add_env( tool: &mut Tool, env: &'static str, paths: Vec, 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, sdk_info: &SdkInfo, ) -> Option { bin_subdir(target) .into_iter() .map(|(sub, host)| { ( path.join("bin").join(sub).join(tool), Some(path.join("bin").join(host)), ) }) .filter(|(path, _)| path.is_file()) .chain(iter::once_with(|| Some((sdk_info.find_tool(tool)?, None))).flatten()) .map(|(tool_path, host)| { let mut tool = MsvcTool::new(tool_path); tool.path.extend(host); 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")); } 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 { 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 pub(super) 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::>(); 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 { 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 } } #[cfg(test)] mod tests { use super::*; use std::path::Path; // Import the find function from the module level use crate::find_tools::find; fn host_arch_to_string(host_arch_value: u16) -> &'static str { match host_arch_value { X86 => "x86", X86_64 => "x64", AARCH64 => "arm64", _ => panic!("Unsupported host architecture: {}", host_arch_value), } } #[test] fn test_find_cl_exe() { // Test that we can find cl.exe for common target architectures // and validate the correct host-target combination paths // This should pass on Windows CI with Visual Studio installed let target_architectures = ["x64", "x86", "arm64"]; let mut found_any = false; // Determine the host architecture let host_arch_value = host_arch(); let host_name = host_arch_to_string(host_arch_value); for &target_arch in &target_architectures { if let Some(cmd) = find(target_arch, "cl.exe") { // Verify the command looks valid assert!( !cmd.get_program().is_empty(), "cl.exe program path should not be empty" ); assert!( Path::new(cmd.get_program()).exists(), "cl.exe should exist at: {:?}", cmd.get_program() ); // Verify the path contains the correct host-target combination // Use case-insensitive comparison since VS IDE uses "Hostx64" while Build Tools use "HostX64" let path_str = cmd.get_program().to_string_lossy(); let path_str_lower = path_str.to_lowercase(); let expected_host_target_path = format!("\\bin\\host{host_name}\\{target_arch}"); let expected_host_target_path_unix = expected_host_target_path.replace("\\", "/"); assert!( path_str_lower.contains(&expected_host_target_path) || path_str_lower.contains(&expected_host_target_path_unix), "cl.exe path should contain host-target combination (case-insensitive) '{}' for {} host targeting {}, but found: {}", expected_host_target_path, host_name, target_arch, path_str ); found_any = true; } } assert!(found_any, "Expected to find cl.exe for at least one target architecture (x64, x86, or arm64) on Windows CI with Visual Studio installed"); } #[test] #[cfg(not(disable_clang_cl_tests))] fn test_find_llvm_tools() { // Import StdEnvGetter from the parent module use crate::find_tools::StdEnvGetter; // Test the actual find_llvm_tool function with various LLVM tools // This test assumes CI environment has Visual Studio + Clang installed // We test against x64 target since clang can cross-compile to any target let target_arch = TargetArch::new("x64").expect("Should support x64 architecture"); let llvm_tools = ["clang.exe", "clang++.exe", "lld.exe", "llvm-ar.exe"]; // Determine expected host-specific path based on host architecture let host_arch_value = host_arch(); let expected_host_path = match host_arch_value { X86 => "LLVM\\bin", // x86 host X86_64 => "LLVM\\x64\\bin", // x64 host AARCH64 => "LLVM\\ARM64\\bin", // arm64 host _ => panic!("Unsupported host architecture: {}", host_arch_value), }; let host_name = host_arch_to_string(host_arch_value); let mut found_tools_count = 0; for &tool in &llvm_tools { // Test finding LLVM tools using the standard environment getter let env_getter = StdEnvGetter; let result = find_llvm_tool(tool, target_arch, &env_getter); match result { Some(found_tool) => { found_tools_count += 1; // Verify the found tool has a valid, non-empty path assert!( !found_tool.path().as_os_str().is_empty(), "Found LLVM tool '{}' should have a non-empty path", tool ); // Verify the tool path actually exists on filesystem assert!( found_tool.path().exists(), "LLVM tool '{}' path should exist: {:?}", tool, found_tool.path() ); // Verify the tool path contains the expected tool name let path_str = found_tool.path().to_string_lossy(); assert!( path_str.contains(tool.trim_end_matches(".exe")), "Tool path '{}' should contain tool name '{}'", path_str, tool ); // Verify it's in the correct host-specific VS LLVM directory assert!( path_str.contains(expected_host_path) || path_str.contains(&expected_host_path.replace("\\", "/")), "LLVM tool should be in host-specific VS LLVM directory '{}' for {} host, but found: {}", expected_host_path, host_name, path_str ); } None => {} } } // On CI with VS + Clang installed, we should find at least some LLVM tools assert!( found_tools_count > 0, "Expected to find at least one LLVM tool on CI with Visual Studio + Clang installed for {} host. Found: {}", host_name, found_tools_count ); } } // 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 { "18.0" => { find_msbuild_vs18(TargetArch::X64, env_getter).is_some() || find_msbuild_vs18(TargetArch::X86, env_getter).is_some() || find_msbuild_vs18(TargetArch::Arm64, env_getter).is_some() } "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 { find_devenv_vs15(target, env_getter) } fn find_devenv_vs15(target: TargetArch, env_getter: &dyn EnvGetter) -> Option { 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 { // VS 15 (2017) changed how to locate msbuild if let Some(r) = find_msbuild_vs18(target, env_getter) { Some(r) } else 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 { find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target, env_getter) } fn find_old_msbuild(target: TargetArch) -> Option { 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 { tool: path, is_clang_cl: false, env: Vec::new(), }; 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, path::PathBuf}; use super::{EnvGetter, TargetArch}; 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 { 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 { None } // Finding Clang/LLVM-related tools on unix systems is not currently supported. #[inline(always)] pub(super) fn find_llvm_tool( _tool: &str, _target: TargetArch, _: &dyn EnvGetter, ) -> Option { 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 { // 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 { tool: path, is_clang_cl: false, env: Vec::new(), }) }; // 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 { 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 { None } #[inline(always)] pub(super) fn has_msbuild_version(_version: &str, _: &dyn EnvGetter) -> bool { false } #[inline(always)] pub(super) fn get_ucrt_dir() -> Option<(PathBuf, String)> { None } } find-msvc-tools-0.1.5/src/lib.rs000064400000000000000000000010151046102023000145530ustar 00000000000000//! 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)] mod find_tools; pub use find_tools::*; mod tool; pub use tool::*; #[cfg(windows)] #[doc(hidden)] pub mod windows_link; #[cfg(windows)] #[doc(hidden)] pub 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; find-msvc-tools-0.1.5/src/registry.rs000064400000000000000000000144441046102023000156670ustar 00000000000000// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::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, 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 { let key = key.encode_wide().chain(Some(0)).collect::>(); 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 { let name: &OsStr = name.as_ref(); let name = name.encode_wide().chain(Some(0)).collect::>(); 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; fn next(&mut self) -> Option> { 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))) } }) } } find-msvc-tools-0.1.5/src/setup_config.rs000064400000000000000000000212661046102023000165040ustar 00000000000000// Copyright © 2017 winapi-rs developers // Licensed under the Apache License, Version 2.0 // or the MIT license // , 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::{ 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); impl SetupConfiguration { pub fn new() -> Result { 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 { 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 { 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 { let mut obj = null_mut(); let this = self.0.cast::()?; let err = unsafe { this.EnumAllInstances(&mut obj) }; if err < 0 { return Err(err); } Ok(unsafe { EnumSetupInstances::from_raw(obj) }) } } pub struct SetupInstance(ComPtr); impl SetupInstance { pub unsafe fn from_raw(obj: *mut ISetupInstance) -> SetupInstance { SetupInstance(ComPtr::from_raw(obj)) } pub fn instance_id(&self) -> Result { 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 { 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 { 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 { 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 { let mut s = null(); let this = self.0.cast::()?; 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); impl EnumSetupInstances { pub unsafe fn from_raw(obj: *mut IEnumSetupInstances) -> EnumSetupInstances { EnumSetupInstances(ComPtr::from_raw(obj)) } } impl Iterator for EnumSetupInstances { type Item = Result; fn next(&mut self) -> Option> { 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) })) } } find-msvc-tools-0.1.5/src/tool.rs000064400000000000000000000021071046102023000147650ustar 00000000000000use std::{ ffi::OsString, path::{Path, PathBuf}, process::Command, }; /// `Tool` found by `find-msvc-tools` #[derive(Clone, Debug)] pub struct Tool { pub(crate) tool: PathBuf, pub(crate) is_clang_cl: bool, pub(crate) env: Vec<(OsString, OsString)>, } impl Tool { /// Converts this compiler into a `Command` that's ready to be run. /// /// This is useful for when the compiler needs to be executed and the /// command returned will already have the initial arguments and environment /// variables configured. pub fn to_command(&self) -> Command { let mut cmd = Command::new(&self.tool); for (k, v) in self.env.iter() { cmd.env(k, v); } cmd } /// Check is the tool clang-cl related pub fn is_clang_cl(&self) -> bool { self.is_clang_cl } /// Get path to the tool pub fn path(&self) -> &Path { &self.tool } /// Get environment variables for the tools pub fn env(&self) -> impl IntoIterator { &self.env } } find-msvc-tools-0.1.5/src/vs_instances.rs000064400000000000000000000145321046102023000165140ustar 00000000000000use std::borrow::Cow; use std::collections::HashMap; use std::convert::TryFrom; use std::io::BufRead; use std::path::PathBuf; use crate::setup_config::{EnumSetupInstances, SetupInstance}; pub enum VsInstance { Com(SetupInstance), Vswhere(VswhereInstance), } impl VsInstance { pub fn installation_name(&self) -> Option> { 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 { 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> { 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>; 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, } impl TryFrom<&Vec> for VswhereInstance { type Error = &'static str; fn try_from(output: &Vec) -> Result { 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()); } } find-msvc-tools-0.1.5/src/winapi.rs000064400000000000000000000107101046102023000152760ustar 00000000000000// Copyright © 2015-2017 winapi-rs developers // Licensed under the Apache License, Version 2.0 // or the MIT license // , 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_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::winapi::GUID = $crate::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::winapi::Interface for $interface { #[inline] fn uuidof() -> $crate::winapi::GUID { $crate::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, }} find-msvc-tools-0.1.5/src/windows_link.rs000064400000000000000000000016061046102023000165220ustar 00000000000000//! 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; find-msvc-tools-0.1.5/src/windows_sys.rs000064400000000000000000000136161046102023000164070ustar 00000000000000// This file is autogenerated. // // To add bindings, edit windows_sys.lst then run: // // ``` // cd dev-tools/generate-windows-sys/ // cargo run // ``` // Bindings generated by `windows-bindgen` 0.65.0 #![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 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;