Path: blob/main/contrib/llvm-project/llvm/lib/WindowsDriver/MSVCPaths.cpp
35233 views
//===-- MSVCPaths.cpp - MSVC path-parsing helpers -------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/WindowsDriver/MSVCPaths.h"9#include "llvm/ADT/SmallString.h"10#include "llvm/ADT/SmallVector.h"11#include "llvm/ADT/StringExtras.h"12#include "llvm/ADT/StringRef.h"13#include "llvm/ADT/Twine.h"14#include "llvm/Support/Path.h"15#include "llvm/Support/Process.h"16#include "llvm/Support/Program.h"17#include "llvm/Support/VersionTuple.h"18#include "llvm/Support/VirtualFileSystem.h"19#include "llvm/TargetParser/Host.h"20#include "llvm/TargetParser/Triple.h"21#include <optional>22#include <string>2324#ifdef _WIN3225#include "llvm/Support/ConvertUTF.h"26#endif2728#ifdef _WIN3229#define WIN32_LEAN_AND_MEAN30#define NOGDI31#ifndef NOMINMAX32#define NOMINMAX33#endif34#include <windows.h>35#endif3637#ifdef _MSC_VER38// Don't support SetupApi on MinGW.39#define USE_MSVC_SETUP_API4041// Make sure this comes before MSVCSetupApi.h42#include <comdef.h>4344#include "llvm/Support/COM.h"45#ifdef __clang__46#pragma clang diagnostic push47#pragma clang diagnostic ignored "-Wnon-virtual-dtor"48#endif49#include "llvm/WindowsDriver/MSVCSetupApi.h"50#ifdef __clang__51#pragma clang diagnostic pop52#endif53_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration));54_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2));55_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper));56_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances));57_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance));58_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2));59#endif6061static std::string62getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS,63llvm::StringRef Directory) {64std::string Highest;65llvm::VersionTuple HighestTuple;6667std::error_code EC;68for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC),69DirEnd;70!EC && DirIt != DirEnd; DirIt.increment(EC)) {71auto Status = VFS.status(DirIt->path());72if (!Status || !Status->isDirectory())73continue;74llvm::StringRef CandidateName = llvm::sys::path::filename(DirIt->path());75llvm::VersionTuple Tuple;76if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error.77continue;78if (Tuple > HighestTuple) {79HighestTuple = Tuple;80Highest = CandidateName.str();81}82}8384return Highest;85}8687static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS,88const std::string &SDKPath,89std::string &SDKVersion) {90llvm::SmallString<128> IncludePath(SDKPath);91llvm::sys::path::append(IncludePath, "Include");92SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath);93return !SDKVersion.empty();94}9596static bool getWindowsSDKDirViaCommandLine(97llvm::vfs::FileSystem &VFS, std::optional<llvm::StringRef> WinSdkDir,98std::optional<llvm::StringRef> WinSdkVersion,99std::optional<llvm::StringRef> WinSysRoot, std::string &Path, int &Major,100std::string &Version) {101if (WinSdkDir || WinSysRoot) {102// Don't validate the input; trust the value supplied by the user.103// The motivation is to prevent unnecessary file and registry access.104llvm::VersionTuple SDKVersion;105if (WinSdkVersion)106SDKVersion.tryParse(*WinSdkVersion);107108if (WinSysRoot) {109llvm::SmallString<128> SDKPath(*WinSysRoot);110llvm::sys::path::append(SDKPath, "Windows Kits");111if (!SDKVersion.empty())112llvm::sys::path::append(SDKPath, llvm::Twine(SDKVersion.getMajor()));113else114llvm::sys::path::append(115SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath));116Path = std::string(SDKPath);117} else {118Path = WinSdkDir->str();119}120121if (!SDKVersion.empty()) {122Major = SDKVersion.getMajor();123Version = SDKVersion.getAsString();124} else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) {125Major = 10;126}127return true;128}129return false;130}131132#ifdef _WIN32133static bool readFullStringValue(HKEY hkey, const char *valueName,134std::string &value) {135std::wstring WideValueName;136if (!llvm::ConvertUTF8toWide(valueName, WideValueName))137return false;138139DWORD result = 0;140DWORD valueSize = 0;141DWORD type = 0;142// First just query for the required size.143result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, &type, NULL,144&valueSize);145if (result != ERROR_SUCCESS || type != REG_SZ || !valueSize)146return false;147std::vector<BYTE> buffer(valueSize);148result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0],149&valueSize);150if (result == ERROR_SUCCESS) {151std::wstring WideValue(reinterpret_cast<const wchar_t *>(buffer.data()),152valueSize / sizeof(wchar_t));153if (valueSize && WideValue.back() == L'\0') {154WideValue.pop_back();155}156// The destination buffer must be empty as an invariant of the conversion157// function; but this function is sometimes called in a loop that passes in158// the same buffer, however. Simply clear it out so we can overwrite it.159value.clear();160return llvm::convertWideToUTF8(WideValue, value);161}162return false;163}164#endif165166/// Read registry string.167/// This also supports a means to look for high-versioned keys by use168/// of a $VERSION placeholder in the key path.169/// $VERSION in the key path is a placeholder for the version number,170/// causing the highest value path to be searched for and used.171/// I.e. "SOFTWARE\\Microsoft\\VisualStudio\\$VERSION".172/// There can be additional characters in the component. Only the numeric173/// characters are compared. This function only searches HKLM.174static bool getSystemRegistryString(const char *keyPath, const char *valueName,175std::string &value, std::string *phValue) {176#ifndef _WIN32177return false;178#else179HKEY hRootKey = HKEY_LOCAL_MACHINE;180HKEY hKey = NULL;181long lResult;182bool returnValue = false;183184const char *placeHolder = strstr(keyPath, "$VERSION");185std::string bestName;186// If we have a $VERSION placeholder, do the highest-version search.187if (placeHolder) {188const char *keyEnd = placeHolder - 1;189const char *nextKey = placeHolder;190// Find end of previous key.191while ((keyEnd > keyPath) && (*keyEnd != '\\'))192keyEnd--;193// Find end of key containing $VERSION.194while (*nextKey && (*nextKey != '\\'))195nextKey++;196size_t partialKeyLength = keyEnd - keyPath;197char partialKey[256];198if (partialKeyLength >= sizeof(partialKey))199partialKeyLength = sizeof(partialKey) - 1;200strncpy(partialKey, keyPath, partialKeyLength);201partialKey[partialKeyLength] = '\0';202HKEY hTopKey = NULL;203lResult = RegOpenKeyExA(hRootKey, partialKey, 0, KEY_READ | KEY_WOW64_32KEY,204&hTopKey);205if (lResult == ERROR_SUCCESS) {206char keyName[256];207double bestValue = 0.0;208DWORD index, size = sizeof(keyName) - 1;209for (index = 0; RegEnumKeyExA(hTopKey, index, keyName, &size, NULL, NULL,210NULL, NULL) == ERROR_SUCCESS;211index++) {212const char *sp = keyName;213while (*sp && !llvm::isDigit(*sp))214sp++;215if (!*sp)216continue;217const char *ep = sp + 1;218while (*ep && (llvm::isDigit(*ep) || (*ep == '.')))219ep++;220char numBuf[32];221strncpy(numBuf, sp, sizeof(numBuf) - 1);222numBuf[sizeof(numBuf) - 1] = '\0';223double dvalue = strtod(numBuf, NULL);224if (dvalue > bestValue) {225// Test that InstallDir is indeed there before keeping this index.226// Open the chosen key path remainder.227bestName = keyName;228// Append rest of key.229bestName.append(nextKey);230lResult = RegOpenKeyExA(hTopKey, bestName.c_str(), 0,231KEY_READ | KEY_WOW64_32KEY, &hKey);232if (lResult == ERROR_SUCCESS) {233if (readFullStringValue(hKey, valueName, value)) {234bestValue = dvalue;235if (phValue)236*phValue = bestName;237returnValue = true;238}239RegCloseKey(hKey);240}241}242size = sizeof(keyName) - 1;243}244RegCloseKey(hTopKey);245}246} else {247lResult =248RegOpenKeyExA(hRootKey, keyPath, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);249if (lResult == ERROR_SUCCESS) {250if (readFullStringValue(hKey, valueName, value))251returnValue = true;252if (phValue)253phValue->clear();254RegCloseKey(hKey);255}256}257return returnValue;258#endif // _WIN32259}260261namespace llvm {262263const char *archToWindowsSDKArch(Triple::ArchType Arch) {264switch (Arch) {265case Triple::ArchType::x86:266return "x86";267case Triple::ArchType::x86_64:268return "x64";269case Triple::ArchType::arm:270case Triple::ArchType::thumb:271return "arm";272case Triple::ArchType::aarch64:273return "arm64";274default:275return "";276}277}278279const char *archToLegacyVCArch(Triple::ArchType Arch) {280switch (Arch) {281case Triple::ArchType::x86:282// x86 is default in legacy VC toolchains.283// e.g. x86 libs are directly in /lib as opposed to /lib/x86.284return "";285case Triple::ArchType::x86_64:286return "amd64";287case Triple::ArchType::arm:288case Triple::ArchType::thumb:289return "arm";290case Triple::ArchType::aarch64:291return "arm64";292default:293return "";294}295}296297const char *archToDevDivInternalArch(Triple::ArchType Arch) {298switch (Arch) {299case Triple::ArchType::x86:300return "i386";301case Triple::ArchType::x86_64:302return "amd64";303case Triple::ArchType::arm:304case Triple::ArchType::thumb:305return "arm";306case Triple::ArchType::aarch64:307return "arm64";308default:309return "";310}311}312313bool appendArchToWindowsSDKLibPath(int SDKMajor, SmallString<128> LibPath,314Triple::ArchType Arch, std::string &path) {315if (SDKMajor >= 8) {316sys::path::append(LibPath, archToWindowsSDKArch(Arch));317} else {318switch (Arch) {319// In Windows SDK 7.x, x86 libraries are directly in the Lib folder.320case Triple::x86:321break;322case Triple::x86_64:323sys::path::append(LibPath, "x64");324break;325case Triple::arm:326case Triple::thumb:327// It is not necessary to link against Windows SDK 7.x when targeting ARM.328return false;329default:330return false;331}332}333334path = std::string(LibPath);335return true;336}337338std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout,339const std::string &VCToolChainPath,340Triple::ArchType TargetArch,341StringRef SubdirParent) {342const char *SubdirName;343const char *IncludeName;344switch (VSLayout) {345case ToolsetLayout::OlderVS:346SubdirName = archToLegacyVCArch(TargetArch);347IncludeName = "include";348break;349case ToolsetLayout::VS2017OrNewer:350SubdirName = archToWindowsSDKArch(TargetArch);351IncludeName = "include";352break;353case ToolsetLayout::DevDivInternal:354SubdirName = archToDevDivInternalArch(TargetArch);355IncludeName = "inc";356break;357}358359SmallString<256> Path(VCToolChainPath);360if (!SubdirParent.empty())361sys::path::append(Path, SubdirParent);362363switch (Type) {364case SubDirectoryType::Bin:365if (VSLayout == ToolsetLayout::VS2017OrNewer) {366// MSVC ships with two linkers: a 32-bit x86 and 64-bit x86 linker.367// On x86, pick the linker that corresponds to the current process.368// On ARM64, pick the 32-bit x86 linker; the 64-bit one doesn't run369// on Windows 10.370//371// FIXME: Consider using IsWow64GuestMachineSupported to figure out372// if we can invoke the 64-bit linker. It's generally preferable373// because it won't run out of address-space.374const bool HostIsX64 =375Triple(sys::getProcessTriple()).getArch() == Triple::x86_64;376const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86";377sys::path::append(Path, "bin", HostName, SubdirName);378} else { // OlderVS or DevDivInternal379sys::path::append(Path, "bin", SubdirName);380}381break;382case SubDirectoryType::Include:383sys::path::append(Path, IncludeName);384break;385case SubDirectoryType::Lib:386sys::path::append(Path, "lib", SubdirName);387break;388}389return std::string(Path);390}391392bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath,393Triple::ArchType TargetArch, vfs::FileSystem &VFS) {394SmallString<128> TestPath(getSubDirectoryPath(395SubDirectoryType::Include, VSLayout, VCToolChainPath, TargetArch));396sys::path::append(TestPath, "stdlib.h");397return !VFS.exists(TestPath);398}399400bool getWindowsSDKDir(vfs::FileSystem &VFS, std::optional<StringRef> WinSdkDir,401std::optional<StringRef> WinSdkVersion,402std::optional<StringRef> WinSysRoot, std::string &Path,403int &Major, std::string &WindowsSDKIncludeVersion,404std::string &WindowsSDKLibVersion) {405// Trust /winsdkdir and /winsdkversion if present.406if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot,407Path, Major, WindowsSDKIncludeVersion)) {408WindowsSDKLibVersion = WindowsSDKIncludeVersion;409return true;410}411412// FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to413// registry.414415// Try the Windows registry.416std::string RegistrySDKVersion;417if (!getSystemRegistryString(418"SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION",419"InstallationFolder", Path, &RegistrySDKVersion))420return false;421if (Path.empty() || RegistrySDKVersion.empty())422return false;423424WindowsSDKIncludeVersion.clear();425WindowsSDKLibVersion.clear();426Major = 0;427std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major);428if (Major <= 7)429return true;430if (Major == 8) {431// Windows SDK 8.x installs libraries in a folder whose names depend on the432// version of the OS you're targeting. By default choose the newest, which433// usually corresponds to the version of the OS you've installed the SDK on.434const char *Tests[] = {"winv6.3", "win8", "win7"};435for (const char *Test : Tests) {436SmallString<128> TestPath(Path);437sys::path::append(TestPath, "Lib", Test);438if (VFS.exists(TestPath)) {439WindowsSDKLibVersion = Test;440break;441}442}443return !WindowsSDKLibVersion.empty();444}445if (Major == 10) {446if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion))447return false;448WindowsSDKLibVersion = WindowsSDKIncludeVersion;449return true;450}451// Unsupported SDK version452return false;453}454455bool getUniversalCRTSdkDir(vfs::FileSystem &VFS,456std::optional<StringRef> WinSdkDir,457std::optional<StringRef> WinSdkVersion,458std::optional<StringRef> WinSysRoot,459std::string &Path, std::string &UCRTVersion) {460// If /winsdkdir is passed, use it as location for the UCRT too.461// FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir?462int Major;463if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot,464Path, Major, UCRTVersion))465return true;466467// FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to468// registry.469470// vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry471// for the specific key "KitsRoot10". So do we.472if (!getSystemRegistryString(473"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10",474Path, nullptr))475return false;476477return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion);478}479480bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS,481std::optional<StringRef> VCToolsDir,482std::optional<StringRef> VCToolsVersion,483std::optional<StringRef> WinSysRoot,484std::string &Path, ToolsetLayout &VSLayout) {485// Don't validate the input; trust the value supplied by the user.486// The primary motivation is to prevent unnecessary file and registry access.487if (VCToolsDir || WinSysRoot) {488if (WinSysRoot) {489SmallString<128> ToolsPath(*WinSysRoot);490sys::path::append(ToolsPath, "VC", "Tools", "MSVC");491std::string ToolsVersion;492if (VCToolsVersion)493ToolsVersion = VCToolsVersion->str();494else495ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath);496sys::path::append(ToolsPath, ToolsVersion);497Path = std::string(ToolsPath);498} else {499Path = VCToolsDir->str();500}501VSLayout = ToolsetLayout::VS2017OrNewer;502return true;503}504return false;505}506507bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path,508ToolsetLayout &VSLayout) {509// These variables are typically set by vcvarsall.bat510// when launching a developer command prompt.511if (std::optional<std::string> VCToolsInstallDir =512sys::Process::GetEnv("VCToolsInstallDir")) {513// This is only set by newer Visual Studios, and it leads straight to514// the toolchain directory.515Path = std::move(*VCToolsInstallDir);516VSLayout = ToolsetLayout::VS2017OrNewer;517return true;518}519if (std::optional<std::string> VCInstallDir =520sys::Process::GetEnv("VCINSTALLDIR")) {521// If the previous variable isn't set but this one is, then we've found522// an older Visual Studio. This variable is set by newer Visual Studios too,523// so this check has to appear second.524// In older Visual Studios, the VC directory is the toolchain.525Path = std::move(*VCInstallDir);526VSLayout = ToolsetLayout::OlderVS;527return true;528}529530// We couldn't find any VC environment variables. Let's walk through PATH and531// see if it leads us to a VC toolchain bin directory. If it does, pick the532// first one that we find.533if (std::optional<std::string> PathEnv = sys::Process::GetEnv("PATH")) {534SmallVector<StringRef, 8> PathEntries;535StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator);536for (StringRef PathEntry : PathEntries) {537if (PathEntry.empty())538continue;539540SmallString<256> ExeTestPath;541542// If cl.exe doesn't exist, then this definitely isn't a VC toolchain.543ExeTestPath = PathEntry;544sys::path::append(ExeTestPath, "cl.exe");545if (!VFS.exists(ExeTestPath))546continue;547548// cl.exe existing isn't a conclusive test for a VC toolchain; clang also549// has a cl.exe. So let's check for link.exe too.550ExeTestPath = PathEntry;551sys::path::append(ExeTestPath, "link.exe");552if (!VFS.exists(ExeTestPath))553continue;554555// whatever/VC/bin --> old toolchain, VC dir is toolchain dir.556StringRef TestPath = PathEntry;557bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin");558if (!IsBin) {559// Strip any architecture subdir like "amd64".560TestPath = sys::path::parent_path(TestPath);561IsBin = sys::path::filename(TestPath).equals_insensitive("bin");562}563if (IsBin) {564StringRef ParentPath = sys::path::parent_path(TestPath);565StringRef ParentFilename = sys::path::filename(ParentPath);566if (ParentFilename.equals_insensitive("VC")) {567Path = std::string(ParentPath);568VSLayout = ToolsetLayout::OlderVS;569return true;570}571if (ParentFilename.equals_insensitive("x86ret") ||572ParentFilename.equals_insensitive("x86chk") ||573ParentFilename.equals_insensitive("amd64ret") ||574ParentFilename.equals_insensitive("amd64chk")) {575Path = std::string(ParentPath);576VSLayout = ToolsetLayout::DevDivInternal;577return true;578}579580} else {581// This could be a new (>=VS2017) toolchain. If it is, we should find582// path components with these prefixes when walking backwards through583// the path.584// Note: empty strings match anything.585StringRef ExpectedPrefixes[] = {"", "Host", "bin", "",586"MSVC", "Tools", "VC"};587588auto It = sys::path::rbegin(PathEntry);589auto End = sys::path::rend(PathEntry);590for (StringRef Prefix : ExpectedPrefixes) {591if (It == End)592goto NotAToolChain;593if (!It->starts_with_insensitive(Prefix))594goto NotAToolChain;595++It;596}597598// We've found a new toolchain!599// Back up 3 times (/bin/Host/arch) to get the root path.600StringRef ToolChainPath(PathEntry);601for (int i = 0; i < 3; ++i)602ToolChainPath = sys::path::parent_path(ToolChainPath);603604Path = std::string(ToolChainPath);605VSLayout = ToolsetLayout::VS2017OrNewer;606return true;607}608609NotAToolChain:610continue;611}612}613return false;614}615616bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS,617std::optional<StringRef> VCToolsVersion,618std::string &Path, ToolsetLayout &VSLayout) {619#if !defined(USE_MSVC_SETUP_API)620return false;621#else622// FIXME: This really should be done once in the top-level program's main623// function, as it may have already been initialized with a different624// threading model otherwise.625sys::InitializeCOMRAII COM(sys::COMThreadingMode::SingleThreaded);626HRESULT HR;627628// _com_ptr_t will throw a _com_error if a COM calls fail.629// The LLVM coding standards forbid exception handling, so we'll have to630// stop them from being thrown in the first place.631// The destructor will put the regular error handler back when we leave632// this scope.633struct SuppressCOMErrorsRAII {634static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {}635636SuppressCOMErrorsRAII() { _set_com_error_handler(handler); }637638~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); }639640} COMErrorSuppressor;641642ISetupConfigurationPtr Query;643HR = Query.CreateInstance(__uuidof(SetupConfiguration));644if (FAILED(HR))645return false;646647IEnumSetupInstancesPtr EnumInstances;648HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances);649if (FAILED(HR))650return false;651652ISetupInstancePtr Instance;653HR = EnumInstances->Next(1, &Instance, nullptr);654if (HR != S_OK)655return false;656657ISetupInstancePtr NewestInstance;658std::optional<uint64_t> NewestVersionNum;659do {660bstr_t VersionString;661uint64_t VersionNum;662HR = Instance->GetInstallationVersion(VersionString.GetAddress());663if (FAILED(HR))664continue;665HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum);666if (FAILED(HR))667continue;668if (!NewestVersionNum || (VersionNum > NewestVersionNum)) {669NewestInstance = Instance;670NewestVersionNum = VersionNum;671}672} while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK);673674if (!NewestInstance)675return false;676677bstr_t VCPathWide;678HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress());679if (FAILED(HR))680return false;681682std::string VCRootPath;683convertWideToUTF8(std::wstring(VCPathWide), VCRootPath);684685std::string ToolsVersion;686if (VCToolsVersion.has_value()) {687ToolsVersion = *VCToolsVersion;688} else {689SmallString<256> ToolsVersionFilePath(VCRootPath);690sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build",691"Microsoft.VCToolsVersion.default.txt");692693auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath);694if (!ToolsVersionFile)695return false;696697ToolsVersion = ToolsVersionFile->get()->getBuffer().rtrim();698}699700701SmallString<256> ToolchainPath(VCRootPath);702sys::path::append(ToolchainPath, "Tools", "MSVC", ToolsVersion);703auto Status = VFS.status(ToolchainPath);704if (!Status || !Status->isDirectory())705return false;706707Path = std::string(ToolchainPath.str());708VSLayout = ToolsetLayout::VS2017OrNewer;709return true;710#endif711}712713bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout) {714std::string VSInstallPath;715if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)",716"InstallDir", VSInstallPath, nullptr) ||717getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)",718"InstallDir", VSInstallPath, nullptr)) {719if (!VSInstallPath.empty()) {720auto pos = VSInstallPath.find(R"(\Common7\IDE)");721if (pos == std::string::npos)722return false;723SmallString<256> VCPath(StringRef(VSInstallPath.c_str(), pos));724sys::path::append(VCPath, "VC");725726Path = std::string(VCPath);727VSLayout = ToolsetLayout::OlderVS;728return true;729}730}731return false;732}733734} // namespace llvm735736737