| //===-- 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/StringRef.h" |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/Host.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 <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.str()); |
| } 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: |
| 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: |
| 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: |
| 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: |
| // It is not necessary to link against Windows SDK 7.x when targeting ARM. |
| return false; |
| default: |
| return false; |
| } |
| } |
| |
| path = std::string(LibPath.str()); |
| 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.str()); |
| } |
| |
| 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.str()); |
| } 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->startswith_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::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); |
| |
| SmallString<256> ToolsVersionFilePath(VCRootPath); |
| sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", |
| "Microsoft.VCToolsVersion.default.txt"); |
| |
| auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath); |
| if (!ToolsVersionFile) |
| return false; |
| |
| SmallString<256> ToolchainPath(VCRootPath); |
| sys::path::append(ToolchainPath, "Tools", "MSVC", |
| ToolsVersionFile->get()->getBuffer().rtrim()); |
| 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.str()); |
| VSLayout = ToolsetLayout::OlderVS; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace llvm |