181ad6265SDimitry Andric //===-- MSVCPaths.cpp - MSVC path-parsing helpers -------------------------===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric 981ad6265SDimitry Andric #include "llvm/WindowsDriver/MSVCPaths.h" 1081ad6265SDimitry Andric #include "llvm/ADT/SmallString.h" 1181ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h" 1281ad6265SDimitry Andric #include "llvm/ADT/StringRef.h" 1381ad6265SDimitry Andric #include "llvm/ADT/Triple.h" 1481ad6265SDimitry Andric #include "llvm/ADT/Twine.h" 1581ad6265SDimitry Andric #include "llvm/Support/Host.h" 1681ad6265SDimitry Andric #include "llvm/Support/Path.h" 1781ad6265SDimitry Andric #include "llvm/Support/Process.h" 1881ad6265SDimitry Andric #include "llvm/Support/Program.h" 1981ad6265SDimitry Andric #include "llvm/Support/VersionTuple.h" 2081ad6265SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 21*bdd1243dSDimitry Andric #include <optional> 2281ad6265SDimitry Andric #include <string> 2381ad6265SDimitry Andric 2481ad6265SDimitry Andric #ifdef _WIN32 2581ad6265SDimitry Andric #include "llvm/Support/ConvertUTF.h" 2681ad6265SDimitry Andric #endif 2781ad6265SDimitry Andric 2881ad6265SDimitry Andric #ifdef _WIN32 2981ad6265SDimitry Andric #define WIN32_LEAN_AND_MEAN 3081ad6265SDimitry Andric #define NOGDI 3181ad6265SDimitry Andric #ifndef NOMINMAX 3281ad6265SDimitry Andric #define NOMINMAX 3381ad6265SDimitry Andric #endif 3481ad6265SDimitry Andric #include <windows.h> 3581ad6265SDimitry Andric #endif 3681ad6265SDimitry Andric 3781ad6265SDimitry Andric #ifdef _MSC_VER 3881ad6265SDimitry Andric // Don't support SetupApi on MinGW. 3981ad6265SDimitry Andric #define USE_MSVC_SETUP_API 4081ad6265SDimitry Andric 4181ad6265SDimitry Andric // Make sure this comes before MSVCSetupApi.h 4281ad6265SDimitry Andric #include <comdef.h> 4381ad6265SDimitry Andric 4481ad6265SDimitry Andric #include "llvm/Support/COM.h" 4581ad6265SDimitry Andric #ifdef __clang__ 4681ad6265SDimitry Andric #pragma clang diagnostic push 4781ad6265SDimitry Andric #pragma clang diagnostic ignored "-Wnon-virtual-dtor" 4881ad6265SDimitry Andric #endif 4981ad6265SDimitry Andric #include "llvm/WindowsDriver/MSVCSetupApi.h" 5081ad6265SDimitry Andric #ifdef __clang__ 5181ad6265SDimitry Andric #pragma clang diagnostic pop 5281ad6265SDimitry Andric #endif 5381ad6265SDimitry Andric _COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); 5481ad6265SDimitry Andric _COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); 5581ad6265SDimitry Andric _COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); 5681ad6265SDimitry Andric _COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); 5781ad6265SDimitry Andric _COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); 5881ad6265SDimitry Andric _COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); 5981ad6265SDimitry Andric #endif 6081ad6265SDimitry Andric 6181ad6265SDimitry Andric static std::string 6281ad6265SDimitry Andric getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS, 6381ad6265SDimitry Andric llvm::StringRef Directory) { 6481ad6265SDimitry Andric std::string Highest; 6581ad6265SDimitry Andric llvm::VersionTuple HighestTuple; 6681ad6265SDimitry Andric 6781ad6265SDimitry Andric std::error_code EC; 6881ad6265SDimitry Andric for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC), 6981ad6265SDimitry Andric DirEnd; 7081ad6265SDimitry Andric !EC && DirIt != DirEnd; DirIt.increment(EC)) { 7181ad6265SDimitry Andric auto Status = VFS.status(DirIt->path()); 7281ad6265SDimitry Andric if (!Status || !Status->isDirectory()) 7381ad6265SDimitry Andric continue; 7481ad6265SDimitry Andric llvm::StringRef CandidateName = llvm::sys::path::filename(DirIt->path()); 7581ad6265SDimitry Andric llvm::VersionTuple Tuple; 7681ad6265SDimitry Andric if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error. 7781ad6265SDimitry Andric continue; 7881ad6265SDimitry Andric if (Tuple > HighestTuple) { 7981ad6265SDimitry Andric HighestTuple = Tuple; 8081ad6265SDimitry Andric Highest = CandidateName.str(); 8181ad6265SDimitry Andric } 8281ad6265SDimitry Andric } 8381ad6265SDimitry Andric 8481ad6265SDimitry Andric return Highest; 8581ad6265SDimitry Andric } 8681ad6265SDimitry Andric 8781ad6265SDimitry Andric static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS, 8881ad6265SDimitry Andric const std::string &SDKPath, 8981ad6265SDimitry Andric std::string &SDKVersion) { 9081ad6265SDimitry Andric llvm::SmallString<128> IncludePath(SDKPath); 9181ad6265SDimitry Andric llvm::sys::path::append(IncludePath, "Include"); 9281ad6265SDimitry Andric SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath); 9381ad6265SDimitry Andric return !SDKVersion.empty(); 9481ad6265SDimitry Andric } 9581ad6265SDimitry Andric 9681ad6265SDimitry Andric static bool getWindowsSDKDirViaCommandLine( 97*bdd1243dSDimitry Andric llvm::vfs::FileSystem &VFS, std::optional<llvm::StringRef> WinSdkDir, 98*bdd1243dSDimitry Andric std::optional<llvm::StringRef> WinSdkVersion, 99*bdd1243dSDimitry Andric std::optional<llvm::StringRef> WinSysRoot, std::string &Path, int &Major, 10081ad6265SDimitry Andric std::string &Version) { 10181ad6265SDimitry Andric if (WinSdkDir || WinSysRoot) { 10281ad6265SDimitry Andric // Don't validate the input; trust the value supplied by the user. 10381ad6265SDimitry Andric // The motivation is to prevent unnecessary file and registry access. 10481ad6265SDimitry Andric llvm::VersionTuple SDKVersion; 10581ad6265SDimitry Andric if (WinSdkVersion) 10681ad6265SDimitry Andric SDKVersion.tryParse(*WinSdkVersion); 10781ad6265SDimitry Andric 10881ad6265SDimitry Andric if (WinSysRoot) { 10981ad6265SDimitry Andric llvm::SmallString<128> SDKPath(*WinSysRoot); 11081ad6265SDimitry Andric llvm::sys::path::append(SDKPath, "Windows Kits"); 11181ad6265SDimitry Andric if (!SDKVersion.empty()) 11281ad6265SDimitry Andric llvm::sys::path::append(SDKPath, llvm::Twine(SDKVersion.getMajor())); 11381ad6265SDimitry Andric else 11481ad6265SDimitry Andric llvm::sys::path::append( 11581ad6265SDimitry Andric SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath)); 11681ad6265SDimitry Andric Path = std::string(SDKPath.str()); 11781ad6265SDimitry Andric } else { 11881ad6265SDimitry Andric Path = WinSdkDir->str(); 11981ad6265SDimitry Andric } 12081ad6265SDimitry Andric 12181ad6265SDimitry Andric if (!SDKVersion.empty()) { 12281ad6265SDimitry Andric Major = SDKVersion.getMajor(); 12381ad6265SDimitry Andric Version = SDKVersion.getAsString(); 12481ad6265SDimitry Andric } else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) { 12581ad6265SDimitry Andric Major = 10; 12681ad6265SDimitry Andric } 12781ad6265SDimitry Andric return true; 12881ad6265SDimitry Andric } 12981ad6265SDimitry Andric return false; 13081ad6265SDimitry Andric } 13181ad6265SDimitry Andric 13281ad6265SDimitry Andric #ifdef _WIN32 13381ad6265SDimitry Andric static bool readFullStringValue(HKEY hkey, const char *valueName, 13481ad6265SDimitry Andric std::string &value) { 13581ad6265SDimitry Andric std::wstring WideValueName; 13681ad6265SDimitry Andric if (!llvm::ConvertUTF8toWide(valueName, WideValueName)) 13781ad6265SDimitry Andric return false; 13881ad6265SDimitry Andric 13981ad6265SDimitry Andric DWORD result = 0; 14081ad6265SDimitry Andric DWORD valueSize = 0; 14181ad6265SDimitry Andric DWORD type = 0; 14281ad6265SDimitry Andric // First just query for the required size. 14381ad6265SDimitry Andric result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, &type, NULL, 14481ad6265SDimitry Andric &valueSize); 14581ad6265SDimitry Andric if (result != ERROR_SUCCESS || type != REG_SZ || !valueSize) 14681ad6265SDimitry Andric return false; 14781ad6265SDimitry Andric std::vector<BYTE> buffer(valueSize); 14881ad6265SDimitry Andric result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0], 14981ad6265SDimitry Andric &valueSize); 15081ad6265SDimitry Andric if (result == ERROR_SUCCESS) { 15181ad6265SDimitry Andric std::wstring WideValue(reinterpret_cast<const wchar_t *>(buffer.data()), 15281ad6265SDimitry Andric valueSize / sizeof(wchar_t)); 15381ad6265SDimitry Andric if (valueSize && WideValue.back() == L'\0') { 15481ad6265SDimitry Andric WideValue.pop_back(); 15581ad6265SDimitry Andric } 15681ad6265SDimitry Andric // The destination buffer must be empty as an invariant of the conversion 15781ad6265SDimitry Andric // function; but this function is sometimes called in a loop that passes in 15881ad6265SDimitry Andric // the same buffer, however. Simply clear it out so we can overwrite it. 15981ad6265SDimitry Andric value.clear(); 16081ad6265SDimitry Andric return llvm::convertWideToUTF8(WideValue, value); 16181ad6265SDimitry Andric } 16281ad6265SDimitry Andric return false; 16381ad6265SDimitry Andric } 16481ad6265SDimitry Andric #endif 16581ad6265SDimitry Andric 16681ad6265SDimitry Andric /// Read registry string. 16781ad6265SDimitry Andric /// This also supports a means to look for high-versioned keys by use 16881ad6265SDimitry Andric /// of a $VERSION placeholder in the key path. 16981ad6265SDimitry Andric /// $VERSION in the key path is a placeholder for the version number, 17081ad6265SDimitry Andric /// causing the highest value path to be searched for and used. 17181ad6265SDimitry Andric /// I.e. "SOFTWARE\\Microsoft\\VisualStudio\\$VERSION". 17281ad6265SDimitry Andric /// There can be additional characters in the component. Only the numeric 17381ad6265SDimitry Andric /// characters are compared. This function only searches HKLM. 17481ad6265SDimitry Andric static bool getSystemRegistryString(const char *keyPath, const char *valueName, 17581ad6265SDimitry Andric std::string &value, std::string *phValue) { 17681ad6265SDimitry Andric #ifndef _WIN32 17781ad6265SDimitry Andric return false; 17881ad6265SDimitry Andric #else 17981ad6265SDimitry Andric HKEY hRootKey = HKEY_LOCAL_MACHINE; 18081ad6265SDimitry Andric HKEY hKey = NULL; 18181ad6265SDimitry Andric long lResult; 18281ad6265SDimitry Andric bool returnValue = false; 18381ad6265SDimitry Andric 18481ad6265SDimitry Andric const char *placeHolder = strstr(keyPath, "$VERSION"); 18581ad6265SDimitry Andric std::string bestName; 18681ad6265SDimitry Andric // If we have a $VERSION placeholder, do the highest-version search. 18781ad6265SDimitry Andric if (placeHolder) { 18881ad6265SDimitry Andric const char *keyEnd = placeHolder - 1; 18981ad6265SDimitry Andric const char *nextKey = placeHolder; 19081ad6265SDimitry Andric // Find end of previous key. 19181ad6265SDimitry Andric while ((keyEnd > keyPath) && (*keyEnd != '\\')) 19281ad6265SDimitry Andric keyEnd--; 19381ad6265SDimitry Andric // Find end of key containing $VERSION. 19481ad6265SDimitry Andric while (*nextKey && (*nextKey != '\\')) 19581ad6265SDimitry Andric nextKey++; 19681ad6265SDimitry Andric size_t partialKeyLength = keyEnd - keyPath; 19781ad6265SDimitry Andric char partialKey[256]; 19881ad6265SDimitry Andric if (partialKeyLength >= sizeof(partialKey)) 19981ad6265SDimitry Andric partialKeyLength = sizeof(partialKey) - 1; 20081ad6265SDimitry Andric strncpy(partialKey, keyPath, partialKeyLength); 20181ad6265SDimitry Andric partialKey[partialKeyLength] = '\0'; 20281ad6265SDimitry Andric HKEY hTopKey = NULL; 20381ad6265SDimitry Andric lResult = RegOpenKeyExA(hRootKey, partialKey, 0, KEY_READ | KEY_WOW64_32KEY, 20481ad6265SDimitry Andric &hTopKey); 20581ad6265SDimitry Andric if (lResult == ERROR_SUCCESS) { 20681ad6265SDimitry Andric char keyName[256]; 20781ad6265SDimitry Andric double bestValue = 0.0; 20881ad6265SDimitry Andric DWORD index, size = sizeof(keyName) - 1; 20981ad6265SDimitry Andric for (index = 0; RegEnumKeyExA(hTopKey, index, keyName, &size, NULL, NULL, 21081ad6265SDimitry Andric NULL, NULL) == ERROR_SUCCESS; 21181ad6265SDimitry Andric index++) { 21281ad6265SDimitry Andric const char *sp = keyName; 21381ad6265SDimitry Andric while (*sp && !llvm::isDigit(*sp)) 21481ad6265SDimitry Andric sp++; 21581ad6265SDimitry Andric if (!*sp) 21681ad6265SDimitry Andric continue; 21781ad6265SDimitry Andric const char *ep = sp + 1; 21881ad6265SDimitry Andric while (*ep && (llvm::isDigit(*ep) || (*ep == '.'))) 21981ad6265SDimitry Andric ep++; 22081ad6265SDimitry Andric char numBuf[32]; 22181ad6265SDimitry Andric strncpy(numBuf, sp, sizeof(numBuf) - 1); 22281ad6265SDimitry Andric numBuf[sizeof(numBuf) - 1] = '\0'; 22381ad6265SDimitry Andric double dvalue = strtod(numBuf, NULL); 22481ad6265SDimitry Andric if (dvalue > bestValue) { 22581ad6265SDimitry Andric // Test that InstallDir is indeed there before keeping this index. 22681ad6265SDimitry Andric // Open the chosen key path remainder. 22781ad6265SDimitry Andric bestName = keyName; 22881ad6265SDimitry Andric // Append rest of key. 22981ad6265SDimitry Andric bestName.append(nextKey); 23081ad6265SDimitry Andric lResult = RegOpenKeyExA(hTopKey, bestName.c_str(), 0, 23181ad6265SDimitry Andric KEY_READ | KEY_WOW64_32KEY, &hKey); 23281ad6265SDimitry Andric if (lResult == ERROR_SUCCESS) { 23381ad6265SDimitry Andric if (readFullStringValue(hKey, valueName, value)) { 23481ad6265SDimitry Andric bestValue = dvalue; 23581ad6265SDimitry Andric if (phValue) 23681ad6265SDimitry Andric *phValue = bestName; 23781ad6265SDimitry Andric returnValue = true; 23881ad6265SDimitry Andric } 23981ad6265SDimitry Andric RegCloseKey(hKey); 24081ad6265SDimitry Andric } 24181ad6265SDimitry Andric } 24281ad6265SDimitry Andric size = sizeof(keyName) - 1; 24381ad6265SDimitry Andric } 24481ad6265SDimitry Andric RegCloseKey(hTopKey); 24581ad6265SDimitry Andric } 24681ad6265SDimitry Andric } else { 24781ad6265SDimitry Andric lResult = 24881ad6265SDimitry Andric RegOpenKeyExA(hRootKey, keyPath, 0, KEY_READ | KEY_WOW64_32KEY, &hKey); 24981ad6265SDimitry Andric if (lResult == ERROR_SUCCESS) { 25081ad6265SDimitry Andric if (readFullStringValue(hKey, valueName, value)) 25181ad6265SDimitry Andric returnValue = true; 25281ad6265SDimitry Andric if (phValue) 25381ad6265SDimitry Andric phValue->clear(); 25481ad6265SDimitry Andric RegCloseKey(hKey); 25581ad6265SDimitry Andric } 25681ad6265SDimitry Andric } 25781ad6265SDimitry Andric return returnValue; 25881ad6265SDimitry Andric #endif // _WIN32 25981ad6265SDimitry Andric } 26081ad6265SDimitry Andric 26181ad6265SDimitry Andric namespace llvm { 26281ad6265SDimitry Andric 26381ad6265SDimitry Andric const char *archToWindowsSDKArch(Triple::ArchType Arch) { 26481ad6265SDimitry Andric switch (Arch) { 26581ad6265SDimitry Andric case Triple::ArchType::x86: 26681ad6265SDimitry Andric return "x86"; 26781ad6265SDimitry Andric case Triple::ArchType::x86_64: 26881ad6265SDimitry Andric return "x64"; 26981ad6265SDimitry Andric case Triple::ArchType::arm: 27081ad6265SDimitry Andric return "arm"; 27181ad6265SDimitry Andric case Triple::ArchType::aarch64: 27281ad6265SDimitry Andric return "arm64"; 27381ad6265SDimitry Andric default: 27481ad6265SDimitry Andric return ""; 27581ad6265SDimitry Andric } 27681ad6265SDimitry Andric } 27781ad6265SDimitry Andric 27881ad6265SDimitry Andric const char *archToLegacyVCArch(Triple::ArchType Arch) { 27981ad6265SDimitry Andric switch (Arch) { 28081ad6265SDimitry Andric case Triple::ArchType::x86: 28181ad6265SDimitry Andric // x86 is default in legacy VC toolchains. 28281ad6265SDimitry Andric // e.g. x86 libs are directly in /lib as opposed to /lib/x86. 28381ad6265SDimitry Andric return ""; 28481ad6265SDimitry Andric case Triple::ArchType::x86_64: 28581ad6265SDimitry Andric return "amd64"; 28681ad6265SDimitry Andric case Triple::ArchType::arm: 28781ad6265SDimitry Andric return "arm"; 28881ad6265SDimitry Andric case Triple::ArchType::aarch64: 28981ad6265SDimitry Andric return "arm64"; 29081ad6265SDimitry Andric default: 29181ad6265SDimitry Andric return ""; 29281ad6265SDimitry Andric } 29381ad6265SDimitry Andric } 29481ad6265SDimitry Andric 29581ad6265SDimitry Andric const char *archToDevDivInternalArch(Triple::ArchType Arch) { 29681ad6265SDimitry Andric switch (Arch) { 29781ad6265SDimitry Andric case Triple::ArchType::x86: 29881ad6265SDimitry Andric return "i386"; 29981ad6265SDimitry Andric case Triple::ArchType::x86_64: 30081ad6265SDimitry Andric return "amd64"; 30181ad6265SDimitry Andric case Triple::ArchType::arm: 30281ad6265SDimitry Andric return "arm"; 30381ad6265SDimitry Andric case Triple::ArchType::aarch64: 30481ad6265SDimitry Andric return "arm64"; 30581ad6265SDimitry Andric default: 30681ad6265SDimitry Andric return ""; 30781ad6265SDimitry Andric } 30881ad6265SDimitry Andric } 30981ad6265SDimitry Andric 31081ad6265SDimitry Andric bool appendArchToWindowsSDKLibPath(int SDKMajor, SmallString<128> LibPath, 31181ad6265SDimitry Andric Triple::ArchType Arch, std::string &path) { 31281ad6265SDimitry Andric if (SDKMajor >= 8) { 31381ad6265SDimitry Andric sys::path::append(LibPath, archToWindowsSDKArch(Arch)); 31481ad6265SDimitry Andric } else { 31581ad6265SDimitry Andric switch (Arch) { 31681ad6265SDimitry Andric // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. 31781ad6265SDimitry Andric case Triple::x86: 31881ad6265SDimitry Andric break; 31981ad6265SDimitry Andric case Triple::x86_64: 32081ad6265SDimitry Andric sys::path::append(LibPath, "x64"); 32181ad6265SDimitry Andric break; 32281ad6265SDimitry Andric case Triple::arm: 32381ad6265SDimitry Andric // It is not necessary to link against Windows SDK 7.x when targeting ARM. 32481ad6265SDimitry Andric return false; 32581ad6265SDimitry Andric default: 32681ad6265SDimitry Andric return false; 32781ad6265SDimitry Andric } 32881ad6265SDimitry Andric } 32981ad6265SDimitry Andric 33081ad6265SDimitry Andric path = std::string(LibPath.str()); 33181ad6265SDimitry Andric return true; 33281ad6265SDimitry Andric } 33381ad6265SDimitry Andric 33481ad6265SDimitry Andric std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout, 33581ad6265SDimitry Andric const std::string &VCToolChainPath, 33681ad6265SDimitry Andric Triple::ArchType TargetArch, 33781ad6265SDimitry Andric StringRef SubdirParent) { 33881ad6265SDimitry Andric const char *SubdirName; 33981ad6265SDimitry Andric const char *IncludeName; 34081ad6265SDimitry Andric switch (VSLayout) { 34181ad6265SDimitry Andric case ToolsetLayout::OlderVS: 34281ad6265SDimitry Andric SubdirName = archToLegacyVCArch(TargetArch); 34381ad6265SDimitry Andric IncludeName = "include"; 34481ad6265SDimitry Andric break; 34581ad6265SDimitry Andric case ToolsetLayout::VS2017OrNewer: 34681ad6265SDimitry Andric SubdirName = archToWindowsSDKArch(TargetArch); 34781ad6265SDimitry Andric IncludeName = "include"; 34881ad6265SDimitry Andric break; 34981ad6265SDimitry Andric case ToolsetLayout::DevDivInternal: 35081ad6265SDimitry Andric SubdirName = archToDevDivInternalArch(TargetArch); 35181ad6265SDimitry Andric IncludeName = "inc"; 35281ad6265SDimitry Andric break; 35381ad6265SDimitry Andric } 35481ad6265SDimitry Andric 35581ad6265SDimitry Andric SmallString<256> Path(VCToolChainPath); 35681ad6265SDimitry Andric if (!SubdirParent.empty()) 35781ad6265SDimitry Andric sys::path::append(Path, SubdirParent); 35881ad6265SDimitry Andric 35981ad6265SDimitry Andric switch (Type) { 36081ad6265SDimitry Andric case SubDirectoryType::Bin: 36181ad6265SDimitry Andric if (VSLayout == ToolsetLayout::VS2017OrNewer) { 36281ad6265SDimitry Andric // MSVC ships with two linkers: a 32-bit x86 and 64-bit x86 linker. 36381ad6265SDimitry Andric // On x86, pick the linker that corresponds to the current process. 36481ad6265SDimitry Andric // On ARM64, pick the 32-bit x86 linker; the 64-bit one doesn't run 36581ad6265SDimitry Andric // on Windows 10. 36681ad6265SDimitry Andric // 36781ad6265SDimitry Andric // FIXME: Consider using IsWow64GuestMachineSupported to figure out 36881ad6265SDimitry Andric // if we can invoke the 64-bit linker. It's generally preferable 36981ad6265SDimitry Andric // because it won't run out of address-space. 37081ad6265SDimitry Andric const bool HostIsX64 = 37181ad6265SDimitry Andric Triple(sys::getProcessTriple()).getArch() == Triple::x86_64; 37281ad6265SDimitry Andric const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; 37381ad6265SDimitry Andric sys::path::append(Path, "bin", HostName, SubdirName); 37481ad6265SDimitry Andric } else { // OlderVS or DevDivInternal 37581ad6265SDimitry Andric sys::path::append(Path, "bin", SubdirName); 37681ad6265SDimitry Andric } 37781ad6265SDimitry Andric break; 37881ad6265SDimitry Andric case SubDirectoryType::Include: 37981ad6265SDimitry Andric sys::path::append(Path, IncludeName); 38081ad6265SDimitry Andric break; 38181ad6265SDimitry Andric case SubDirectoryType::Lib: 38281ad6265SDimitry Andric sys::path::append(Path, "lib", SubdirName); 38381ad6265SDimitry Andric break; 38481ad6265SDimitry Andric } 38581ad6265SDimitry Andric return std::string(Path.str()); 38681ad6265SDimitry Andric } 38781ad6265SDimitry Andric 38881ad6265SDimitry Andric bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath, 38981ad6265SDimitry Andric Triple::ArchType TargetArch, vfs::FileSystem &VFS) { 39081ad6265SDimitry Andric SmallString<128> TestPath(getSubDirectoryPath( 39181ad6265SDimitry Andric SubDirectoryType::Include, VSLayout, VCToolChainPath, TargetArch)); 39281ad6265SDimitry Andric sys::path::append(TestPath, "stdlib.h"); 39381ad6265SDimitry Andric return !VFS.exists(TestPath); 39481ad6265SDimitry Andric } 39581ad6265SDimitry Andric 396*bdd1243dSDimitry Andric bool getWindowsSDKDir(vfs::FileSystem &VFS, std::optional<StringRef> WinSdkDir, 397*bdd1243dSDimitry Andric std::optional<StringRef> WinSdkVersion, 398*bdd1243dSDimitry Andric std::optional<StringRef> WinSysRoot, std::string &Path, 39981ad6265SDimitry Andric int &Major, std::string &WindowsSDKIncludeVersion, 40081ad6265SDimitry Andric std::string &WindowsSDKLibVersion) { 40181ad6265SDimitry Andric // Trust /winsdkdir and /winsdkversion if present. 40281ad6265SDimitry Andric if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, 40381ad6265SDimitry Andric Path, Major, WindowsSDKIncludeVersion)) { 40481ad6265SDimitry Andric WindowsSDKLibVersion = WindowsSDKIncludeVersion; 40581ad6265SDimitry Andric return true; 40681ad6265SDimitry Andric } 40781ad6265SDimitry Andric 40881ad6265SDimitry Andric // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to 40981ad6265SDimitry Andric // registry. 41081ad6265SDimitry Andric 41181ad6265SDimitry Andric // Try the Windows registry. 41281ad6265SDimitry Andric std::string RegistrySDKVersion; 41381ad6265SDimitry Andric if (!getSystemRegistryString( 41481ad6265SDimitry Andric "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", 41581ad6265SDimitry Andric "InstallationFolder", Path, &RegistrySDKVersion)) 41681ad6265SDimitry Andric return false; 41781ad6265SDimitry Andric if (Path.empty() || RegistrySDKVersion.empty()) 41881ad6265SDimitry Andric return false; 41981ad6265SDimitry Andric 42081ad6265SDimitry Andric WindowsSDKIncludeVersion.clear(); 42181ad6265SDimitry Andric WindowsSDKLibVersion.clear(); 42281ad6265SDimitry Andric Major = 0; 42381ad6265SDimitry Andric std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major); 42481ad6265SDimitry Andric if (Major <= 7) 42581ad6265SDimitry Andric return true; 42681ad6265SDimitry Andric if (Major == 8) { 42781ad6265SDimitry Andric // Windows SDK 8.x installs libraries in a folder whose names depend on the 42881ad6265SDimitry Andric // version of the OS you're targeting. By default choose the newest, which 42981ad6265SDimitry Andric // usually corresponds to the version of the OS you've installed the SDK on. 43081ad6265SDimitry Andric const char *Tests[] = {"winv6.3", "win8", "win7"}; 43181ad6265SDimitry Andric for (const char *Test : Tests) { 43281ad6265SDimitry Andric SmallString<128> TestPath(Path); 43381ad6265SDimitry Andric sys::path::append(TestPath, "Lib", Test); 43481ad6265SDimitry Andric if (VFS.exists(TestPath)) { 43581ad6265SDimitry Andric WindowsSDKLibVersion = Test; 43681ad6265SDimitry Andric break; 43781ad6265SDimitry Andric } 43881ad6265SDimitry Andric } 43981ad6265SDimitry Andric return !WindowsSDKLibVersion.empty(); 44081ad6265SDimitry Andric } 44181ad6265SDimitry Andric if (Major == 10) { 44281ad6265SDimitry Andric if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion)) 44381ad6265SDimitry Andric return false; 44481ad6265SDimitry Andric WindowsSDKLibVersion = WindowsSDKIncludeVersion; 44581ad6265SDimitry Andric return true; 44681ad6265SDimitry Andric } 44781ad6265SDimitry Andric // Unsupported SDK version 44881ad6265SDimitry Andric return false; 44981ad6265SDimitry Andric } 45081ad6265SDimitry Andric 451*bdd1243dSDimitry Andric bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, 452*bdd1243dSDimitry Andric std::optional<StringRef> WinSdkDir, 453*bdd1243dSDimitry Andric std::optional<StringRef> WinSdkVersion, 454*bdd1243dSDimitry Andric std::optional<StringRef> WinSysRoot, 455*bdd1243dSDimitry Andric std::string &Path, std::string &UCRTVersion) { 45681ad6265SDimitry Andric // If /winsdkdir is passed, use it as location for the UCRT too. 45781ad6265SDimitry Andric // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir? 45881ad6265SDimitry Andric int Major; 45981ad6265SDimitry Andric if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, 46081ad6265SDimitry Andric Path, Major, UCRTVersion)) 46181ad6265SDimitry Andric return true; 46281ad6265SDimitry Andric 46381ad6265SDimitry Andric // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to 46481ad6265SDimitry Andric // registry. 46581ad6265SDimitry Andric 46681ad6265SDimitry Andric // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry 46781ad6265SDimitry Andric // for the specific key "KitsRoot10". So do we. 46881ad6265SDimitry Andric if (!getSystemRegistryString( 46981ad6265SDimitry Andric "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", 47081ad6265SDimitry Andric Path, nullptr)) 47181ad6265SDimitry Andric return false; 47281ad6265SDimitry Andric 47381ad6265SDimitry Andric return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion); 47481ad6265SDimitry Andric } 47581ad6265SDimitry Andric 47681ad6265SDimitry Andric bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS, 477*bdd1243dSDimitry Andric std::optional<StringRef> VCToolsDir, 478*bdd1243dSDimitry Andric std::optional<StringRef> VCToolsVersion, 479*bdd1243dSDimitry Andric std::optional<StringRef> WinSysRoot, 48081ad6265SDimitry Andric std::string &Path, ToolsetLayout &VSLayout) { 48181ad6265SDimitry Andric // Don't validate the input; trust the value supplied by the user. 48281ad6265SDimitry Andric // The primary motivation is to prevent unnecessary file and registry access. 48381ad6265SDimitry Andric if (VCToolsDir || WinSysRoot) { 48481ad6265SDimitry Andric if (WinSysRoot) { 48581ad6265SDimitry Andric SmallString<128> ToolsPath(*WinSysRoot); 48681ad6265SDimitry Andric sys::path::append(ToolsPath, "VC", "Tools", "MSVC"); 48781ad6265SDimitry Andric std::string ToolsVersion; 48881ad6265SDimitry Andric if (VCToolsVersion) 48981ad6265SDimitry Andric ToolsVersion = VCToolsVersion->str(); 49081ad6265SDimitry Andric else 49181ad6265SDimitry Andric ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath); 49281ad6265SDimitry Andric sys::path::append(ToolsPath, ToolsVersion); 49381ad6265SDimitry Andric Path = std::string(ToolsPath.str()); 49481ad6265SDimitry Andric } else { 49581ad6265SDimitry Andric Path = VCToolsDir->str(); 49681ad6265SDimitry Andric } 49781ad6265SDimitry Andric VSLayout = ToolsetLayout::VS2017OrNewer; 49881ad6265SDimitry Andric return true; 49981ad6265SDimitry Andric } 50081ad6265SDimitry Andric return false; 50181ad6265SDimitry Andric } 50281ad6265SDimitry Andric 50381ad6265SDimitry Andric bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path, 50481ad6265SDimitry Andric ToolsetLayout &VSLayout) { 50581ad6265SDimitry Andric // These variables are typically set by vcvarsall.bat 50681ad6265SDimitry Andric // when launching a developer command prompt. 507*bdd1243dSDimitry Andric if (std::optional<std::string> VCToolsInstallDir = 50881ad6265SDimitry Andric sys::Process::GetEnv("VCToolsInstallDir")) { 50981ad6265SDimitry Andric // This is only set by newer Visual Studios, and it leads straight to 51081ad6265SDimitry Andric // the toolchain directory. 51181ad6265SDimitry Andric Path = std::move(*VCToolsInstallDir); 51281ad6265SDimitry Andric VSLayout = ToolsetLayout::VS2017OrNewer; 51381ad6265SDimitry Andric return true; 51481ad6265SDimitry Andric } 515*bdd1243dSDimitry Andric if (std::optional<std::string> VCInstallDir = 51681ad6265SDimitry Andric sys::Process::GetEnv("VCINSTALLDIR")) { 51781ad6265SDimitry Andric // If the previous variable isn't set but this one is, then we've found 51881ad6265SDimitry Andric // an older Visual Studio. This variable is set by newer Visual Studios too, 51981ad6265SDimitry Andric // so this check has to appear second. 52081ad6265SDimitry Andric // In older Visual Studios, the VC directory is the toolchain. 52181ad6265SDimitry Andric Path = std::move(*VCInstallDir); 52281ad6265SDimitry Andric VSLayout = ToolsetLayout::OlderVS; 52381ad6265SDimitry Andric return true; 52481ad6265SDimitry Andric } 52581ad6265SDimitry Andric 52681ad6265SDimitry Andric // We couldn't find any VC environment variables. Let's walk through PATH and 52781ad6265SDimitry Andric // see if it leads us to a VC toolchain bin directory. If it does, pick the 52881ad6265SDimitry Andric // first one that we find. 529*bdd1243dSDimitry Andric if (std::optional<std::string> PathEnv = sys::Process::GetEnv("PATH")) { 53081ad6265SDimitry Andric SmallVector<StringRef, 8> PathEntries; 53181ad6265SDimitry Andric StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator); 53281ad6265SDimitry Andric for (StringRef PathEntry : PathEntries) { 53381ad6265SDimitry Andric if (PathEntry.empty()) 53481ad6265SDimitry Andric continue; 53581ad6265SDimitry Andric 53681ad6265SDimitry Andric SmallString<256> ExeTestPath; 53781ad6265SDimitry Andric 53881ad6265SDimitry Andric // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. 53981ad6265SDimitry Andric ExeTestPath = PathEntry; 54081ad6265SDimitry Andric sys::path::append(ExeTestPath, "cl.exe"); 54181ad6265SDimitry Andric if (!VFS.exists(ExeTestPath)) 54281ad6265SDimitry Andric continue; 54381ad6265SDimitry Andric 54481ad6265SDimitry Andric // cl.exe existing isn't a conclusive test for a VC toolchain; clang also 54581ad6265SDimitry Andric // has a cl.exe. So let's check for link.exe too. 54681ad6265SDimitry Andric ExeTestPath = PathEntry; 54781ad6265SDimitry Andric sys::path::append(ExeTestPath, "link.exe"); 54881ad6265SDimitry Andric if (!VFS.exists(ExeTestPath)) 54981ad6265SDimitry Andric continue; 55081ad6265SDimitry Andric 55181ad6265SDimitry Andric // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. 55281ad6265SDimitry Andric StringRef TestPath = PathEntry; 55381ad6265SDimitry Andric bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); 55481ad6265SDimitry Andric if (!IsBin) { 55581ad6265SDimitry Andric // Strip any architecture subdir like "amd64". 55681ad6265SDimitry Andric TestPath = sys::path::parent_path(TestPath); 55781ad6265SDimitry Andric IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); 55881ad6265SDimitry Andric } 55981ad6265SDimitry Andric if (IsBin) { 56081ad6265SDimitry Andric StringRef ParentPath = sys::path::parent_path(TestPath); 56181ad6265SDimitry Andric StringRef ParentFilename = sys::path::filename(ParentPath); 56281ad6265SDimitry Andric if (ParentFilename.equals_insensitive("VC")) { 56381ad6265SDimitry Andric Path = std::string(ParentPath); 56481ad6265SDimitry Andric VSLayout = ToolsetLayout::OlderVS; 56581ad6265SDimitry Andric return true; 56681ad6265SDimitry Andric } 56781ad6265SDimitry Andric if (ParentFilename.equals_insensitive("x86ret") || 56881ad6265SDimitry Andric ParentFilename.equals_insensitive("x86chk") || 56981ad6265SDimitry Andric ParentFilename.equals_insensitive("amd64ret") || 57081ad6265SDimitry Andric ParentFilename.equals_insensitive("amd64chk")) { 57181ad6265SDimitry Andric Path = std::string(ParentPath); 57281ad6265SDimitry Andric VSLayout = ToolsetLayout::DevDivInternal; 57381ad6265SDimitry Andric return true; 57481ad6265SDimitry Andric } 57581ad6265SDimitry Andric 57681ad6265SDimitry Andric } else { 57781ad6265SDimitry Andric // This could be a new (>=VS2017) toolchain. If it is, we should find 57881ad6265SDimitry Andric // path components with these prefixes when walking backwards through 57981ad6265SDimitry Andric // the path. 58081ad6265SDimitry Andric // Note: empty strings match anything. 58181ad6265SDimitry Andric StringRef ExpectedPrefixes[] = {"", "Host", "bin", "", 58281ad6265SDimitry Andric "MSVC", "Tools", "VC"}; 58381ad6265SDimitry Andric 58481ad6265SDimitry Andric auto It = sys::path::rbegin(PathEntry); 58581ad6265SDimitry Andric auto End = sys::path::rend(PathEntry); 58681ad6265SDimitry Andric for (StringRef Prefix : ExpectedPrefixes) { 58781ad6265SDimitry Andric if (It == End) 58881ad6265SDimitry Andric goto NotAToolChain; 58981ad6265SDimitry Andric if (!It->startswith_insensitive(Prefix)) 59081ad6265SDimitry Andric goto NotAToolChain; 59181ad6265SDimitry Andric ++It; 59281ad6265SDimitry Andric } 59381ad6265SDimitry Andric 59481ad6265SDimitry Andric // We've found a new toolchain! 59581ad6265SDimitry Andric // Back up 3 times (/bin/Host/arch) to get the root path. 59681ad6265SDimitry Andric StringRef ToolChainPath(PathEntry); 59781ad6265SDimitry Andric for (int i = 0; i < 3; ++i) 59881ad6265SDimitry Andric ToolChainPath = sys::path::parent_path(ToolChainPath); 59981ad6265SDimitry Andric 60081ad6265SDimitry Andric Path = std::string(ToolChainPath); 60181ad6265SDimitry Andric VSLayout = ToolsetLayout::VS2017OrNewer; 60281ad6265SDimitry Andric return true; 60381ad6265SDimitry Andric } 60481ad6265SDimitry Andric 60581ad6265SDimitry Andric NotAToolChain: 60681ad6265SDimitry Andric continue; 60781ad6265SDimitry Andric } 60881ad6265SDimitry Andric } 60981ad6265SDimitry Andric return false; 61081ad6265SDimitry Andric } 61181ad6265SDimitry Andric 61281ad6265SDimitry Andric bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, std::string &Path, 61381ad6265SDimitry Andric ToolsetLayout &VSLayout) { 61481ad6265SDimitry Andric #if !defined(USE_MSVC_SETUP_API) 61581ad6265SDimitry Andric return false; 61681ad6265SDimitry Andric #else 61781ad6265SDimitry Andric // FIXME: This really should be done once in the top-level program's main 61881ad6265SDimitry Andric // function, as it may have already been initialized with a different 61981ad6265SDimitry Andric // threading model otherwise. 62081ad6265SDimitry Andric sys::InitializeCOMRAII COM(sys::COMThreadingMode::SingleThreaded); 62181ad6265SDimitry Andric HRESULT HR; 62281ad6265SDimitry Andric 62381ad6265SDimitry Andric // _com_ptr_t will throw a _com_error if a COM calls fail. 62481ad6265SDimitry Andric // The LLVM coding standards forbid exception handling, so we'll have to 62581ad6265SDimitry Andric // stop them from being thrown in the first place. 62681ad6265SDimitry Andric // The destructor will put the regular error handler back when we leave 62781ad6265SDimitry Andric // this scope. 62881ad6265SDimitry Andric struct SuppressCOMErrorsRAII { 62981ad6265SDimitry Andric static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {} 63081ad6265SDimitry Andric 63181ad6265SDimitry Andric SuppressCOMErrorsRAII() { _set_com_error_handler(handler); } 63281ad6265SDimitry Andric 63381ad6265SDimitry Andric ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); } 63481ad6265SDimitry Andric 63581ad6265SDimitry Andric } COMErrorSuppressor; 63681ad6265SDimitry Andric 63781ad6265SDimitry Andric ISetupConfigurationPtr Query; 63881ad6265SDimitry Andric HR = Query.CreateInstance(__uuidof(SetupConfiguration)); 63981ad6265SDimitry Andric if (FAILED(HR)) 64081ad6265SDimitry Andric return false; 64181ad6265SDimitry Andric 64281ad6265SDimitry Andric IEnumSetupInstancesPtr EnumInstances; 64381ad6265SDimitry Andric HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances); 64481ad6265SDimitry Andric if (FAILED(HR)) 64581ad6265SDimitry Andric return false; 64681ad6265SDimitry Andric 64781ad6265SDimitry Andric ISetupInstancePtr Instance; 64881ad6265SDimitry Andric HR = EnumInstances->Next(1, &Instance, nullptr); 64981ad6265SDimitry Andric if (HR != S_OK) 65081ad6265SDimitry Andric return false; 65181ad6265SDimitry Andric 65281ad6265SDimitry Andric ISetupInstancePtr NewestInstance; 653*bdd1243dSDimitry Andric std::optional<uint64_t> NewestVersionNum; 65481ad6265SDimitry Andric do { 65581ad6265SDimitry Andric bstr_t VersionString; 65681ad6265SDimitry Andric uint64_t VersionNum; 65781ad6265SDimitry Andric HR = Instance->GetInstallationVersion(VersionString.GetAddress()); 65881ad6265SDimitry Andric if (FAILED(HR)) 65981ad6265SDimitry Andric continue; 66081ad6265SDimitry Andric HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum); 66181ad6265SDimitry Andric if (FAILED(HR)) 66281ad6265SDimitry Andric continue; 66381ad6265SDimitry Andric if (!NewestVersionNum || (VersionNum > NewestVersionNum)) { 66481ad6265SDimitry Andric NewestInstance = Instance; 66581ad6265SDimitry Andric NewestVersionNum = VersionNum; 66681ad6265SDimitry Andric } 66781ad6265SDimitry Andric } while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK); 66881ad6265SDimitry Andric 66981ad6265SDimitry Andric if (!NewestInstance) 67081ad6265SDimitry Andric return false; 67181ad6265SDimitry Andric 67281ad6265SDimitry Andric bstr_t VCPathWide; 67381ad6265SDimitry Andric HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress()); 67481ad6265SDimitry Andric if (FAILED(HR)) 67581ad6265SDimitry Andric return false; 67681ad6265SDimitry Andric 67781ad6265SDimitry Andric std::string VCRootPath; 67881ad6265SDimitry Andric convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); 67981ad6265SDimitry Andric 68081ad6265SDimitry Andric SmallString<256> ToolsVersionFilePath(VCRootPath); 68181ad6265SDimitry Andric sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", 68281ad6265SDimitry Andric "Microsoft.VCToolsVersion.default.txt"); 68381ad6265SDimitry Andric 68481ad6265SDimitry Andric auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath); 68581ad6265SDimitry Andric if (!ToolsVersionFile) 68681ad6265SDimitry Andric return false; 68781ad6265SDimitry Andric 68881ad6265SDimitry Andric SmallString<256> ToolchainPath(VCRootPath); 68981ad6265SDimitry Andric sys::path::append(ToolchainPath, "Tools", "MSVC", 69081ad6265SDimitry Andric ToolsVersionFile->get()->getBuffer().rtrim()); 69181ad6265SDimitry Andric auto Status = VFS.status(ToolchainPath); 69281ad6265SDimitry Andric if (!Status || !Status->isDirectory()) 69381ad6265SDimitry Andric return false; 69481ad6265SDimitry Andric 69581ad6265SDimitry Andric Path = std::string(ToolchainPath.str()); 69681ad6265SDimitry Andric VSLayout = ToolsetLayout::VS2017OrNewer; 69781ad6265SDimitry Andric return true; 69881ad6265SDimitry Andric #endif 69981ad6265SDimitry Andric } 70081ad6265SDimitry Andric 70181ad6265SDimitry Andric bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout) { 70281ad6265SDimitry Andric std::string VSInstallPath; 70381ad6265SDimitry Andric if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)", 70481ad6265SDimitry Andric "InstallDir", VSInstallPath, nullptr) || 70581ad6265SDimitry Andric getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)", 70681ad6265SDimitry Andric "InstallDir", VSInstallPath, nullptr)) { 70781ad6265SDimitry Andric if (!VSInstallPath.empty()) { 708*bdd1243dSDimitry Andric auto pos = VSInstallPath.find(R"(\Common7\IDE)"); 709*bdd1243dSDimitry Andric if (pos == std::string::npos) 710*bdd1243dSDimitry Andric return false; 711*bdd1243dSDimitry Andric SmallString<256> VCPath(StringRef(VSInstallPath.c_str(), pos)); 71281ad6265SDimitry Andric sys::path::append(VCPath, "VC"); 71381ad6265SDimitry Andric 71481ad6265SDimitry Andric Path = std::string(VCPath.str()); 71581ad6265SDimitry Andric VSLayout = ToolsetLayout::OlderVS; 71681ad6265SDimitry Andric return true; 71781ad6265SDimitry Andric } 71881ad6265SDimitry Andric } 71981ad6265SDimitry Andric return false; 72081ad6265SDimitry Andric } 72181ad6265SDimitry Andric 72281ad6265SDimitry Andric } // namespace llvm 723