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 @@ -5329,6 +5328,8 @@ llvm/include/llvm/Transforms/Utils/UnifyLoopExits.h llvm/include/llvm/Transforms/Utils/ValueMapper.h llvm/include/llvm/Transforms/Vectorize/SLPVectorizer.h +llvm/include/llvm/WindowsDriver/MSVCPaths.h +llvm/include/llvm/WindowsDriver/MSVCSetupApi.h llvm/include/llvm/WindowsManifest/WindowsManifestMerger.h llvm/include/llvm/WindowsResource/ResourceScriptToken.h llvm/include/llvm/XRay/BlockIndexer.h @@ -6492,6 +6493,7 @@ llvm/lib/Transforms/Vectorize/VPlanTransforms.h llvm/lib/Transforms/Vectorize/VPlanValue.h llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp +llvm/lib/WindowsDriver/MSVCPaths.cpp llvm/lib/WindowsManifest/WindowsManifestMerger.cpp llvm/lib/XRay/BlockIndexer.cpp llvm/lib/XRay/BlockVerifier.cpp diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -4,6 +4,7 @@ Option ProfileData Support + WindowsDriver ) if(WIN32) 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/WindowsDriver/MSVCPaths.h" namespace clang { namespace driver { @@ -73,29 +74,15 @@ return 4; } - enum class SubDirectoryType { - Bin, - Include, - Lib, - }; - std::string getSubDirectoryPath(SubDirectoryType Type, - llvm::StringRef SubdirParent, + std::string getSubDirectoryPath(llvm::SubDirectoryType Type, + llvm::StringRef SubdirParent = "") const; + std::string getSubDirectoryPath(llvm::SubDirectoryType Type, llvm::Triple::ArchType TargetArch) const; - // Convenience overload. - // Uses the current target arch. - std::string getSubDirectoryPath(SubDirectoryType Type, - llvm::StringRef SubdirParent = "") const { - return getSubDirectoryPath(Type, SubdirParent, getArch()); + bool getIsVS2017OrNewer() const { + return VSLayout == llvm::ToolsetLayout::VS2017OrNewer; } - enum class ToolsetLayout { - OlderVS, - VS2017OrNewer, - DevDivInternal, - }; - bool getIsVS2017OrNewer() const { return VSLayout == ToolsetLayout::VS2017OrNewer; } - void AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const override; @@ -142,8 +129,9 @@ Tool *buildLinker() const override; Tool *buildAssembler() const override; private: + llvm::Optional WinSdkDir, WinSdkVersion, WinSysRoot; 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 @@ -40,91 +40,12 @@ #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 "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)); -#endif - using namespace clang::driver; using namespace clang::driver::toolchains; using namespace clang::driver::tools; using namespace clang; using namespace llvm::opt; -// Windows SDKs and VC Toolchains group their contents into subdirectories based -// on the target architecture. This function converts an llvm::Triple::ArchType -// to the corresponding subdirectory name. -static const char *llvmArchToWindowsSDKArch(llvm::Triple::ArchType Arch) { - using ArchType = llvm::Triple::ArchType; - switch (Arch) { - case ArchType::x86: - return "x86"; - case ArchType::x86_64: - return "x64"; - case ArchType::arm: - return "arm"; - case ArchType::aarch64: - return "arm64"; - default: - return ""; - } -} - -// Similar to the above function, but for Visual Studios before VS2017. -static const char *llvmArchToLegacyVCArch(llvm::Triple::ArchType Arch) { - using ArchType = llvm::Triple::ArchType; - switch (Arch) { - case ArchType::x86: - // x86 is default in legacy VC toolchains. - // e.g. x86 libs are directly in /lib as opposed to /lib/x86. - return ""; - case ArchType::x86_64: - return "amd64"; - case ArchType::arm: - return "arm"; - case ArchType::aarch64: - return "arm64"; - default: - return ""; - } -} - -// Similar to the above function, but for DevDiv internal builds. -static const char *llvmArchToDevDivInternalArch(llvm::Triple::ArchType Arch) { - using ArchType = llvm::Triple::ArchType; - switch (Arch) { - case ArchType::x86: - return "i386"; - case ArchType::x86_64: - return "amd64"; - case ArchType::arm: - return "arm"; - case ArchType::aarch64: - return "arm64"; - default: - return ""; - } -} - static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) { auto Status = VFS.status(Path); if (!Status) @@ -132,294 +53,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 @@ -427,8 +60,8 @@ static std::string FindVisualStudioExecutable(const ToolChain &TC, const char *Exe) { const auto &MSVC = static_cast(TC); - SmallString<128> FilePath(MSVC.getSubDirectoryPath( - toolchains::MSVCToolChain::SubDirectoryType::Bin)); + SmallString<128> FilePath( + MSVC.getSubDirectoryPath(llvm::SubDirectoryType::Bin)); llvm::sys::path::append(FilePath, Exe); return std::string(canExecute(TC.getVFS(), FilePath) ? FilePath.str() : Exe); } @@ -469,7 +102,7 @@ // The DIA SDK always uses the legacy vc arch, even in new MSVC versions. llvm::sys::path::append(DIAPath, "lib", - llvmArchToLegacyVCArch(TC.getArch())); + llvm::archToLegacyVCArch(TC.getArch())); CmdArgs.push_back(Args.MakeArgString(Twine("-libpath:") + DIAPath)); } if (!llvm::sys::Process::GetEnv("LIB") || @@ -477,12 +110,10 @@ options::OPT__SLASH_winsysroot)) { CmdArgs.push_back(Args.MakeArgString( Twine("-libpath:") + - TC.getSubDirectoryPath( - toolchains::MSVCToolChain::SubDirectoryType::Lib))); + TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib))); CmdArgs.push_back(Args.MakeArgString( Twine("-libpath:") + - TC.getSubDirectoryPath(toolchains::MSVCToolChain::SubDirectoryType::Lib, - "atlmfc"))); + TC.getSubDirectoryPath(llvm::SubDirectoryType::Lib, "atlmfc"))); } if (!llvm::sys::Process::GetEnv("LIB") || Args.getLastArg(options::OPT__SLASH_winsdkdir, @@ -695,7 +326,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(); @@ -730,13 +361,12 @@ for (const char *Cursor = EnvBlock.data(); *Cursor != '\0';) { llvm::StringRef EnvVar(Cursor); if (EnvVar.startswith_insensitive("path=")) { - using SubDirectoryType = toolchains::MSVCToolChain::SubDirectoryType; constexpr size_t PrefixLen = 5; // strlen("path=") Environment.push_back(Args.MakeArgString( EnvVar.substr(0, PrefixLen) + - TC.getSubDirectoryPath(SubDirectoryType::Bin) + + TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin) + llvm::Twine(llvm::sys::EnvPathSeparator) + - TC.getSubDirectoryPath(SubDirectoryType::Bin, "", HostArch) + + TC.getSubDirectoryPath(llvm::SubDirectoryType::Bin, HostArch) + (EnvVar.size() > PrefixLen ? llvm::Twine(llvm::sys::EnvPathSeparator) + EnvVar.substr(PrefixLen) @@ -769,14 +399,29 @@ if (getDriver().getInstalledDir() != getDriver().Dir) getProgramPaths().push_back(getDriver().Dir); + Optional VCToolsDir, VCToolsVersion; + 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_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(); + // 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. - findVCToolChainViaCommandLine(getVFS(), Args, VCToolChainPath, VSLayout) || - findVCToolChainViaEnvironment(getVFS(), VCToolChainPath, VSLayout) || - findVCToolChainViaSetupConfig(getVFS(), VCToolChainPath, VSLayout) || - findVCToolChainViaRegistry(VCToolChainPath, VSLayout); + 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 { @@ -835,355 +480,48 @@ RocmInstallation.print(OS); } -// Get the path to a specific subdirectory in the current toolchain for -// a given target architecture. -// VS2017 changed the VC toolchain layout, so this should be used instead -// of hardcoding paths. std::string -MSVCToolChain::getSubDirectoryPath(SubDirectoryType Type, - llvm::StringRef SubdirParent, - llvm::Triple::ArchType TargetArch) const { - const char *SubdirName; - const char *IncludeName; - switch (VSLayout) { - case ToolsetLayout::OlderVS: - SubdirName = llvmArchToLegacyVCArch(TargetArch); - IncludeName = "include"; - break; - case ToolsetLayout::VS2017OrNewer: - SubdirName = llvmArchToWindowsSDKArch(TargetArch); - IncludeName = "include"; - break; - case ToolsetLayout::DevDivInternal: - SubdirName = llvmArchToDevDivInternalArch(TargetArch); - IncludeName = "inc"; - break; - } - - llvm::SmallString<256> Path(VCToolChainPath); - if (!SubdirParent.empty()) - llvm::sys::path::append(Path, SubdirParent); - - switch (Type) { - case SubDirectoryType::Bin: - if (VSLayout == ToolsetLayout::VS2017OrNewer) { - const bool HostIsX64 = - llvm::Triple(llvm::sys::getProcessTriple()).isArch64Bit(); - const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; - llvm::sys::path::append(Path, "bin", HostName, SubdirName); - } else { // OlderVS or DevDivInternal - llvm::sys::path::append(Path, "bin", SubdirName); - } - break; - case SubDirectoryType::Include: - llvm::sys::path::append(Path, IncludeName); - break; - case SubDirectoryType::Lib: - llvm::sys::path::append(Path, "lib", SubdirName); - break; - } - 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; +MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type, + llvm::StringRef SubdirParent) const { + return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, getArch(), + SubdirParent); } -#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 +std::string +MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type, + llvm::Triple::ArchType TargetArch) const { + return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, TargetArch, + ""); } // 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 { +bool MSVCToolChain::getWindowsSDKLibraryPath(const ArgList &Args, + std::string &path) const { std::string sdkPath; int sdkMajor = 0; std::string windowsSDKIncludeVersion; std::string windowsSDKLibVersion; path.clear(); - if (!getWindowsSDKDir(getVFS(), Args, sdkPath, sdkMajor, - windowsSDKIncludeVersion, windowsSDKLibVersion)) + if (!llvm::getWindowsSDKDir(getVFS(), WinSdkDir, WinSdkVersion, WinSysRoot, + sdkPath, sdkMajor, windowsSDKIncludeVersion, + windowsSDKLibVersion)) return false; llvm::SmallString<128> libPath(sdkPath); llvm::sys::path::append(libPath, "Lib"); - if (sdkMajor >= 8) { - llvm::sys::path::append(libPath, windowsSDKLibVersion, "um", - llvmArchToWindowsSDKArch(getArch())); - } else { - switch (getArch()) { - // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. - case llvm::Triple::x86: - break; - case llvm::Triple::x86_64: - llvm::sys::path::append(libPath, "x64"); - break; - case llvm::Triple::arm: - // It is not necessary to link against Windows SDK 7.x when targeting ARM. - return false; - default: - return false; - } - } - - path = std::string(libPath.str()); - return true; + if (sdkMajor >= 8) + llvm::sys::path::append(libPath, windowsSDKLibVersion, "um"); + return llvm::appendArchToWindowsSDKLibPath(sdkMajor, libPath, getArch(), + path); } -// 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. bool MSVCToolChain::useUniversalCRT() const { - llvm::SmallString<128> TestPath( - getSubDirectoryPath(SubDirectoryType::Include)); - llvm::sys::path::append(TestPath, "stdlib.h"); - 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); + return llvm::useUniversalCRT(VSLayout, VCToolChainPath, getArch(), getVFS()); } bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args, @@ -1192,10 +530,12 @@ std::string UCRTVersion; Path.clear(); - if (!getUniversalCRTSdkDir(getVFS(), Args, UniversalCRTSdkPath, UCRTVersion)) + if (!llvm::getUniversalCRTSdkDir(getVFS(), WinSdkDir, WinSdkVersion, + WinSysRoot, UniversalCRTSdkPath, + UCRTVersion)) return false; - StringRef ArchName = llvmArchToWindowsSDKArch(getArch()); + StringRef ArchName = llvm::archToWindowsSDKArch(getArch()); if (ArchName.empty()) return false; @@ -1313,15 +653,17 @@ // the correct include paths first. if (!VCToolChainPath.empty()) { addSystemInclude(DriverArgs, CC1Args, - getSubDirectoryPath(SubDirectoryType::Include)); - addSystemInclude(DriverArgs, CC1Args, - getSubDirectoryPath(SubDirectoryType::Include, "atlmfc")); + getSubDirectoryPath(llvm::SubDirectoryType::Include)); + addSystemInclude( + DriverArgs, CC1Args, + getSubDirectoryPath(llvm::SubDirectoryType::Include, "atlmfc")); 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 +673,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. @@ -1389,7 +732,8 @@ if (MSVT.empty()) MSVT = getTriple().getEnvironmentVersion(); if (MSVT.empty() && IsWindowsMSVC) - MSVT = getMSVCVersionFromExe(getSubDirectoryPath(SubDirectoryType::Bin)); + MSVT = + getMSVCVersionFromExe(getSubDirectoryPath(llvm::SubDirectoryType::Bin)); if (MSVT.empty() && Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions, IsWindowsMSVC)) { diff --git a/lld/COFF/CMakeLists.txt b/lld/COFF/CMakeLists.txt --- a/lld/COFF/CMakeLists.txt +++ b/lld/COFF/CMakeLists.txt @@ -38,6 +38,7 @@ Option Passes Support + WindowsDriver WindowsManifest LINK_LIBS diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -23,6 +23,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/TarWriter.h" +#include "llvm/WindowsDriver/MSVCPaths.h" #include #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,6 +112,9 @@ 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(); @@ -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 @@ -24,6 +24,7 @@ #include "lld/Common/Version.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" @@ -150,6 +151,21 @@ return sym; } +static llvm::Triple::ArchType getArch() { + switch (config->machine) { + case I386: + return llvm::Triple::ArchType::x86; + case AMD64: + return llvm::Triple::ArchType::x86_64; + case ARMNT: + return llvm::Triple::ArchType::arm; + case ARM64: + return llvm::Triple::ArchType::aarch64; + default: + return llvm::Triple::ArchType::UnknownArch; + } +} + bool LinkerDriver::findUnderscoreMangle(StringRef sym) { Symbol *s = ctx.symtab.findMangle(mangle(sym)); return s && !isa(s); @@ -504,6 +520,101 @@ 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. + Optional 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)) { + Optional WinSdkDir, WinSdkVersion; + if (auto *A = Args.getLastArg(OPT_winsdkdir)) + WinSdkDir = A->getValue(); + if (auto *A = Args.getLastArg(OPT_winsdkversion)) + WinSdkVersion = A->getValue(); + + if (useUniversalCRT(vsLayout, vcToolChainPath, getArch(), *VFS)) { + 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", archToLegacyVCArch(getArch())); + searchPaths.push_back(saver().save(diaPath.str())); + } + if (useWinSysRootLibPath) { + searchPaths.push_back(saver().save(getSubDirectoryPath( + SubDirectoryType::Lib, vsLayout, vcToolChainPath, getArch()))); + searchPaths.push_back(saver().save( + getSubDirectoryPath(SubDirectoryType::Lib, vsLayout, vcToolChainPath, + getArch(), "atlmfc"))); + } + if (!universalCRTLibPath.empty()) { + StringRef ArchName = archToWindowsSDKArch(getArch()); + if (!ArchName.empty()) { + path::append(universalCRTLibPath, ArchName); + searchPaths.push_back(saver().save(universalCRTLibPath.str())); + } + } + if (!windowsSdkLibPath.empty()) { + std::string path; + if (appendArchToWindowsSDKLibPath(sdkMajor, windowsSdkLibPath, getArch(), + path)) + searchPaths.push_back(saver().save(path)); + } +} + // Parses LIB environment which contains a list of search paths. void LinkerDriver::addLibSearchPaths() { Optional envOpt = Process::GetEnv("LIB"); @@ -626,6 +737,7 @@ case OPT_INPUT: case OPT_defaultlib: case OPT_libpath: + case OPT_winsysroot: break; case OPT_call_graph_ordering_file: case OPT_deffile: @@ -1335,7 +1447,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_lldignoreenv) && !args.hasArg(OPT_winsysroot)) addLibSearchPaths(); // Handle /ignore @@ -1475,6 +1588,7 @@ config->machine = getMachineType(arg->getValue()); if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) fatal(Twine("unknown /machine argument: ") + arg->getValue()); + addWinSysRootLibSearchPaths(); } // Handle /nodefaultlib: @@ -1836,15 +1950,8 @@ } } - // Process files specified as /defaultlib. These should be enequeued after - // other files, which is why they are in a separate loop. - for (auto *arg : args.filtered(OPT_defaultlib)) - if (Optional path = findLib(arg->getValue())) - enqueuePath(*path, false, false); - // Read all input files given via the command line. run(); - if (errorCount()) return; @@ -1853,9 +1960,19 @@ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) { warn("/machine is not specified. x64 is assumed"); config->machine = AMD64; + addWinSysRootLibSearchPaths(); } config->wordsize = config->is64() ? 8 : 4; + // Process files specified as /defaultlib. These must be processed after + // addWinSysRootLibSearchPaths(), which is why they are in a separate loop. + for (auto *arg : args.filtered(OPT_defaultlib)) + if (Optional path = findLib(arg->getValue())) + enqueuePath(*path, false, false); + run(); + if (errorCount()) + return; + // Handle /safeseh, x86 only, on by default, except for mingw. if (config->machine == I386) { config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw); @@ -1866,10 +1983,11 @@ for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) parseFunctionPadMin(arg, config->machine); - if (tar) + 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/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -36,6 +36,9 @@ COFF Improvements ----------------- +* Added autodetection of MSVC toolchain, a la clang-cl. Also added /winsysroot + support for explicit specification of MSVC toolchain location. + (`D118070 `_) * ... MinGW Improvements diff --git a/lld/test/COFF/winsysroot.test b/lld/test/COFF/winsysroot.test new file mode 100644 --- /dev/null +++ b/lld/test/COFF/winsysroot.test @@ -0,0 +1,17 @@ +Check that /winsysroot results in the correct machine-specific subdirectory +being searched for the defaultlibs, for a 32-bit .obj. +# RUN: yaml2obj %p/Inputs/hello32.yaml -o %t.obj +# RUN: mkdir -p %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x86 +# RUN: mkdir -p %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x64 +# RUN: cp %p/Inputs/std32.lib %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x86 +# RUN: cp %p/Inputs/std64.lib %t.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x64 +# RUN: not lld-link %t.obj /winsysroot:%t.dir/sysroot /defaultlib:std32 2>&1 | FileCheck -check-prefix=MISSINGENTRY32 --implicit-check-not=_ExitProcess %s +MISSINGENTRY32: undefined symbol: _mainCRTStartup + +Check the same for a 64-bit input .obj. +# RUN: not lld-link %p/Inputs/hello64.obj /winsysroot:%t.dir/sysroot /defaultlib:std64 2>&1 | FileCheck -check-prefix=MISSINGENTRY64 --implicit-check-not=_ExitProcess %s +MISSINGENTRY64: undefined symbol: mainCRTStartup + +Check that when /winsysroot is specified, %LIB% is ignored. +# RUN: env LIB=foo.dir/sysroot/VC/Tools/MSVC/1.1.1.1/lib/x86 not lld-link %t.obj /winsysroot:%t.dir/doesnotexist /defaultlib:std32 2>&1 | FileCheck -check-prefix=LIBIGNORED %s +LIBIGNORED: could not open 'std32.lib' diff --git a/llvm/include/llvm/WindowsDriver/MSVCPaths.h b/llvm/include/llvm/WindowsDriver/MSVCPaths.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/WindowsDriver/MSVCPaths.h @@ -0,0 +1,104 @@ +//===-- 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/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include + +namespace llvm { + +enum class SubDirectoryType { + Bin, + Include, + Lib, +}; + +enum class ToolsetLayout { + OlderVS, + VS2017OrNewer, + DevDivInternal, +}; + +// Windows SDKs and VC Toolchains group their contents into subdirectories based +// on the target architecture. This function converts an llvm::Triple::ArchType +// to the corresponding subdirectory name. +const char *archToWindowsSDKArch(llvm::Triple::ArchType Arch); + +// Similar to the above function, but for Visual Studios before VS2017. +const char *archToLegacyVCArch(llvm::Triple::ArchType Arch); + +// Similar to the above function, but for DevDiv internal builds. +const char *archToDevDivInternalArch(llvm::Triple::ArchType Arch); + +bool appendArchToWindowsSDKLibPath(int SDKMajor, llvm::SmallString<128> LibPath, + llvm::Triple::ArchType Arch, + std::string &path); + +// Get the path to a specific subdirectory in the current toolchain for +// a given target architecture. +// VS2017 changed the VC toolchain layout, so this should be used instead +// of hardcoding paths. +std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout, + const std::string &VCToolChainPath, + llvm::Triple::ArchType TargetArch, + llvm::StringRef SubdirParent = ""); + +// 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. +bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath, + llvm::Triple::ArchType TargetArch, + llvm::vfs::FileSystem &VFS); + +/// Get Windows SDK installation directory. +bool getWindowsSDKDir(vfs::FileSystem &VFS, + llvm::Optional WinSdkDir, + llvm::Optional WinSdkVersion, + llvm::Optional WinSysRoot, + std::string &Path, int &Major, + std::string &WindowsSDKIncludeVersion, + std::string &WindowsSDKLibVersion); + +bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, + llvm::Optional WinSdkDir, + llvm::Optional WinSdkVersion, + llvm::Optional WinSysRoot, + std::string &Path, + std::string &UCRTVersion); + +// Check command line arguments to try and find a toolchain. +bool findVCToolChainViaCommandLine( + vfs::FileSystem &VFS, llvm::Optional VCToolsDir, + llvm::Optional VCToolsVersion, + llvm::Optional 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/WindowsDriver/MSVCSetupApi.h rename from clang/lib/Driver/ToolChains/MSVCSetupApi.h rename to llvm/include/llvm/WindowsDriver/MSVCSetupApi.h diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt --- a/llvm/lib/CMakeLists.txt +++ b/llvm/lib/CMakeLists.txt @@ -40,6 +40,7 @@ if (LLVM_INCLUDE_TESTS) add_subdirectory(Testing) endif() +add_subdirectory(WindowsDriver) add_subdirectory(WindowsManifest) set(LLVMCONFIGLIBRARYDEPENDENCIESINC "${LLVM_BINARY_DIR}/tools/llvm-config/LibraryDependencies.inc") diff --git a/llvm/lib/WindowsDriver/CMakeLists.txt b/llvm/lib/WindowsDriver/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/WindowsDriver/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_component_library(LLVMWindowsDriver + MSVCPaths.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/WindowsDriver + + LINK_LIBS + ${imported_libs} + + LINK_COMPONENTS + Option + Support + ) diff --git a/llvm/lib/WindowsDriver/MSVCPaths.cpp b/llvm/lib/WindowsDriver/MSVCPaths.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/WindowsDriver/MSVCPaths.cpp @@ -0,0 +1,710 @@ +//===-- MSVCPaths.cpp - MSVC path-parsing helpers -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/WindowsDriver/MSVCPaths.h" +#include "llvm/ADT/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/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/Support/VirtualFileSystem.h" +#include + +#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/WindowsDriver/MSVCSetupApi.h" +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); +_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); +_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); +_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); +_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); +_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); +#endif + +static std::string +getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS, + llvm::StringRef Directory) { + std::string Highest; + llvm::VersionTuple HighestTuple; + + std::error_code EC; + for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC), + DirEnd; + !EC && DirIt != DirEnd; DirIt.increment(EC)) { + auto Status = VFS.status(DirIt->path()); + if (!Status || !Status->isDirectory()) + continue; + llvm::StringRef CandidateName = llvm::sys::path::filename(DirIt->path()); + llvm::VersionTuple Tuple; + if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error. + continue; + if (Tuple > HighestTuple) { + HighestTuple = Tuple; + Highest = CandidateName.str(); + } + } + + return Highest; +} + +static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS, + const std::string &SDKPath, + std::string &SDKVersion) { + llvm::SmallString<128> IncludePath(SDKPath); + llvm::sys::path::append(IncludePath, "Include"); + SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath); + return !SDKVersion.empty(); +} + +static bool getWindowsSDKDirViaCommandLine( + llvm::vfs::FileSystem &VFS, llvm::Optional WinSdkDir, + llvm::Optional WinSdkVersion, + llvm::Optional WinSysRoot, std::string &Path, int &Major, + std::string &Version) { + if (WinSdkDir.hasValue() || WinSysRoot.hasValue()) { + // 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.hasValue()) + SDKVersion.tryParse(*WinSdkVersion); + + if (WinSysRoot.hasValue()) { + 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 { + +const char *archToWindowsSDKArch(Triple::ArchType Arch) { + switch (Arch) { + case Triple::ArchType::x86: + return "x86"; + case Triple::ArchType::x86_64: + return "x64"; + case Triple::ArchType::arm: + return "arm"; + case Triple::ArchType::aarch64: + return "arm64"; + default: + return ""; + } +} + +const char *archToLegacyVCArch(Triple::ArchType Arch) { + switch (Arch) { + case Triple::ArchType::x86: + // x86 is default in legacy VC toolchains. + // e.g. x86 libs are directly in /lib as opposed to /lib/x86. + return ""; + case Triple::ArchType::x86_64: + return "amd64"; + case Triple::ArchType::arm: + return "arm"; + case Triple::ArchType::aarch64: + return "arm64"; + default: + return ""; + } +} + +const char *archToDevDivInternalArch(Triple::ArchType Arch) { + switch (Arch) { + case Triple::ArchType::x86: + return "i386"; + case Triple::ArchType::x86_64: + return "amd64"; + case Triple::ArchType::arm: + return "arm"; + case Triple::ArchType::aarch64: + return "arm64"; + default: + return ""; + } +} + +bool appendArchToWindowsSDKLibPath(int SDKMajor, SmallString<128> LibPath, + Triple::ArchType Arch, std::string &path) { + if (SDKMajor >= 8) { + sys::path::append(LibPath, archToWindowsSDKArch(Arch)); + } else { + switch (Arch) { + // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. + case Triple::x86: + break; + case Triple::x86_64: + sys::path::append(LibPath, "x64"); + break; + case Triple::arm: + // It is not necessary to link against Windows SDK 7.x when targeting ARM. + return false; + default: + return false; + } + } + + path = std::string(LibPath.str()); + return true; +} + +std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout, + const std::string &VCToolChainPath, + Triple::ArchType TargetArch, + StringRef SubdirParent) { + const char *SubdirName; + const char *IncludeName; + switch (VSLayout) { + case ToolsetLayout::OlderVS: + SubdirName = archToLegacyVCArch(TargetArch); + IncludeName = "include"; + break; + case ToolsetLayout::VS2017OrNewer: + SubdirName = archToWindowsSDKArch(TargetArch); + IncludeName = "include"; + break; + case ToolsetLayout::DevDivInternal: + SubdirName = archToDevDivInternalArch(TargetArch); + IncludeName = "inc"; + break; + } + + SmallString<256> Path(VCToolChainPath); + if (!SubdirParent.empty()) + sys::path::append(Path, SubdirParent); + + switch (Type) { + case SubDirectoryType::Bin: + if (VSLayout == ToolsetLayout::VS2017OrNewer) { + const bool HostIsX64 = Triple(sys::getProcessTriple()).isArch64Bit(); + const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; + sys::path::append(Path, "bin", HostName, SubdirName); + } else { // OlderVS or DevDivInternal + sys::path::append(Path, "bin", SubdirName); + } + break; + case SubDirectoryType::Include: + sys::path::append(Path, IncludeName); + break; + case SubDirectoryType::Lib: + sys::path::append(Path, "lib", SubdirName); + break; + } + return std::string(Path.str()); +} + +bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath, + Triple::ArchType TargetArch, vfs::FileSystem &VFS) { + SmallString<128> TestPath(getSubDirectoryPath( + SubDirectoryType::Include, VSLayout, VCToolChainPath, TargetArch)); + sys::path::append(TestPath, "stdlib.h"); + return !VFS.exists(TestPath); +} + +bool getWindowsSDKDir(vfs::FileSystem &VFS, Optional WinSdkDir, + Optional WinSdkVersion, + Optional WinSysRoot, std::string &Path, + int &Major, std::string &WindowsSDKIncludeVersion, + std::string &WindowsSDKLibVersion) { + // Trust /winsdkdir and /winsdkversion if present. + if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, + Path, Major, WindowsSDKIncludeVersion)) { + WindowsSDKLibVersion = WindowsSDKIncludeVersion; + return true; + } + + // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to + // registry. + + // Try the Windows registry. + std::string RegistrySDKVersion; + if (!getSystemRegistryString( + "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", + "InstallationFolder", Path, &RegistrySDKVersion)) + return false; + if (Path.empty() || RegistrySDKVersion.empty()) + return false; + + WindowsSDKIncludeVersion.clear(); + WindowsSDKLibVersion.clear(); + Major = 0; + std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major); + if (Major <= 7) + return true; + if (Major == 8) { + // Windows SDK 8.x installs libraries in a folder whose names depend on the + // version of the OS you're targeting. By default choose the newest, which + // usually corresponds to the version of the OS you've installed the SDK on. + const char *Tests[] = {"winv6.3", "win8", "win7"}; + for (const char *Test : Tests) { + SmallString<128> TestPath(Path); + sys::path::append(TestPath, "Lib", Test); + if (VFS.exists(TestPath)) { + WindowsSDKLibVersion = Test; + break; + } + } + return !WindowsSDKLibVersion.empty(); + } + if (Major == 10) { + if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion)) + return false; + WindowsSDKLibVersion = WindowsSDKIncludeVersion; + return true; + } + // Unsupported SDK version + return false; +} + +bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, Optional WinSdkDir, + Optional WinSdkVersion, + Optional WinSysRoot, std::string &Path, + std::string &UCRTVersion) { + // If /winsdkdir is passed, use it as location for the UCRT too. + // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir? + int Major; + if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, + Path, Major, UCRTVersion)) + return true; + + // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to + // registry. + + // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry + // for the specific key "KitsRoot10". So do we. + if (!getSystemRegistryString( + "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", + Path, nullptr)) + return false; + + return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion); +} + +bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS, + Optional VCToolsDir, + Optional VCToolsVersion, + Optional 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.hasValue() || WinSysRoot.hasValue()) { + if (WinSysRoot.hasValue()) { + SmallString<128> ToolsPath(*WinSysRoot); + sys::path::append(ToolsPath, "VC", "Tools", "MSVC"); + std::string ToolsVersion; + if (VCToolsVersion.hasValue()) + ToolsVersion = VCToolsVersion->str(); + else + ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath); + sys::path::append(ToolsPath, ToolsVersion); + Path = std::string(ToolsPath.str()); + } else { + Path = VCToolsDir->str(); + } + VSLayout = ToolsetLayout::VS2017OrNewer; + return true; + } + return false; +} + +bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path, + ToolsetLayout &VSLayout) { + // These variables are typically set by vcvarsall.bat + // when launching a developer command prompt. + if (Optional VCToolsInstallDir = + sys::Process::GetEnv("VCToolsInstallDir")) { + // This is only set by newer Visual Studios, and it leads straight to + // the toolchain directory. + Path = std::move(*VCToolsInstallDir); + VSLayout = ToolsetLayout::VS2017OrNewer; + return true; + } + if (Optional VCInstallDir = + sys::Process::GetEnv("VCINSTALLDIR")) { + // If the previous variable isn't set but this one is, then we've found + // an older Visual Studio. This variable is set by newer Visual Studios too, + // so this check has to appear second. + // In older Visual Studios, the VC directory is the toolchain. + Path = std::move(*VCInstallDir); + VSLayout = ToolsetLayout::OlderVS; + return true; + } + + // We couldn't find any VC environment variables. Let's walk through PATH and + // see if it leads us to a VC toolchain bin directory. If it does, pick the + // first one that we find. + if (Optional PathEnv = sys::Process::GetEnv("PATH")) { + SmallVector PathEntries; + StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator); + for (StringRef PathEntry : PathEntries) { + if (PathEntry.empty()) + continue; + + SmallString<256> ExeTestPath; + + // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. + ExeTestPath = PathEntry; + sys::path::append(ExeTestPath, "cl.exe"); + if (!VFS.exists(ExeTestPath)) + continue; + + // cl.exe existing isn't a conclusive test for a VC toolchain; clang also + // has a cl.exe. So let's check for link.exe too. + ExeTestPath = PathEntry; + sys::path::append(ExeTestPath, "link.exe"); + if (!VFS.exists(ExeTestPath)) + continue; + + // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. + StringRef TestPath = PathEntry; + bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); + if (!IsBin) { + // Strip any architecture subdir like "amd64". + TestPath = sys::path::parent_path(TestPath); + IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); + } + if (IsBin) { + StringRef ParentPath = sys::path::parent_path(TestPath); + StringRef ParentFilename = sys::path::filename(ParentPath); + if (ParentFilename.equals_insensitive("VC")) { + Path = std::string(ParentPath); + VSLayout = ToolsetLayout::OlderVS; + return true; + } + if (ParentFilename.equals_insensitive("x86ret") || + ParentFilename.equals_insensitive("x86chk") || + ParentFilename.equals_insensitive("amd64ret") || + ParentFilename.equals_insensitive("amd64chk")) { + Path = std::string(ParentPath); + VSLayout = ToolsetLayout::DevDivInternal; + return true; + } + + } else { + // This could be a new (>=VS2017) toolchain. If it is, we should find + // path components with these prefixes when walking backwards through + // the path. + // Note: empty strings match anything. + StringRef ExpectedPrefixes[] = {"", "Host", "bin", "", + "MSVC", "Tools", "VC"}; + + auto It = sys::path::rbegin(PathEntry); + auto End = sys::path::rend(PathEntry); + for (StringRef Prefix : ExpectedPrefixes) { + if (It == End) + goto NotAToolChain; + if (!It->startswith_insensitive(Prefix)) + goto NotAToolChain; + ++It; + } + + // We've found a new toolchain! + // Back up 3 times (/bin/Host/arch) to get the root path. + StringRef ToolChainPath(PathEntry); + for (int i = 0; i < 3; ++i) + ToolChainPath = sys::path::parent_path(ToolChainPath); + + Path = std::string(ToolChainPath); + VSLayout = ToolsetLayout::VS2017OrNewer; + return true; + } + + NotAToolChain: + continue; + } + } + return false; +} + +bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, std::string &Path, + ToolsetLayout &VSLayout) { +#if !defined(USE_MSVC_SETUP_API) + return false; +#else + // FIXME: This really should be done once in the top-level program's main + // function, as it may have already been initialized with a different + // threading model otherwise. + sys::InitializeCOMRAII COM(sys::COMThreadingMode::SingleThreaded); + HRESULT HR; + + // _com_ptr_t will throw a _com_error if a COM calls fail. + // The LLVM coding standards forbid exception handling, so we'll have to + // stop them from being thrown in the first place. + // The destructor will put the regular error handler back when we leave + // this scope. + struct SuppressCOMErrorsRAII { + static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {} + + SuppressCOMErrorsRAII() { _set_com_error_handler(handler); } + + ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); } + + } COMErrorSuppressor; + + ISetupConfigurationPtr Query; + HR = Query.CreateInstance(__uuidof(SetupConfiguration)); + if (FAILED(HR)) + return false; + + IEnumSetupInstancesPtr EnumInstances; + HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances); + if (FAILED(HR)) + return false; + + ISetupInstancePtr Instance; + HR = EnumInstances->Next(1, &Instance, nullptr); + if (HR != S_OK) + return false; + + ISetupInstancePtr NewestInstance; + 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); + sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", + "Microsoft.VCToolsVersion.default.txt"); + + auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath); + if (!ToolsVersionFile) + return false; + + SmallString<256> ToolchainPath(VCRootPath); + sys::path::append(ToolchainPath, "Tools", "MSVC", + ToolsVersionFile->get()->getBuffer().rtrim()); + auto Status = VFS.status(ToolchainPath); + if (!Status || !Status->isDirectory()) + return false; + + Path = std::string(ToolchainPath.str()); + VSLayout = ToolsetLayout::VS2017OrNewer; + return true; +#endif +} + +bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout) { + std::string VSInstallPath; + if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)", + "InstallDir", VSInstallPath, nullptr) || + getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)", + "InstallDir", VSInstallPath, nullptr)) { + if (!VSInstallPath.empty()) { + SmallString<256> VCPath(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/clang/lib/Driver/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/Driver/BUILD.gn --- a/llvm/utils/gn/secondary/clang/lib/Driver/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/Driver/BUILD.gn @@ -18,6 +18,7 @@ "//llvm/lib/BinaryFormat", "//llvm/lib/Option", "//llvm/lib/Support", + "//llvm/lib/WindowsDriver", ] public_deps = [ # public_dep because public header Options.h includes generated Options.inc. diff --git a/llvm/utils/gn/secondary/lld/COFF/BUILD.gn b/llvm/utils/gn/secondary/lld/COFF/BUILD.gn --- a/llvm/utils/gn/secondary/lld/COFF/BUILD.gn +++ b/llvm/utils/gn/secondary/lld/COFF/BUILD.gn @@ -23,6 +23,7 @@ "//llvm/lib/Support", "//llvm/lib/Target:TargetsToBuild", "//llvm/lib/ToolDrivers/llvm-lib:LibDriver", + "//llvm/lib/WindowsDriver", "//llvm/lib/WindowsManifest", ] sources = [ diff --git a/llvm/utils/gn/secondary/llvm/lib/WindowsDriver/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/WindowsDriver/BUILD.gn new file mode 100644 --- /dev/null +++ b/llvm/utils/gn/secondary/llvm/lib/WindowsDriver/BUILD.gn @@ -0,0 +1,8 @@ +static_library("WindowsDriver") { + output_name = "LLVMWindowsDriver" + deps = [ + "//llvm/lib/Option", + "//llvm/lib/Support", + ] + sources = [ "MSVCPaths.cpp" ] +}