|  | //===-- MSVCPaths.cpp - MSVC path-parsing helpers -------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/WindowsDriver/MSVCPaths.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/ADT/SmallVector.h" | 
|  | #include "llvm/ADT/StringExtras.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  | #include "llvm/ADT/Twine.h" | 
|  | #include "llvm/Support/Path.h" | 
|  | #include "llvm/Support/Process.h" | 
|  | #include "llvm/Support/Program.h" | 
|  | #include "llvm/Support/VersionTuple.h" | 
|  | #include "llvm/Support/VirtualFileSystem.h" | 
|  | #include "llvm/TargetParser/Host.h" | 
|  | #include "llvm/TargetParser/Triple.h" | 
|  | #include <optional> | 
|  | #include <string> | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | #include "llvm/Support/ConvertUTF.h" | 
|  | #endif | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | #define WIN32_LEAN_AND_MEAN | 
|  | #define NOGDI | 
|  | #ifndef NOMINMAX | 
|  | #define NOMINMAX | 
|  | #endif | 
|  | #include <windows.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef _MSC_VER | 
|  | // Don't support SetupApi on MinGW. | 
|  | #define USE_MSVC_SETUP_API | 
|  |  | 
|  | // Make sure this comes before MSVCSetupApi.h | 
|  | #include <comdef.h> | 
|  |  | 
|  | #include "llvm/Support/COM.h" | 
|  | #ifdef __clang__ | 
|  | #pragma clang diagnostic push | 
|  | #pragma clang diagnostic ignored "-Wnon-virtual-dtor" | 
|  | #endif | 
|  | #include "llvm/WindowsDriver/MSVCSetupApi.h" | 
|  | #ifdef __clang__ | 
|  | #pragma clang diagnostic pop | 
|  | #endif | 
|  | _COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); | 
|  | _COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); | 
|  | _COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); | 
|  | _COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); | 
|  | _COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); | 
|  | _COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); | 
|  | #endif | 
|  |  | 
|  | static std::string | 
|  | getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS, | 
|  | llvm::StringRef Directory) { | 
|  | std::string Highest; | 
|  | llvm::VersionTuple HighestTuple; | 
|  |  | 
|  | std::error_code EC; | 
|  | for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC), | 
|  | DirEnd; | 
|  | !EC && DirIt != DirEnd; DirIt.increment(EC)) { | 
|  | auto Status = VFS.status(DirIt->path()); | 
|  | if (!Status || !Status->isDirectory()) | 
|  | continue; | 
|  | llvm::StringRef CandidateName = llvm::sys::path::filename(DirIt->path()); | 
|  | llvm::VersionTuple Tuple; | 
|  | if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error. | 
|  | continue; | 
|  | if (Tuple > HighestTuple) { | 
|  | HighestTuple = Tuple; | 
|  | Highest = CandidateName.str(); | 
|  | } | 
|  | } | 
|  |  | 
|  | return Highest; | 
|  | } | 
|  |  | 
|  | static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS, | 
|  | const std::string &SDKPath, | 
|  | std::string &SDKVersion) { | 
|  | llvm::SmallString<128> IncludePath(SDKPath); | 
|  | llvm::sys::path::append(IncludePath, "Include"); | 
|  | SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath); | 
|  | return !SDKVersion.empty(); | 
|  | } | 
|  |  | 
|  | static bool getWindowsSDKDirViaCommandLine( | 
|  | llvm::vfs::FileSystem &VFS, std::optional<llvm::StringRef> WinSdkDir, | 
|  | std::optional<llvm::StringRef> WinSdkVersion, | 
|  | std::optional<llvm::StringRef> WinSysRoot, std::string &Path, int &Major, | 
|  | std::string &Version) { | 
|  | if (WinSdkDir || WinSysRoot) { | 
|  | // Don't validate the input; trust the value supplied by the user. | 
|  | // The motivation is to prevent unnecessary file and registry access. | 
|  | llvm::VersionTuple SDKVersion; | 
|  | if (WinSdkVersion) | 
|  | SDKVersion.tryParse(*WinSdkVersion); | 
|  |  | 
|  | if (WinSysRoot) { | 
|  | llvm::SmallString<128> SDKPath(*WinSysRoot); | 
|  | llvm::sys::path::append(SDKPath, "Windows Kits"); | 
|  | if (!SDKVersion.empty()) | 
|  | llvm::sys::path::append(SDKPath, llvm::Twine(SDKVersion.getMajor())); | 
|  | else | 
|  | llvm::sys::path::append( | 
|  | SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath)); | 
|  | Path = std::string(SDKPath); | 
|  | } else { | 
|  | Path = WinSdkDir->str(); | 
|  | } | 
|  |  | 
|  | if (!SDKVersion.empty()) { | 
|  | Major = SDKVersion.getMajor(); | 
|  | Version = SDKVersion.getAsString(); | 
|  | } else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) { | 
|  | Major = 10; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | static bool readFullStringValue(HKEY hkey, const char *valueName, | 
|  | std::string &value) { | 
|  | std::wstring WideValueName; | 
|  | if (!llvm::ConvertUTF8toWide(valueName, WideValueName)) | 
|  | return false; | 
|  |  | 
|  | DWORD result = 0; | 
|  | DWORD valueSize = 0; | 
|  | DWORD type = 0; | 
|  | // First just query for the required size. | 
|  | result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, &type, NULL, | 
|  | &valueSize); | 
|  | if (result != ERROR_SUCCESS || type != REG_SZ || !valueSize) | 
|  | return false; | 
|  | std::vector<BYTE> buffer(valueSize); | 
|  | result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0], | 
|  | &valueSize); | 
|  | if (result == ERROR_SUCCESS) { | 
|  | std::wstring WideValue(reinterpret_cast<const wchar_t *>(buffer.data()), | 
|  | valueSize / sizeof(wchar_t)); | 
|  | if (valueSize && WideValue.back() == L'\0') { | 
|  | WideValue.pop_back(); | 
|  | } | 
|  | // The destination buffer must be empty as an invariant of the conversion | 
|  | // function; but this function is sometimes called in a loop that passes in | 
|  | // the same buffer, however. Simply clear it out so we can overwrite it. | 
|  | value.clear(); | 
|  | return llvm::convertWideToUTF8(WideValue, value); | 
|  | } | 
|  | return false; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /// Read registry string. | 
|  | /// This also supports a means to look for high-versioned keys by use | 
|  | /// of a $VERSION placeholder in the key path. | 
|  | /// $VERSION in the key path is a placeholder for the version number, | 
|  | /// causing the highest value path to be searched for and used. | 
|  | /// I.e. "SOFTWARE\\Microsoft\\VisualStudio\\$VERSION". | 
|  | /// There can be additional characters in the component.  Only the numeric | 
|  | /// characters are compared.  This function only searches HKLM. | 
|  | static bool getSystemRegistryString(const char *keyPath, const char *valueName, | 
|  | std::string &value, std::string *phValue) { | 
|  | #ifndef _WIN32 | 
|  | return false; | 
|  | #else | 
|  | HKEY hRootKey = HKEY_LOCAL_MACHINE; | 
|  | HKEY hKey = NULL; | 
|  | long lResult; | 
|  | bool returnValue = false; | 
|  |  | 
|  | const char *placeHolder = strstr(keyPath, "$VERSION"); | 
|  | std::string bestName; | 
|  | // If we have a $VERSION placeholder, do the highest-version search. | 
|  | if (placeHolder) { | 
|  | const char *keyEnd = placeHolder - 1; | 
|  | const char *nextKey = placeHolder; | 
|  | // Find end of previous key. | 
|  | while ((keyEnd > keyPath) && (*keyEnd != '\\')) | 
|  | keyEnd--; | 
|  | // Find end of key containing $VERSION. | 
|  | while (*nextKey && (*nextKey != '\\')) | 
|  | nextKey++; | 
|  | size_t partialKeyLength = keyEnd - keyPath; | 
|  | char partialKey[256]; | 
|  | if (partialKeyLength >= sizeof(partialKey)) | 
|  | partialKeyLength = sizeof(partialKey) - 1; | 
|  | strncpy(partialKey, keyPath, partialKeyLength); | 
|  | partialKey[partialKeyLength] = '\0'; | 
|  | HKEY hTopKey = NULL; | 
|  | lResult = RegOpenKeyExA(hRootKey, partialKey, 0, KEY_READ | KEY_WOW64_32KEY, | 
|  | &hTopKey); | 
|  | if (lResult == ERROR_SUCCESS) { | 
|  | char keyName[256]; | 
|  | double bestValue = 0.0; | 
|  | DWORD index, size = sizeof(keyName) - 1; | 
|  | for (index = 0; RegEnumKeyExA(hTopKey, index, keyName, &size, NULL, NULL, | 
|  | NULL, NULL) == ERROR_SUCCESS; | 
|  | index++) { | 
|  | const char *sp = keyName; | 
|  | while (*sp && !llvm::isDigit(*sp)) | 
|  | sp++; | 
|  | if (!*sp) | 
|  | continue; | 
|  | const char *ep = sp + 1; | 
|  | while (*ep && (llvm::isDigit(*ep) || (*ep == '.'))) | 
|  | ep++; | 
|  | char numBuf[32]; | 
|  | strncpy(numBuf, sp, sizeof(numBuf) - 1); | 
|  | numBuf[sizeof(numBuf) - 1] = '\0'; | 
|  | double dvalue = strtod(numBuf, NULL); | 
|  | if (dvalue > bestValue) { | 
|  | // Test that InstallDir is indeed there before keeping this index. | 
|  | // Open the chosen key path remainder. | 
|  | bestName = keyName; | 
|  | // Append rest of key. | 
|  | bestName.append(nextKey); | 
|  | lResult = RegOpenKeyExA(hTopKey, bestName.c_str(), 0, | 
|  | KEY_READ | KEY_WOW64_32KEY, &hKey); | 
|  | if (lResult == ERROR_SUCCESS) { | 
|  | if (readFullStringValue(hKey, valueName, value)) { | 
|  | bestValue = dvalue; | 
|  | if (phValue) | 
|  | *phValue = bestName; | 
|  | returnValue = true; | 
|  | } | 
|  | RegCloseKey(hKey); | 
|  | } | 
|  | } | 
|  | size = sizeof(keyName) - 1; | 
|  | } | 
|  | RegCloseKey(hTopKey); | 
|  | } | 
|  | } else { | 
|  | lResult = | 
|  | RegOpenKeyExA(hRootKey, keyPath, 0, KEY_READ | KEY_WOW64_32KEY, &hKey); | 
|  | if (lResult == ERROR_SUCCESS) { | 
|  | if (readFullStringValue(hKey, valueName, value)) | 
|  | returnValue = true; | 
|  | if (phValue) | 
|  | phValue->clear(); | 
|  | RegCloseKey(hKey); | 
|  | } | 
|  | } | 
|  | return returnValue; | 
|  | #endif // _WIN32 | 
|  | } | 
|  |  | 
|  | namespace llvm { | 
|  |  | 
|  | const char *archToWindowsSDKArch(Triple::ArchType Arch) { | 
|  | switch (Arch) { | 
|  | case Triple::ArchType::x86: | 
|  | return "x86"; | 
|  | case Triple::ArchType::x86_64: | 
|  | return "x64"; | 
|  | case Triple::ArchType::arm: | 
|  | case Triple::ArchType::thumb: | 
|  | return "arm"; | 
|  | case Triple::ArchType::aarch64: | 
|  | return "arm64"; | 
|  | default: | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | const char *archToLegacyVCArch(Triple::ArchType Arch) { | 
|  | switch (Arch) { | 
|  | case Triple::ArchType::x86: | 
|  | // x86 is default in legacy VC toolchains. | 
|  | // e.g. x86 libs are directly in /lib as opposed to /lib/x86. | 
|  | return ""; | 
|  | case Triple::ArchType::x86_64: | 
|  | return "amd64"; | 
|  | case Triple::ArchType::arm: | 
|  | case Triple::ArchType::thumb: | 
|  | return "arm"; | 
|  | case Triple::ArchType::aarch64: | 
|  | return "arm64"; | 
|  | default: | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | const char *archToDevDivInternalArch(Triple::ArchType Arch) { | 
|  | switch (Arch) { | 
|  | case Triple::ArchType::x86: | 
|  | return "i386"; | 
|  | case Triple::ArchType::x86_64: | 
|  | return "amd64"; | 
|  | case Triple::ArchType::arm: | 
|  | case Triple::ArchType::thumb: | 
|  | return "arm"; | 
|  | case Triple::ArchType::aarch64: | 
|  | return "arm64"; | 
|  | default: | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool appendArchToWindowsSDKLibPath(int SDKMajor, SmallString<128> LibPath, | 
|  | Triple::ArchType Arch, std::string &path) { | 
|  | if (SDKMajor >= 8) { | 
|  | sys::path::append(LibPath, archToWindowsSDKArch(Arch)); | 
|  | } else { | 
|  | switch (Arch) { | 
|  | // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. | 
|  | case Triple::x86: | 
|  | break; | 
|  | case Triple::x86_64: | 
|  | sys::path::append(LibPath, "x64"); | 
|  | break; | 
|  | case Triple::arm: | 
|  | case Triple::thumb: | 
|  | // It is not necessary to link against Windows SDK 7.x when targeting ARM. | 
|  | return false; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | path = std::string(LibPath); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout, | 
|  | const std::string &VCToolChainPath, | 
|  | Triple::ArchType TargetArch, | 
|  | StringRef SubdirParent) { | 
|  | const char *SubdirName; | 
|  | const char *IncludeName; | 
|  | switch (VSLayout) { | 
|  | case ToolsetLayout::OlderVS: | 
|  | SubdirName = archToLegacyVCArch(TargetArch); | 
|  | IncludeName = "include"; | 
|  | break; | 
|  | case ToolsetLayout::VS2017OrNewer: | 
|  | SubdirName = archToWindowsSDKArch(TargetArch); | 
|  | IncludeName = "include"; | 
|  | break; | 
|  | case ToolsetLayout::DevDivInternal: | 
|  | SubdirName = archToDevDivInternalArch(TargetArch); | 
|  | IncludeName = "inc"; | 
|  | break; | 
|  | } | 
|  |  | 
|  | SmallString<256> Path(VCToolChainPath); | 
|  | if (!SubdirParent.empty()) | 
|  | sys::path::append(Path, SubdirParent); | 
|  |  | 
|  | switch (Type) { | 
|  | case SubDirectoryType::Bin: | 
|  | if (VSLayout == ToolsetLayout::VS2017OrNewer) { | 
|  | // MSVC ships with two linkers: a 32-bit x86 and 64-bit x86 linker. | 
|  | // On x86, pick the linker that corresponds to the current process. | 
|  | // On ARM64, pick the 32-bit x86 linker; the 64-bit one doesn't run | 
|  | // on Windows 10. | 
|  | // | 
|  | // FIXME: Consider using IsWow64GuestMachineSupported to figure out | 
|  | // if we can invoke the 64-bit linker. It's generally preferable | 
|  | // because it won't run out of address-space. | 
|  | const bool HostIsX64 = | 
|  | Triple(sys::getProcessTriple()).getArch() == Triple::x86_64; | 
|  | const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; | 
|  | sys::path::append(Path, "bin", HostName, SubdirName); | 
|  | } else { // OlderVS or DevDivInternal | 
|  | sys::path::append(Path, "bin", SubdirName); | 
|  | } | 
|  | break; | 
|  | case SubDirectoryType::Include: | 
|  | sys::path::append(Path, IncludeName); | 
|  | break; | 
|  | case SubDirectoryType::Lib: | 
|  | sys::path::append(Path, "lib", SubdirName); | 
|  | break; | 
|  | } | 
|  | return std::string(Path); | 
|  | } | 
|  |  | 
|  | bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath, | 
|  | Triple::ArchType TargetArch, vfs::FileSystem &VFS) { | 
|  | SmallString<128> TestPath(getSubDirectoryPath( | 
|  | SubDirectoryType::Include, VSLayout, VCToolChainPath, TargetArch)); | 
|  | sys::path::append(TestPath, "stdlib.h"); | 
|  | return !VFS.exists(TestPath); | 
|  | } | 
|  |  | 
|  | bool getWindowsSDKDir(vfs::FileSystem &VFS, std::optional<StringRef> WinSdkDir, | 
|  | std::optional<StringRef> WinSdkVersion, | 
|  | std::optional<StringRef> WinSysRoot, std::string &Path, | 
|  | int &Major, std::string &WindowsSDKIncludeVersion, | 
|  | std::string &WindowsSDKLibVersion) { | 
|  | // Trust /winsdkdir and /winsdkversion if present. | 
|  | if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, | 
|  | Path, Major, WindowsSDKIncludeVersion)) { | 
|  | WindowsSDKLibVersion = WindowsSDKIncludeVersion; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to | 
|  | // registry. | 
|  |  | 
|  | // Try the Windows registry. | 
|  | std::string RegistrySDKVersion; | 
|  | if (!getSystemRegistryString( | 
|  | "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", | 
|  | "InstallationFolder", Path, &RegistrySDKVersion)) | 
|  | return false; | 
|  | if (Path.empty() || RegistrySDKVersion.empty()) | 
|  | return false; | 
|  |  | 
|  | WindowsSDKIncludeVersion.clear(); | 
|  | WindowsSDKLibVersion.clear(); | 
|  | Major = 0; | 
|  | std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major); | 
|  | if (Major <= 7) | 
|  | return true; | 
|  | if (Major == 8) { | 
|  | // Windows SDK 8.x installs libraries in a folder whose names depend on the | 
|  | // version of the OS you're targeting.  By default choose the newest, which | 
|  | // usually corresponds to the version of the OS you've installed the SDK on. | 
|  | const char *Tests[] = {"winv6.3", "win8", "win7"}; | 
|  | for (const char *Test : Tests) { | 
|  | SmallString<128> TestPath(Path); | 
|  | sys::path::append(TestPath, "Lib", Test); | 
|  | if (VFS.exists(TestPath)) { | 
|  | WindowsSDKLibVersion = Test; | 
|  | break; | 
|  | } | 
|  | } | 
|  | return !WindowsSDKLibVersion.empty(); | 
|  | } | 
|  | if (Major == 10) { | 
|  | if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion)) | 
|  | return false; | 
|  | WindowsSDKLibVersion = WindowsSDKIncludeVersion; | 
|  | return true; | 
|  | } | 
|  | // Unsupported SDK version | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, | 
|  | std::optional<StringRef> WinSdkDir, | 
|  | std::optional<StringRef> WinSdkVersion, | 
|  | std::optional<StringRef> WinSysRoot, | 
|  | std::string &Path, std::string &UCRTVersion) { | 
|  | // If /winsdkdir is passed, use it as location for the UCRT too. | 
|  | // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir? | 
|  | int Major; | 
|  | if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, | 
|  | Path, Major, UCRTVersion)) | 
|  | return true; | 
|  |  | 
|  | // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to | 
|  | // registry. | 
|  |  | 
|  | // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry | 
|  | // for the specific key "KitsRoot10". So do we. | 
|  | if (!getSystemRegistryString( | 
|  | "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", | 
|  | Path, nullptr)) | 
|  | return false; | 
|  |  | 
|  | return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion); | 
|  | } | 
|  |  | 
|  | bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS, | 
|  | std::optional<StringRef> VCToolsDir, | 
|  | std::optional<StringRef> VCToolsVersion, | 
|  | std::optional<StringRef> WinSysRoot, | 
|  | std::string &Path, ToolsetLayout &VSLayout) { | 
|  | // Don't validate the input; trust the value supplied by the user. | 
|  | // The primary motivation is to prevent unnecessary file and registry access. | 
|  | if (VCToolsDir || WinSysRoot) { | 
|  | if (WinSysRoot) { | 
|  | SmallString<128> ToolsPath(*WinSysRoot); | 
|  | sys::path::append(ToolsPath, "VC", "Tools", "MSVC"); | 
|  | std::string ToolsVersion; | 
|  | if (VCToolsVersion) | 
|  | ToolsVersion = VCToolsVersion->str(); | 
|  | else | 
|  | ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath); | 
|  | sys::path::append(ToolsPath, ToolsVersion); | 
|  | Path = std::string(ToolsPath); | 
|  | } else { | 
|  | Path = VCToolsDir->str(); | 
|  | } | 
|  | VSLayout = ToolsetLayout::VS2017OrNewer; | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path, | 
|  | ToolsetLayout &VSLayout) { | 
|  | // These variables are typically set by vcvarsall.bat | 
|  | // when launching a developer command prompt. | 
|  | if (std::optional<std::string> VCToolsInstallDir = | 
|  | sys::Process::GetEnv("VCToolsInstallDir")) { | 
|  | // This is only set by newer Visual Studios, and it leads straight to | 
|  | // the toolchain directory. | 
|  | Path = std::move(*VCToolsInstallDir); | 
|  | VSLayout = ToolsetLayout::VS2017OrNewer; | 
|  | return true; | 
|  | } | 
|  | if (std::optional<std::string> VCInstallDir = | 
|  | sys::Process::GetEnv("VCINSTALLDIR")) { | 
|  | // If the previous variable isn't set but this one is, then we've found | 
|  | // an older Visual Studio. This variable is set by newer Visual Studios too, | 
|  | // so this check has to appear second. | 
|  | // In older Visual Studios, the VC directory is the toolchain. | 
|  | Path = std::move(*VCInstallDir); | 
|  | VSLayout = ToolsetLayout::OlderVS; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // We couldn't find any VC environment variables. Let's walk through PATH and | 
|  | // see if it leads us to a VC toolchain bin directory. If it does, pick the | 
|  | // first one that we find. | 
|  | if (std::optional<std::string> PathEnv = sys::Process::GetEnv("PATH")) { | 
|  | SmallVector<StringRef, 8> PathEntries; | 
|  | StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator); | 
|  | for (StringRef PathEntry : PathEntries) { | 
|  | if (PathEntry.empty()) | 
|  | continue; | 
|  |  | 
|  | SmallString<256> ExeTestPath; | 
|  |  | 
|  | // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. | 
|  | ExeTestPath = PathEntry; | 
|  | sys::path::append(ExeTestPath, "cl.exe"); | 
|  | if (!VFS.exists(ExeTestPath)) | 
|  | continue; | 
|  |  | 
|  | // cl.exe existing isn't a conclusive test for a VC toolchain; clang also | 
|  | // has a cl.exe. So let's check for link.exe too. | 
|  | ExeTestPath = PathEntry; | 
|  | sys::path::append(ExeTestPath, "link.exe"); | 
|  | if (!VFS.exists(ExeTestPath)) | 
|  | continue; | 
|  |  | 
|  | // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. | 
|  | StringRef TestPath = PathEntry; | 
|  | bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); | 
|  | if (!IsBin) { | 
|  | // Strip any architecture subdir like "amd64". | 
|  | TestPath = sys::path::parent_path(TestPath); | 
|  | IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); | 
|  | } | 
|  | if (IsBin) { | 
|  | StringRef ParentPath = sys::path::parent_path(TestPath); | 
|  | StringRef ParentFilename = sys::path::filename(ParentPath); | 
|  | if (ParentFilename.equals_insensitive("VC")) { | 
|  | Path = std::string(ParentPath); | 
|  | VSLayout = ToolsetLayout::OlderVS; | 
|  | return true; | 
|  | } | 
|  | if (ParentFilename.equals_insensitive("x86ret") || | 
|  | ParentFilename.equals_insensitive("x86chk") || | 
|  | ParentFilename.equals_insensitive("amd64ret") || | 
|  | ParentFilename.equals_insensitive("amd64chk")) { | 
|  | Path = std::string(ParentPath); | 
|  | VSLayout = ToolsetLayout::DevDivInternal; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | } else { | 
|  | // This could be a new (>=VS2017) toolchain. If it is, we should find | 
|  | // path components with these prefixes when walking backwards through | 
|  | // the path. | 
|  | // Note: empty strings match anything. | 
|  | StringRef ExpectedPrefixes[] = {"",     "Host",  "bin", "", | 
|  | "MSVC", "Tools", "VC"}; | 
|  |  | 
|  | auto It = sys::path::rbegin(PathEntry); | 
|  | auto End = sys::path::rend(PathEntry); | 
|  | for (StringRef Prefix : ExpectedPrefixes) { | 
|  | if (It == End) | 
|  | goto NotAToolChain; | 
|  | if (!It->starts_with_insensitive(Prefix)) | 
|  | goto NotAToolChain; | 
|  | ++It; | 
|  | } | 
|  |  | 
|  | // We've found a new toolchain! | 
|  | // Back up 3 times (/bin/Host/arch) to get the root path. | 
|  | StringRef ToolChainPath(PathEntry); | 
|  | for (int i = 0; i < 3; ++i) | 
|  | ToolChainPath = sys::path::parent_path(ToolChainPath); | 
|  |  | 
|  | Path = std::string(ToolChainPath); | 
|  | VSLayout = ToolsetLayout::VS2017OrNewer; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | NotAToolChain: | 
|  | continue; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, | 
|  | std::optional<StringRef> VCToolsVersion, | 
|  | std::string &Path, ToolsetLayout &VSLayout) { | 
|  | #if !defined(USE_MSVC_SETUP_API) | 
|  | return false; | 
|  | #else | 
|  | // FIXME: This really should be done once in the top-level program's main | 
|  | // function, as it may have already been initialized with a different | 
|  | // threading model otherwise. | 
|  | sys::InitializeCOMRAII COM(sys::COMThreadingMode::SingleThreaded); | 
|  | HRESULT HR; | 
|  |  | 
|  | // _com_ptr_t will throw a _com_error if a COM calls fail. | 
|  | // The LLVM coding standards forbid exception handling, so we'll have to | 
|  | // stop them from being thrown in the first place. | 
|  | // The destructor will put the regular error handler back when we leave | 
|  | // this scope. | 
|  | struct SuppressCOMErrorsRAII { | 
|  | static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {} | 
|  |  | 
|  | SuppressCOMErrorsRAII() { _set_com_error_handler(handler); } | 
|  |  | 
|  | ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); } | 
|  |  | 
|  | } COMErrorSuppressor; | 
|  |  | 
|  | ISetupConfigurationPtr Query; | 
|  | HR = Query.CreateInstance(__uuidof(SetupConfiguration)); | 
|  | if (FAILED(HR)) | 
|  | return false; | 
|  |  | 
|  | IEnumSetupInstancesPtr EnumInstances; | 
|  | HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances); | 
|  | if (FAILED(HR)) | 
|  | return false; | 
|  |  | 
|  | ISetupInstancePtr Instance; | 
|  | HR = EnumInstances->Next(1, &Instance, nullptr); | 
|  | if (HR != S_OK) | 
|  | return false; | 
|  |  | 
|  | ISetupInstancePtr NewestInstance; | 
|  | std::optional<uint64_t> NewestVersionNum; | 
|  | do { | 
|  | bstr_t VersionString; | 
|  | uint64_t VersionNum; | 
|  | HR = Instance->GetInstallationVersion(VersionString.GetAddress()); | 
|  | if (FAILED(HR)) | 
|  | continue; | 
|  | HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum); | 
|  | if (FAILED(HR)) | 
|  | continue; | 
|  | if (!NewestVersionNum || (VersionNum > NewestVersionNum)) { | 
|  | NewestInstance = Instance; | 
|  | NewestVersionNum = VersionNum; | 
|  | } | 
|  | } while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK); | 
|  |  | 
|  | if (!NewestInstance) | 
|  | return false; | 
|  |  | 
|  | bstr_t VCPathWide; | 
|  | HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress()); | 
|  | if (FAILED(HR)) | 
|  | return false; | 
|  |  | 
|  | std::string VCRootPath; | 
|  | convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); | 
|  |  | 
|  | std::string ToolsVersion; | 
|  | if (VCToolsVersion.has_value()) { | 
|  | ToolsVersion = *VCToolsVersion; | 
|  | } else { | 
|  | SmallString<256> ToolsVersionFilePath(VCRootPath); | 
|  | sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", | 
|  | "Microsoft.VCToolsVersion.default.txt"); | 
|  |  | 
|  | auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath); | 
|  | if (!ToolsVersionFile) | 
|  | return false; | 
|  |  | 
|  | ToolsVersion = ToolsVersionFile->get()->getBuffer().rtrim(); | 
|  | } | 
|  |  | 
|  |  | 
|  | SmallString<256> ToolchainPath(VCRootPath); | 
|  | sys::path::append(ToolchainPath, "Tools", "MSVC", ToolsVersion); | 
|  | auto Status = VFS.status(ToolchainPath); | 
|  | if (!Status || !Status->isDirectory()) | 
|  | return false; | 
|  |  | 
|  | Path = std::string(ToolchainPath.str()); | 
|  | VSLayout = ToolsetLayout::VS2017OrNewer; | 
|  | return true; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout) { | 
|  | std::string VSInstallPath; | 
|  | if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)", | 
|  | "InstallDir", VSInstallPath, nullptr) || | 
|  | getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)", | 
|  | "InstallDir", VSInstallPath, nullptr)) { | 
|  | if (!VSInstallPath.empty()) { | 
|  | auto pos = VSInstallPath.find(R"(\Common7\IDE)"); | 
|  | if (pos == std::string::npos) | 
|  | return false; | 
|  | SmallString<256> VCPath(StringRef(VSInstallPath.c_str(), pos)); | 
|  | sys::path::append(VCPath, "VC"); | 
|  |  | 
|  | Path = std::string(VCPath); | 
|  | VSLayout = ToolsetLayout::OlderVS; | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | } // namespace llvm |