diff --git a/clang/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt --- a/clang/docs/tools/clang-formatted-files.txt +++ b/clang/docs/tools/clang-formatted-files.txt @@ -285,7 +285,6 @@ clang/lib/Driver/ToolChains/Minix.h clang/lib/Driver/ToolChains/MipsLinux.cpp clang/lib/Driver/ToolChains/MSP430.h -clang/lib/Driver/ToolChains/MSVCSetupApi.h clang/lib/Driver/ToolChains/PPCFreeBSD.cpp clang/lib/Driver/ToolChains/PPCFreeBSD.h clang/lib/Driver/ToolChains/PPCLinux.h @@ -5150,6 +5149,8 @@ llvm/include/llvm/Support/MSP430AttributeParser.h llvm/include/llvm/Support/MSP430Attributes.h llvm/include/llvm/Support/MSVCErrorWorkarounds.h +llvm/include/llvm/Support/MSVCPaths.h +llvm/include/llvm/Support/MSVCSetupApi.h llvm/include/llvm/Support/Parallel.h llvm/include/llvm/Support/PGOOptions.h llvm/include/llvm/Support/PointerLikeTypeTraits.h @@ -5840,6 +5841,7 @@ llvm/lib/Support/MemoryBufferRef.cpp llvm/lib/Support/MSP430AttributeParser.cpp llvm/lib/Support/MSP430Attributes.cpp +llvm/lib/Support/MSVCPaths.cpp llvm/lib/Support/Optional.cpp llvm/lib/Support/Parallel.cpp llvm/lib/Support/Program.cpp diff --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h --- a/clang/lib/Driver/ToolChains/MSVC.h +++ b/clang/lib/Driver/ToolChains/MSVC.h @@ -15,6 +15,7 @@ #include "clang/Driver/Compilation.h" #include "clang/Driver/Tool.h" #include "clang/Driver/ToolChain.h" +#include "llvm/Support/MSVCPaths.h" namespace clang { namespace driver { @@ -85,12 +86,9 @@ return getSubDirectoryPath(Type, SubdirParent, getArch()); } - enum class ToolsetLayout { - OlderVS, - VS2017OrNewer, - DevDivInternal, - }; - bool getIsVS2017OrNewer() const { return VSLayout == ToolsetLayout::VS2017OrNewer; } + bool getIsVS2017OrNewer() const { + return VSLayout == llvm::ToolsetLayout::VS2017OrNewer; + } void AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, @@ -139,7 +137,7 @@ Tool *buildAssembler() const override; private: std::string VCToolChainPath; - ToolsetLayout VSLayout = ToolsetLayout::OlderVS; + llvm::ToolsetLayout VSLayout = llvm::ToolsetLayout::OlderVS; CudaInstallationDetector CudaInstallation; RocmInstallationDetector RocmInstallation; }; diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" +#include "llvm/Support/MSVCPaths.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" @@ -32,36 +33,12 @@ #include #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #define NOGDI - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#ifndef NOMINMAX +#define NOMINMAX #endif - -#ifdef _MSC_VER -// Don't support SetupApi on MinGW. -#define USE_MSVC_SETUP_API - -// Make sure this comes before MSVCSetupApi.h -#include - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-virtual-dtor" -#endif -#include "MSVCSetupApi.h" -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#include "llvm/Support/COM.h" -_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)); +#include #endif using namespace clang::driver; @@ -132,294 +109,6 @@ return (Status->getPermissions() & llvm::sys::fs::perms::all_exe) != 0; } -// Defined below. -// Forward declare this so there aren't too many things above the constructor. -static bool getSystemRegistryString(const char *keyPath, const char *valueName, - std::string &value, std::string *phValue); - -static std::string getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS, - 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; - 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; -} - -// Check command line arguments to try and find a toolchain. -static bool -findVCToolChainViaCommandLine(llvm::vfs::FileSystem &VFS, const ArgList &Args, - std::string &Path, - MSVCToolChain::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 (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir, - options::OPT__SLASH_winsysroot)) { - if (A->getOption().getID() == options::OPT__SLASH_winsysroot) { - llvm::SmallString<128> ToolsPath(A->getValue()); - llvm::sys::path::append(ToolsPath, "VC", "Tools", "MSVC"); - std::string VCToolsVersion; - if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion)) - VCToolsVersion = A->getValue(); - else - VCToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath); - llvm::sys::path::append(ToolsPath, VCToolsVersion); - Path = std::string(ToolsPath.str()); - } else { - Path = A->getValue(); - } - VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; - } - return false; -} - -// Check various environment variables to try and find a toolchain. -static bool -findVCToolChainViaEnvironment(llvm::vfs::FileSystem &VFS, std::string &Path, - MSVCToolChain::ToolsetLayout &VSLayout) { - // These variables are typically set by vcvarsall.bat - // when launching a developer command prompt. - if (llvm::Optional VCToolsInstallDir = - llvm::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 = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; - } - if (llvm::Optional VCInstallDir = - llvm::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 = MSVCToolChain::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 (llvm::Optional PathEnv = - llvm::sys::Process::GetEnv("PATH")) { - llvm::SmallVector PathEntries; - llvm::StringRef(*PathEnv).split(PathEntries, llvm::sys::EnvPathSeparator); - for (llvm::StringRef PathEntry : PathEntries) { - if (PathEntry.empty()) - continue; - - llvm::SmallString<256> ExeTestPath; - - // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. - ExeTestPath = PathEntry; - llvm::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; - llvm::sys::path::append(ExeTestPath, "link.exe"); - if (!VFS.exists(ExeTestPath)) - continue; - - // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. - llvm::StringRef TestPath = PathEntry; - bool IsBin = - llvm::sys::path::filename(TestPath).equals_insensitive("bin"); - if (!IsBin) { - // Strip any architecture subdir like "amd64". - TestPath = llvm::sys::path::parent_path(TestPath); - IsBin = llvm::sys::path::filename(TestPath).equals_insensitive("bin"); - } - if (IsBin) { - llvm::StringRef ParentPath = llvm::sys::path::parent_path(TestPath); - llvm::StringRef ParentFilename = llvm::sys::path::filename(ParentPath); - if (ParentFilename.equals_insensitive("VC")) { - Path = std::string(ParentPath); - VSLayout = MSVCToolChain::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 = MSVCToolChain::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. - llvm::StringRef ExpectedPrefixes[] = {"", "Host", "bin", "", - "MSVC", "Tools", "VC"}; - - auto It = llvm::sys::path::rbegin(PathEntry); - auto End = llvm::sys::path::rend(PathEntry); - for (llvm::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. - llvm::StringRef ToolChainPath(PathEntry); - for (int i = 0; i < 3; ++i) - ToolChainPath = llvm::sys::path::parent_path(ToolChainPath); - - Path = std::string(ToolChainPath); - VSLayout = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; - } - - NotAToolChain: - continue; - } - } - return false; -} - -// Query the Setup Config server for installs, then pick the newest version -// and find its default VC toolchain. -// This is the preferred way to discover new Visual Studios, as they're no -// longer listed in the registry. -static bool -findVCToolChainViaSetupConfig(llvm::vfs::FileSystem &VFS, std::string &Path, - MSVCToolChain::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. - llvm::sys::InitializeCOMRAII COM(llvm::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; - Optional 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; - llvm::convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); - - llvm::SmallString<256> ToolsVersionFilePath(VCRootPath); - llvm::sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", - "Microsoft.VCToolsVersion.default.txt"); - - auto ToolsVersionFile = llvm::MemoryBuffer::getFile(ToolsVersionFilePath); - if (!ToolsVersionFile) - return false; - - llvm::SmallString<256> ToolchainPath(VCRootPath); - llvm::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 = MSVCToolChain::ToolsetLayout::VS2017OrNewer; - return true; -#endif -} - -// Look in the registry for Visual Studio installs, and use that to get -// a toolchain path. VS2017 and newer don't get added to the registry. -// So if we find something here, we know that it's an older version. -static bool findVCToolChainViaRegistry(std::string &Path, - MSVCToolChain::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()) { - llvm::SmallString<256> VCPath(llvm::StringRef( - VSInstallPath.c_str(), VSInstallPath.find(R"(\Common7\IDE)"))); - llvm::sys::path::append(VCPath, "VC"); - - Path = std::string(VCPath.str()); - VSLayout = MSVCToolChain::ToolsetLayout::OlderVS; - return true; - } - } - return false; -} - // Try to find Exe from a Visual Studio distribution. This first tries to find // an installed copy of Visual Studio and, failing that, looks in the PATH, // making sure that whatever executable that's found is not a same-named exe @@ -695,7 +384,7 @@ // native target bin directory. // e.g. when compiling for x86 on an x64 host, PATH should start with: // /bin/Hostx64/x86;/bin/Hostx64/x64 - // This doesn't attempt to handle ToolsetLayout::DevDivInternal. + // This doesn't attempt to handle llvm::ToolsetLayout::DevDivInternal. if (TC.getIsVS2017OrNewer() && llvm::Triple(llvm::sys::getProcessTriple()).getArch() != TC.getArch()) { auto HostArch = llvm::Triple(llvm::sys::getProcessTriple()).getArch(); @@ -773,10 +462,20 @@ // use. Check the environment next, in case we're being invoked from a VS // command prompt. Failing that, just try to find the newest Visual Studio // version we can and use its default VC toolchain. - findVCToolChainViaCommandLine(getVFS(), Args, VCToolChainPath, VSLayout) || - findVCToolChainViaEnvironment(getVFS(), VCToolChainPath, VSLayout) || - findVCToolChainViaSetupConfig(getVFS(), VCToolChainPath, VSLayout) || - findVCToolChainViaRegistry(VCToolChainPath, VSLayout); + llvm::StringRef VCToolsDir, VCToolsVersion, WinSysRoot; + if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsdir)) + VCToolsDir = A->getValue(); + if (Arg *A = Args.getLastArg(options::OPT__SLASH_vctoolsversion)) + VCToolsVersion = A->getValue(); + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsysroot)) + WinSysRoot = A->getValue(); + llvm::findVCToolChainViaCommandLine(getVFS(), VCToolsDir, VCToolsVersion, + WinSysRoot, VCToolChainPath, VSLayout) || + llvm::findVCToolChainViaEnvironment(getVFS(), VCToolChainPath, + VSLayout) || + llvm::findVCToolChainViaSetupConfig(getVFS(), VCToolChainPath, + VSLayout) || + llvm::findVCToolChainViaRegistry(VCToolChainPath, VSLayout); } Tool *MSVCToolChain::buildLinker() const { @@ -846,15 +545,15 @@ const char *SubdirName; const char *IncludeName; switch (VSLayout) { - case ToolsetLayout::OlderVS: + case llvm::ToolsetLayout::OlderVS: SubdirName = llvmArchToLegacyVCArch(TargetArch); IncludeName = "include"; break; - case ToolsetLayout::VS2017OrNewer: + case llvm::ToolsetLayout::VS2017OrNewer: SubdirName = llvmArchToWindowsSDKArch(TargetArch); IncludeName = "include"; break; - case ToolsetLayout::DevDivInternal: + case llvm::ToolsetLayout::DevDivInternal: SubdirName = llvmArchToDevDivInternalArch(TargetArch); IncludeName = "inc"; break; @@ -866,7 +565,7 @@ switch (Type) { case SubDirectoryType::Bin: - if (VSLayout == ToolsetLayout::VS2017OrNewer) { + if (VSLayout == llvm::ToolsetLayout::VS2017OrNewer) { const bool HostIsX64 = llvm::Triple(llvm::sys::getProcessTriple()).isArch64Bit(); const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; @@ -885,238 +584,10 @@ return std::string(Path.str()); } -#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 buffer(valueSize); - result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0], - &valueSize); - if (result == ERROR_SUCCESS) { - std::wstring WideValue(reinterpret_cast(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 && !isDigit(*sp)) - sp++; - if (!*sp) - continue; - const char *ep = sp + 1; - while (*ep && (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 -} - // Find the most recent version of Universal CRT or Windows 10 SDK. // vcvarsqueryregistry.bat from Visual Studio 2015 sorts entries in the include // directory by name and uses the last one of the list. // So we compare entry names lexicographically to find the greatest one. -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, - const ArgList &Args, - std::string &Path, int &Major, - std::string &Version) { - if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir, - options::OPT__SLASH_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 (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion)) - SDKVersion.tryParse(A->getValue()); - - if (A->getOption().getID() == options::OPT__SLASH_winsysroot) { - llvm::SmallString<128> SDKPath(A->getValue()); - llvm::sys::path::append(SDKPath, "Windows Kits"); - if (!SDKVersion.empty()) - llvm::sys::path::append(SDKPath, Twine(SDKVersion.getMajor())); - else - llvm::sys::path::append( - SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath)); - Path = std::string(SDKPath.str()); - } else { - Path = A->getValue(); - } - - if (!SDKVersion.empty()) { - Major = SDKVersion.getMajor(); - Version = SDKVersion.getAsString(); - } else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) { - Major = 10; - } - return true; - } - return false; -} - -/// Get Windows SDK installation directory. -static bool getWindowsSDKDir(llvm::vfs::FileSystem &VFS, const ArgList &Args, - std::string &Path, int &Major, - std::string &WindowsSDKIncludeVersion, - std::string &WindowsSDKLibVersion) { - // Trust /winsdkdir and /winsdkversion if present. - if (getWindowsSDKDirViaCommandLine(VFS, Args, 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) { - llvm::SmallString<128> TestPath(Path); - llvm::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; -} - // Gets the library path required to link against the Windows SDK. bool MSVCToolChain::getWindowsSDKLibraryPath( const ArgList &Args, std::string &path) const { @@ -1126,8 +597,16 @@ std::string windowsSDKLibVersion; path.clear(); - if (!getWindowsSDKDir(getVFS(), Args, sdkPath, sdkMajor, - windowsSDKIncludeVersion, windowsSDKLibVersion)) + llvm::StringRef WinSdkDir, WinSdkVersion, WinSysRoot; + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir)) + WinSdkDir = A->getValue(); + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion)) + WinSdkVersion = A->getValue(); + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsysroot)) + WinSysRoot = A->getValue(); + if (!llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot, + sdkPath, sdkMajor, windowsSDKIncludeVersion, + windowsSDKLibVersion)) return false; llvm::SmallString<128> libPath(sdkPath); @@ -1164,35 +643,22 @@ return !getVFS().exists(TestPath); } -static bool getUniversalCRTSdkDir(llvm::vfs::FileSystem &VFS, - const ArgList &Args, 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, Args, 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 MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args, std::string &Path) const { std::string UniversalCRTSdkPath; std::string UCRTVersion; Path.clear(); - if (!getUniversalCRTSdkDir(getVFS(), Args, UniversalCRTSdkPath, UCRTVersion)) + llvm::StringRef WinSdkDir, WinSdkVersion, WinSysRoot; + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkdir)) + WinSdkDir = A->getValue(); + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsdkversion)) + WinSdkVersion = A->getValue(); + if (Arg *A = Args.getLastArg(options::OPT__SLASH_winsysroot)) + WinSysRoot = A->getValue(); + if (!llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion, + WinSysRoot, UniversalCRTSdkPath, + UCRTVersion)) return false; StringRef ArchName = llvmArchToWindowsSDKArch(getArch()); @@ -1317,11 +783,20 @@ addSystemInclude(DriverArgs, CC1Args, getSubDirectoryPath(SubDirectoryType::Include, "atlmfc")); + llvm::StringRef WinSdkDir, WinSdkVersion, WinSysRoot; + if (Arg *A = DriverArgs.getLastArg(options::OPT__SLASH_winsdkdir)) + WinSdkDir = A->getValue(); + if (Arg *A = DriverArgs.getLastArg(options::OPT__SLASH_winsdkversion)) + WinSdkVersion = A->getValue(); + if (Arg *A = DriverArgs.getLastArg(options::OPT__SLASH_winsysroot)) + WinSysRoot = A->getValue(); + if (useUniversalCRT()) { std::string UniversalCRTSdkPath; std::string UCRTVersion; - if (getUniversalCRTSdkDir(getVFS(), DriverArgs, UniversalCRTSdkPath, - UCRTVersion)) { + if (llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion, + WinSysRoot, UniversalCRTSdkPath, + UCRTVersion)) { AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, UniversalCRTSdkPath, "Include", UCRTVersion, "ucrt"); } @@ -1331,8 +806,9 @@ int major = 0; std::string windowsSDKIncludeVersion; std::string windowsSDKLibVersion; - if (getWindowsSDKDir(getVFS(), DriverArgs, WindowsSDKDir, major, - windowsSDKIncludeVersion, windowsSDKLibVersion)) { + if (llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot, + WindowsSDKDir, major, windowsSDKIncludeVersion, + windowsSDKLibVersion)) { if (major >= 8) { // Note: windowsSDKIncludeVersion is empty for SDKs prior to v10. // Anyway, llvm::sys::path::append is able to manage it. diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -22,6 +22,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/MSVCPaths.h" #include "llvm/Support/TarWriter.h" #include #include @@ -82,6 +83,10 @@ void linkerMain(llvm::ArrayRef args); + // Adds various search paths based on the sysroot. Must only be called once + // config->machine has been set. + void addWinSysRootLibSearchPaths(); + // Used by the resolver to parse .drectve section contents. void parseDirectives(InputFile *file); @@ -107,11 +112,14 @@ bool findUnderscoreMangle(StringRef sym); + // Determines the location of the sysroot based on `args`, environment, etc. + void detectWinSysRoot(const llvm::opt::InputArgList &args); + // Parses LIB environment which contains a list of search paths. void addLibSearchPaths(); // Library search path. The first element is always "" (current directory). - std::vector searchPaths; + std::vector searchPaths; // Convert resource files and potentially merge input resource object // trees into one resource tree. @@ -154,6 +162,14 @@ llvm::StringSet<> directivesExports; COFFLinkerContext &ctx; + + llvm::ToolsetLayout vsLayout = llvm::ToolsetLayout::OlderVS; + std::string vcToolChainPath; + llvm::SmallString<128> diaPath; + bool useWinSysRootLibPath = false; + llvm::SmallString<128> universalCRTLibPath; + int sdkMajor = 0; + llvm::SmallString<128> windowsSdkLibPath; }; // Functions below this line are defined in DriverUtils.cpp. diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -37,7 +37,9 @@ #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Host.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/MSVCPaths.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" @@ -150,6 +152,118 @@ return sym; } +// Windows SDKs and VC Toolchains group their contents into subdirectories based +// on the target architecture. +static const char *getWindowsSDKArch() { + switch (config->machine) { + case I386: + return "x86"; + case AMD64: + return "x64"; + case ARMNT: + return "arm"; + case ARM64: + return "arm64"; + default: + return ""; + } +} + +// Similar to the above function, but for Visual Studios before VS2017. +static const char *getLegacyVCArch() { + switch (config->machine) { + case I386: + // x86 is default in legacy VC toolchains. + // e.g. x86 libs are directly in /lib as opposed to /lib/x86. + return ""; + case AMD64: + return "amd64"; + case ARMNT: + return "arm"; + case ARM64: + return "arm64"; + default: + return ""; + } +} + +// Similar to the above function, but for DevDiv internal builds. +static const char *getDevDivInternalArch() { + switch (config->machine) { + case I386: + return "i386"; + case AMD64: + return "amd64"; + case ARMNT: + return "arm"; + case ARM64: + return "arm64"; + default: + return ""; + } +} + +enum class SubDirectoryType { + Bin, + Include, + Lib, +}; + +static std::string getSubDirectoryPath(SubDirectoryType Type, + ToolsetLayout VSLayout, + const std::string &VCToolChainPath, + StringRef SubdirParent = "") { + const char *SubdirName; + const char *IncludeName; + switch (VSLayout) { + case ToolsetLayout::OlderVS: + SubdirName = getLegacyVCArch(); + IncludeName = "include"; + break; + case ToolsetLayout::VS2017OrNewer: + SubdirName = getWindowsSDKArch(); + IncludeName = "include"; + break; + case ToolsetLayout::DevDivInternal: + SubdirName = getDevDivInternalArch(); + IncludeName = "inc"; + break; + } + + llvm::SmallString<256> Path(VCToolChainPath); + if (!SubdirParent.empty()) + path::append(Path, SubdirParent); + + switch (Type) { + case SubDirectoryType::Bin: + if (VSLayout == ToolsetLayout::VS2017OrNewer) { + const bool HostIsX64 = llvm::Triple(getProcessTriple()).isArch64Bit(); + const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; + path::append(Path, "bin", HostName, SubdirName); + } else { // OlderVS or DevDivInternal + path::append(Path, "bin", SubdirName); + } + break; + case SubDirectoryType::Include: + path::append(Path, IncludeName); + break; + case SubDirectoryType::Lib: + path::append(Path, "lib", SubdirName); + break; + } + return std::string(Path.str()); +} + +// Check if the Include path of a specified version of Visual Studio contains +// specific header files. If not, they are probably shipped with Universal CRT. +static bool useUniversalCRT(ToolsetLayout VSLayout, + const std::string &VCToolChainPath) { + SmallString<128> TestPath(getSubDirectoryPath(SubDirectoryType::Include, + VSLayout, VCToolChainPath)); + path::append(TestPath, "stdlib.h"); + return !fs::exists(TestPath); +} + bool LinkerDriver::findUnderscoreMangle(StringRef sym) { Symbol *s = ctx.symtab.findMangle(mangle(sym)); return s && !isa(s); @@ -422,8 +536,8 @@ if (hasPathSep) return filename; bool hasExt = filename.contains('.'); - for (StringRef dir : searchPaths) { - SmallString<128> path = dir; + for (const std::string& dir : searchPaths) { + SmallString<128> path(dir); sys::path::append(path, filename); if (sys::fs::exists(path.str())) return saver().save(path.str()); @@ -504,6 +618,117 @@ return path; } +void LinkerDriver::detectWinSysRoot(const opt::InputArgList &Args) { + IntrusiveRefCntPtr VFS = vfs::getRealFileSystem(); + + // Check the command line first, that's the user explicitly telling us what to + // use. Check the environment next, in case we're being invoked from a VS + // command prompt. Failing that, just try to find the newest Visual Studio + // version we can and use its default VC toolchain. + StringRef VCToolsDir, VCToolsVersion, WinSysRoot; + if (auto *A = Args.getLastArg(OPT_vctoolsdir)) + VCToolsDir = A->getValue(); + if (auto *A = Args.getLastArg(OPT_vctoolsversion)) + VCToolsVersion = A->getValue(); + if (auto *A = Args.getLastArg(OPT_winsysroot)) + WinSysRoot = A->getValue(); + if (!findVCToolChainViaCommandLine(*VFS, VCToolsDir, VCToolsVersion, + WinSysRoot, vcToolChainPath, vsLayout) && + (Args.hasArg(OPT_lldignoreenv) || + !findVCToolChainViaEnvironment(*VFS, vcToolChainPath, vsLayout)) && + !findVCToolChainViaSetupConfig(*VFS, vcToolChainPath, vsLayout) && + !findVCToolChainViaRegistry(vcToolChainPath, vsLayout)) + return; + + // If the VC environment hasn't been configured (perhaps because the user did + // not run vcvarsall), try to build a consistent link environment. If the + // environment variable is set however, assume the user knows what they're + // doing. If the user passes /vctoolsdir or /winsdkdir, trust that over env + // vars. + if (const auto *A = Args.getLastArg(OPT_diasdkdir, OPT_winsysroot)) { + diaPath = A->getValue(); + if (A->getOption().getID() == OPT_winsysroot) + path::append(diaPath, "DIA SDK"); + } + useWinSysRootLibPath = Args.hasArg(OPT_lldignoreenv) || + !Process::GetEnv("LIB") || + Args.getLastArg(OPT_vctoolsdir, OPT_winsysroot); + if (Args.hasArg(OPT_lldignoreenv) || !Process::GetEnv("LIB") || + Args.getLastArg(OPT_winsdkdir, OPT_winsysroot)) { + StringRef WinSdkDir, WinSdkVersion, WinSysRoot; + if (auto *A = Args.getLastArg(OPT_winsdkdir)) + WinSdkDir = A->getValue(); + if (auto *A = Args.getLastArg(OPT_winsdkversion)) + WinSdkVersion = A->getValue(); + if (auto *A = Args.getLastArg(OPT_winsysroot)) + WinSysRoot = A->getValue(); + + if (useUniversalCRT(vsLayout, vcToolChainPath)) { + std::string UniversalCRTSdkPath; + std::string UCRTVersion; + if (getUniversalCRTSdkDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot, + UniversalCRTSdkPath, UCRTVersion)) { + universalCRTLibPath = UniversalCRTSdkPath; + path::append(universalCRTLibPath, "Lib", UCRTVersion, "ucrt"); + } + } + + std::string sdkPath; + std::string windowsSDKIncludeVersion; + std::string windowsSDKLibVersion; + if (getWindowsSDKDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot, sdkPath, + sdkMajor, windowsSDKIncludeVersion, + windowsSDKLibVersion)) { + windowsSdkLibPath = sdkPath; + path::append(windowsSdkLibPath, "Lib"); + if (sdkMajor >= 8) + path::append(windowsSdkLibPath, windowsSDKLibVersion, "um"); + } + } +} + +void LinkerDriver::addWinSysRootLibSearchPaths() { + if (!diaPath.empty()) { + // The DIA SDK always uses the legacy vc arch, even in new MSVC versions. + path::append(diaPath, "lib", getLegacyVCArch()); + searchPaths.push_back(diaPath.str().str()); + } + if (useWinSysRootLibPath) { + searchPaths.push_back( + getSubDirectoryPath(SubDirectoryType::Lib, vsLayout, vcToolChainPath)); + searchPaths.push_back(getSubDirectoryPath(SubDirectoryType::Lib, vsLayout, + vcToolChainPath, "atlmfc")); + } + if (!universalCRTLibPath.empty()) { + StringRef ArchName = getWindowsSDKArch(); + if (!ArchName.empty()) { + path::append(universalCRTLibPath, ArchName); + searchPaths.push_back(universalCRTLibPath.str().str()); + } + } + if (!windowsSdkLibPath.empty()) { + if (sdkMajor >= 8) { + path::append(windowsSdkLibPath, getWindowsSDKArch()); + } else { + switch (config->machine) { + // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. + case I386: + break; + case AMD64: + path::append(windowsSdkLibPath, "x64"); + break; + case ARMNT: + // It is not necessary to link against Windows SDK 7.x when targeting + // ARM. + return; + default: + return; + } + } + searchPaths.push_back(windowsSdkLibPath.str().str()); + } +} + // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { Optional envOpt = Process::GetEnv("LIB"); @@ -513,7 +738,7 @@ while (!env.empty()) { StringRef path; std::tie(path, env) = env.split(';'); - searchPaths.push_back(path); + searchPaths.push_back(path.str()); } } @@ -615,7 +840,7 @@ // the same command with the same inputs. This is for --reproduce. static std::string createResponseFile(const opt::InputArgList &args, ArrayRef filePaths, - ArrayRef searchPaths) { + ArrayRef searchPaths) { SmallString<0> data; raw_svector_ostream os(data); @@ -626,6 +851,7 @@ case OPT_INPUT: case OPT_defaultlib: case OPT_libpath: + case OPT_winsysroot: break; case OPT_call_graph_ordering_file: case OPT_deffile: @@ -658,7 +884,7 @@ } } - for (StringRef path : searchPaths) { + for (const std::string& path : searchPaths) { std::string relPath = relativeToRoot(path); os << "/libpath:" << quote(relPath) << "\n"; } @@ -1335,7 +1561,8 @@ searchPaths.push_back(""); for (auto *arg : args.filtered(OPT_libpath)) searchPaths.push_back(arg->getValue()); - if (!args.hasArg(OPT_lldignoreenv)) + detectWinSysRoot(args); + if (!args.hasArg(OPT_winsysroot)) addLibSearchPaths(); // Handle /ignore @@ -1475,6 +1702,7 @@ config->machine = getMachineType(arg->getValue()); if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) fatal(Twine("unknown /machine argument: ") + arg->getValue()); + addWinSysRootLibSearchPaths(); } // Handle /nodefaultlib: @@ -1866,10 +2094,12 @@ for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) parseFunctionPadMin(arg, config->machine); - if (tar) - tar->append("response.txt", - createResponseFile(args, filePaths, - ArrayRef(searchPaths).slice(1))); + if (tar) { + tar->append( + "response.txt", + createResponseFile(args, filePaths, + ArrayRef(searchPaths).slice(1))); + } // Handle /largeaddressaware config->largeAddressAware = args.hasFlag( diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -41,6 +41,7 @@ MetaVarName<"[auto,always,never]">; def defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; +def diasdkdir : P<"diasdkdir", "Set the location of the DIA SDK">; def entry : P<"entry", "Name of entry point symbol">; def errorlimit : P<"errorlimit", "Maximum number of errors to emit before stopping (0 = no limit)">; @@ -89,9 +90,16 @@ def stub : P<"stub", "Specify DOS stub file">; def subsystem : P<"subsystem", "Specify subsystem">; def timestamp : P<"timestamp", "Specify the PE header timestamp">; +def vctoolsdir : P<"vctoolsdir", "Set the location of the VC tools">; +def vctoolsversion : P<"vctoolsversion", + "Specify which VC tools version to use">; def version : P<"version", "Specify a version number in the PE header">; def wholearchive_file : P<"wholearchive", "Include all object files from this library">; +def winsdkdir : P<"winsdkdir", "Set the location of the Windows SDK">; +def winsdkversion : P<"winsdkversion", "Specify which SDK version to use">; +def winsysroot : P<"winsysroot", + "Adds several subdirectories to the library search paths">; def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">, Alias; diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -56,6 +56,7 @@ MachineTypes mt = file->getMachineType(); if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { config->machine = mt; + driver->addWinSysRootLibSearchPaths(); } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) { error(toString(file) + ": machine type " + machineToStr(mt) + " conflicts with " + machineToStr(config->machine)); diff --git a/llvm/include/llvm/Support/MSVCPaths.h b/llvm/include/llvm/Support/MSVCPaths.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Support/MSVCPaths.h @@ -0,0 +1,63 @@ +//===-- MSVCPaths.h - MSVC path-parsing helpers -----------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MSVCPATHS_H +#define LLVM_SUPPORT_MSVCPATHS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include + +namespace llvm { + +enum class ToolsetLayout { + OlderVS, + VS2017OrNewer, + DevDivInternal, +}; + +/// Get Windows SDK installation directory. +bool getWindowsSDKDir(vfs::FileSystem &VFS, llvm::StringRef WinSdkDir, + llvm::StringRef WinSdkVersion, llvm::StringRef WinSysRoot, + std::string &Path, int &Major, + std::string &WindowsSDKIncludeVersion, + std::string &WindowsSDKLibVersion); + +bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, llvm::StringRef WinSdkDir, + llvm::StringRef WinSdkVersion, + llvm::StringRef WinSysRoot, std::string &Path, + std::string &UCRTVersion); + +// Check command line arguments to try and find a toolchain. +bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS, + llvm::StringRef VCToolsDir, + llvm::StringRef VCToolsVersion, + llvm::StringRef WinSysRoot, + std::string &Path, + ToolsetLayout &VSLayout); + +// Check various environment variables to try and find a toolchain. +bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path, + ToolsetLayout &VSLayout); + +// Query the Setup Config server for installs, then pick the newest version +// and find its default VC toolchain. +// This is the preferred way to discover new Visual Studios, as they're no +// longer listed in the registry. +bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, std::string &Path, + ToolsetLayout &VSLayout); + +// Look in the registry for Visual Studio installs, and use that to get +// a toolchain path. VS2017 and newer don't get added to the registry. +// So if we find something here, we know that it's an older version. +bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout); + +} // namespace llvm + +#endif diff --git a/clang/lib/Driver/ToolChains/MSVCSetupApi.h b/llvm/include/llvm/Support/MSVCSetupApi.h rename from clang/lib/Driver/ToolChains/MSVCSetupApi.h rename to llvm/include/llvm/Support/MSVCSetupApi.h diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -175,6 +175,7 @@ MD5.cpp MSP430Attributes.cpp MSP430AttributeParser.cpp + MSVCPaths.cpp NativeFormatting.cpp OptimizedStructLayout.cpp Optional.cpp diff --git a/llvm/lib/Support/MSVCPaths.cpp b/llvm/lib/Support/MSVCPaths.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Support/MSVCPaths.cpp @@ -0,0 +1,589 @@ +//===-- 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/Support/MSVCPaths.h" +#include "llvm/ADT/Optional.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/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/ConvertUTF.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 + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif + +#ifdef _MSC_VER +// Don't support SetupApi on MinGW. +#define USE_MSVC_SETUP_API + +// Make sure this comes before MSVCSetupApi.h +#include + +#include "llvm/Support/COM.h" +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif +#include "llvm/Support/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, + llvm::StringRef WinSdkDir, + llvm::StringRef WinSdkVersion, + llvm::StringRef WinSysRoot, + std::string &Path, int &Major, + std::string &Version) { + if (!WinSdkDir.empty() || !WinSysRoot.empty()) { + // 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.empty()) + SDKVersion.tryParse(WinSdkVersion); + + if (!WinSysRoot.empty()) { + 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 buffer(valueSize); + result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0], + &valueSize); + if (result == ERROR_SUCCESS) { + std::wstring WideValue(reinterpret_cast(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 { + +bool getWindowsSDKDir(llvm::vfs::FileSystem &VFS, llvm::StringRef WinSdkDir, + llvm::StringRef WinSdkVersion, llvm::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) { + llvm::SmallString<128> TestPath(Path); + llvm::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(llvm::vfs::FileSystem &VFS, + llvm::StringRef WinSdkDir, + llvm::StringRef WinSdkVersion, + llvm::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(llvm::vfs::FileSystem &VFS, + llvm::StringRef VCToolsDir, + llvm::StringRef VCToolsVersion, + llvm::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.empty() || !WinSysRoot.empty()) { + if (!WinSysRoot.empty()) { + llvm::SmallString<128> ToolsPath(WinSysRoot); + llvm::sys::path::append(ToolsPath, "VC", "Tools", "MSVC"); + std::string ToolsVersion; + if (!VCToolsVersion.empty()) + ToolsVersion = VCToolsVersion.str(); + else + ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath); + llvm::sys::path::append(ToolsPath, ToolsVersion); + Path = std::string(ToolsPath.str()); + } else { + Path = VCToolsDir.str(); + } + VSLayout = ToolsetLayout::VS2017OrNewer; + return true; + } + return false; +} + +bool findVCToolChainViaEnvironment(llvm::vfs::FileSystem &VFS, + std::string &Path, ToolsetLayout &VSLayout) { + // These variables are typically set by vcvarsall.bat + // when launching a developer command prompt. + if (llvm::Optional VCToolsInstallDir = + llvm::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 (llvm::Optional VCInstallDir = + llvm::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 (llvm::Optional PathEnv = + llvm::sys::Process::GetEnv("PATH")) { + llvm::SmallVector PathEntries; + llvm::StringRef(*PathEnv).split(PathEntries, llvm::sys::EnvPathSeparator); + for (llvm::StringRef PathEntry : PathEntries) { + if (PathEntry.empty()) + continue; + + llvm::SmallString<256> ExeTestPath; + + // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. + ExeTestPath = PathEntry; + llvm::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; + llvm::sys::path::append(ExeTestPath, "link.exe"); + if (!VFS.exists(ExeTestPath)) + continue; + + // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. + llvm::StringRef TestPath = PathEntry; + bool IsBin = + llvm::sys::path::filename(TestPath).equals_insensitive("bin"); + if (!IsBin) { + // Strip any architecture subdir like "amd64". + TestPath = llvm::sys::path::parent_path(TestPath); + IsBin = llvm::sys::path::filename(TestPath).equals_insensitive("bin"); + } + if (IsBin) { + llvm::StringRef ParentPath = llvm::sys::path::parent_path(TestPath); + llvm::StringRef ParentFilename = llvm::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. + llvm::StringRef ExpectedPrefixes[] = {"", "Host", "bin", "", + "MSVC", "Tools", "VC"}; + + auto It = llvm::sys::path::rbegin(PathEntry); + auto End = llvm::sys::path::rend(PathEntry); + for (llvm::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. + llvm::StringRef ToolChainPath(PathEntry); + for (int i = 0; i < 3; ++i) + ToolChainPath = llvm::sys::path::parent_path(ToolChainPath); + + Path = std::string(ToolChainPath); + VSLayout = ToolsetLayout::VS2017OrNewer; + return true; + } + + NotAToolChain: + continue; + } + } + return false; +} + +bool findVCToolChainViaSetupConfig(llvm::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. + llvm::sys::InitializeCOMRAII COM(llvm::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; + Optional 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; + llvm::convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); + + llvm::SmallString<256> ToolsVersionFilePath(VCRootPath); + llvm::sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", + "Microsoft.VCToolsVersion.default.txt"); + + auto ToolsVersionFile = llvm::MemoryBuffer::getFile(ToolsVersionFilePath); + if (!ToolsVersionFile) + return false; + + llvm::SmallString<256> ToolchainPath(VCRootPath); + llvm::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()) { + llvm::SmallString<256> VCPath(llvm::StringRef( + VSInstallPath.c_str(), VSInstallPath.find(R"(\Common7\IDE)"))); + sys::path::append(VCPath, "VC"); + + Path = std::string(VCPath.str()); + VSLayout = ToolsetLayout::OlderVS; + return true; + } + } + return false; +} + +} // namespace llvm diff --git a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn @@ -98,6 +98,7 @@ "MD5.cpp", "MSP430AttributeParser.cpp", "MSP430Attributes.cpp", + "MSVCPaths.cpp", "ManagedStatic.cpp", "MathExtras.cpp", "MemAlloc.cpp", diff --git a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel @@ -1309,9 +1309,6 @@ "lib/Driver/ToolChains/Arch/*.cpp", "lib/Driver/ToolChains/Arch/*.h", ], - exclude = [ - "lib/Driver/ToolChains/MSVCSetupApi.h", - ], ), hdrs = glob([ "include/clang/Driver/*.h",