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,7 @@ llvm/include/llvm/Support/MSP430AttributeParser.h llvm/include/llvm/Support/MSP430Attributes.h llvm/include/llvm/Support/MSVCErrorWorkarounds.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 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 @@ -51,7 +51,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnon-virtual-dtor" #endif -#include "MSVCSetupApi.h" +#include "llvm/Support/MSVCSetupApi.h" #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -36,6 +36,12 @@ using llvm::COFF::WindowsSubsystem; using llvm::Optional; +enum class ToolsetLayout { + OlderVS, + VS2017OrNewer, + DevDivInternal, +}; + class COFFOptTable : public llvm::opt::OptTable { public: COFFOptTable(); @@ -82,6 +88,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 +117,17 @@ bool findUnderscoreMangle(StringRef sym); + // Determines the location of the sysroot based on `args`, environment, etc. + void detectWinSysRoot(const llvm::opt::InputArgList &args); + + void getUniversalCRTLibraryPath(const llvm::opt::InputArgList &Args); + void getWindowsSDKLibraryPath(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 +170,14 @@ llvm::StringSet<> directivesExports; COFFLinkerContext &ctx; + + ToolsetLayout vsLayout = 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,6 +37,7 @@ #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/MathExtras.h" #include "llvm/Support/Parallel.h" @@ -50,6 +51,44 @@ #include #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 + +#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 +#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)); + +// TODO(pkasting): Remove +#undef IMAGE_SUBSYSTEM_UNKNOWN +#undef IMAGE_SUBSYSTEM_WINDOWS_GUI +#undef IMAGE_SUBSYSTEM_WINDOWS_CUI +#endif + using namespace llvm; using namespace llvm::object; using namespace llvm::COFF; @@ -150,6 +189,642 @@ return sym; } +static std::string getHighestNumericTupleInDirectory(StringRef Directory) { + std::string Highest; + VersionTuple HighestTuple; + + std::error_code EC; + for (fs::directory_iterator DirIt(Directory, EC), DirEnd; + !EC && DirIt != DirEnd; DirIt.increment(EC)) { + + if (!fs::is_directory(DirIt->path())) + continue; + StringRef CandidateName = path::filename(DirIt->path()); + VersionTuple Tuple; + if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error. + continue; + if (Tuple > HighestTuple) { + HighestTuple = Tuple; + Highest = CandidateName.str(); + } + } + + return Highest; +} + +// 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); +} + +#ifdef _WIN32 +static bool readFullStringValue(HKEY hkey, const char *valueName, + std::string &value) { + std::wstring WideValueName; + if (!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 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(const std::string &SDKPath, + std::string &SDKVersion) { + llvm::SmallString<128> IncludePath(SDKPath); + path::append(IncludePath, "Include"); + SDKVersion = getHighestNumericTupleInDirectory(IncludePath); + return !SDKVersion.empty(); +} + +static bool getWindowsSDKDirViaCommandLine(const opt::InputArgList &Args, + std::string &Path, int &Major, + std::string &Version) { + if (auto *A = Args.getLastArg(OPT_winsdkdir, OPT_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 (auto *A = Args.getLastArg(OPT_winsdkversion)) + SDKVersion.tryParse(A->getValue()); + + if (A->getOption().getID() == OPT_winsysroot) { + llvm::SmallString<128> SDKPath(A->getValue()); + path::append(SDKPath, "Windows Kits"); + if (!SDKVersion.empty()) + path::append(SDKPath, Twine(SDKVersion.getMajor())); + else + path::append(SDKPath, getHighestNumericTupleInDirectory(SDKPath)); + Path = std::string(SDKPath.str()); + } else { + Path = A->getValue(); + } + + if (!SDKVersion.empty()) { + Major = SDKVersion.getMajor(); + Version = SDKVersion.getAsString(); + } else if (getWindows10SDKVersionFromPath(Path, Version)) { + Major = 10; + } + return true; + } + return false; +} + +static bool getUniversalCRTSdkDir(const opt::InputArgList &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(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(Path, UCRTVersion); +} + +/// Get Windows SDK installation directory. +static bool getWindowsSDKDir(const opt::InputArgList &Args, + std::string &Path, int &Major, + std::string &WindowsSDKIncludeVersion, + std::string &WindowsSDKLibVersion) { + // Trust /winsdkdir and /winsdkversion if present. + if (getWindowsSDKDirViaCommandLine(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); + path::append(TestPath, "Lib", Test); + if (fs::exists(TestPath)) { + WindowsSDKLibVersion = Test; + break; + } + } + return !WindowsSDKLibVersion.empty(); + } + if (Major == 10) { + if (!getWindows10SDKVersionFromPath(Path, WindowsSDKIncludeVersion)) + return false; + WindowsSDKLibVersion = WindowsSDKIncludeVersion; + return true; + } + // Unsupported SDK version + return false; +} + + // Check command line arguments to try and find a toolchain. +static bool +findVCToolChainViaCommandLine(const opt::InputArgList &Args, 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 (auto *A = Args.getLastArg(OPT_vctoolsdir, OPT_winsysroot)) { + if (A->getOption().getID() == OPT_winsysroot) { + SmallString<128> ToolsPath(A->getValue()); + path::append(ToolsPath, "VC", "Tools", "MSVC"); + std::string VCToolsVersion; + if (auto *A = Args.getLastArg(OPT_vctoolsversion)) + VCToolsVersion = A->getValue(); + else + VCToolsVersion = getHighestNumericTupleInDirectory(ToolsPath); + llvm::sys::path::append(ToolsPath, VCToolsVersion); + Path = std::string(ToolsPath.str()); + } else { + Path = A->getValue(); + } + VSLayout = ToolsetLayout::VS2017OrNewer; + return true; + } + return false; +} + +// Check various environment variables to try and find a toolchain. +static bool +findVCToolChainViaEnvironment(std::string &Path, ToolsetLayout &VSLayout) { + // These variables are typically set by vcvarsall.bat + // when launching a developer command prompt. + if (Optional VCToolsInstallDir = + 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 (Optional VCInstallDir = 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 (Optional PathEnv = Process::GetEnv("PATH")) { + SmallVector PathEntries; + StringRef(*PathEnv).split(PathEntries, EnvPathSeparator); + for (StringRef PathEntry : PathEntries) { + if (PathEntry.empty()) + continue; + + SmallString<256> ExeTestPath; + + // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. + ExeTestPath = PathEntry; + path::append(ExeTestPath, "cl.exe"); + if (!fs::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; + path::append(ExeTestPath, "link.exe"); + if (!fs::exists(ExeTestPath)) + continue; + + // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. + StringRef TestPath = PathEntry; + bool IsBin = + path::filename(TestPath).equals_insensitive("bin"); + if (!IsBin) { + // Strip any architecture subdir like "amd64". + TestPath = path::parent_path(TestPath); + IsBin = path::filename(TestPath).equals_insensitive("bin"); + } + if (IsBin) { + StringRef ParentPath = path::parent_path(TestPath); + StringRef ParentFilename = path::filename(ParentPath); + if (ParentFilename.equals_insensitive("VC")) { + Path = std::string(ParentPath); + VSLayout = ToolsetLayout::OlderVS; + return true; + } + if (ParentFilename.equals_insensitive("x86ret") || + ParentFilename.equals_insensitive("x86chk") || + ParentFilename.equals_insensitive("amd64ret") || + ParentFilename.equals_insensitive("amd64chk")) { + Path = std::string(ParentPath); + VSLayout = ToolsetLayout::DevDivInternal; + return true; + } + + } else { + // This could be a new (>=VS2017) toolchain. If it is, we should find + // path components with these prefixes when walking backwards through + // the path. + // Note: empty strings match anything. + StringRef ExpectedPrefixes[] = + {"", "Host", "bin", "", "MSVC", "Tools", "VC"}; + + auto It = path::rbegin(PathEntry); + auto End = path::rend(PathEntry); + for (StringRef Prefix : ExpectedPrefixes) { + if (It == End) + goto NotAToolChain; + if (!It->startswith_insensitive(Prefix)) + goto NotAToolChain; + ++It; + } + + // We've found a new toolchain! + // Back up 3 times (/bin/Host/arch) to get the root path. + StringRef ToolChainPath(PathEntry); + for (int i = 0; i < 3; ++i) + ToolChainPath = path::parent_path(ToolChainPath); + + Path = std::string(ToolChainPath); + VSLayout = 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(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. + InitializeCOMRAII COM(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; + convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); + + SmallString<256> ToolsVersionFilePath(VCRootPath); + path::append(ToolsVersionFilePath, "Auxiliary", "Build", + "Microsoft.VCToolsVersion.default.txt"); + + auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath); + if (!ToolsVersionFile) + return false; + + SmallString<256> ToolchainPath(VCRootPath); + path::append(ToolchainPath, "Tools", "MSVC", + ToolsVersionFile->get()->getBuffer().rtrim()); + if (!fs::is_directory(ToolchainPath)) + return false; + + Path = std::string(ToolchainPath.str()); + VSLayout = 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, + 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()) { + SmallString<256> VCPath(StringRef( + VSInstallPath.c_str(), VSInstallPath.find(R"(\Common7\IDE)"))); + path::append(VCPath, "VC"); + + Path = std::string(VCPath.str()); + VSLayout = ToolsetLayout::OlderVS; + return true; + } + } + return false; +} + bool LinkerDriver::findUnderscoreMangle(StringRef sym) { Symbol *s = ctx.symtab.findMangle(mangle(sym)); return s && !isa(s); @@ -422,8 +1097,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 +1179,105 @@ return path; } +void LinkerDriver::detectWinSysRoot(const opt::InputArgList &Args) { + // 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. + if (!findVCToolChainViaCommandLine(Args, vcToolChainPath, vsLayout) && + (Args.hasArg(OPT_lldignoreenv) || + !findVCToolChainViaEnvironment(vcToolChainPath, vsLayout)) && + !findVCToolChainViaSetupConfig(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)) { + if (useUniversalCRT(vsLayout, vcToolChainPath)) + getUniversalCRTLibraryPath(Args); + getWindowsSDKLibraryPath(Args); + } +} + +void LinkerDriver::getUniversalCRTLibraryPath(const opt::InputArgList &Args) { + std::string UniversalCRTSdkPath; + std::string UCRTVersion; + + if (getUniversalCRTSdkDir(Args, UniversalCRTSdkPath, UCRTVersion)) { + universalCRTLibPath = UniversalCRTSdkPath; + path::append(universalCRTLibPath, "Lib", UCRTVersion, "ucrt"); + } +} + +void LinkerDriver::getWindowsSDKLibraryPath(const opt::InputArgList &Args) { + std::string sdkPath; + std::string windowsSDKIncludeVersion; + std::string windowsSDKLibVersion; + + if (getWindowsSDKDir(Args, 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 +1287,7 @@ while (!env.empty()) { StringRef path; std::tie(path, env) = env.split(';'); - searchPaths.push_back(path); + searchPaths.push_back(path.str()); } } @@ -615,7 +1389,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 +1400,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 +1433,7 @@ } } - for (StringRef path : searchPaths) { + for (const std::string& path : searchPaths) { std::string relPath = relativeToRoot(path); os << "/libpath:" << quote(relPath) << "\n"; } @@ -1335,7 +2110,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 +2251,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 +2643,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/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/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",