diff options
Diffstat (limited to 'clang/lib/Driver/ToolChains/MSVC.cpp')
| -rw-r--r-- | clang/lib/Driver/ToolChains/MSVC.cpp | 819 |
1 files changed, 91 insertions, 728 deletions
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp index 18cef288f018..14ebe38ee191 100644 --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -40,91 +40,12 @@ #include <windows.h> #endif -#ifdef _MSC_VER -// Don't support SetupApi on MinGW. -#define USE_MSVC_SETUP_API - -// Make sure this comes before MSVCSetupApi.h -#include <comdef.h> - -#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 @@ static bool canExecute(llvm::vfs::FileSystem &VFS, StringRef Path) { 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<std::string> 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<std::string> 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<std::string> PathEnv = - llvm::sys::Process::GetEnv("PATH")) { - llvm::SmallVector<llvm::StringRef, 8> 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<uint64_t> 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 bool findVCToolChainViaRegistry(std::string &Path, static std::string FindVisualStudioExecutable(const ToolChain &TC, const char *Exe) { const auto &MSVC = static_cast<const toolchains::MSVCToolChain &>(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); } @@ -448,7 +81,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, Args.MakeArgString(std::string("-out:") + Output.getFilename())); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) && - !C.getDriver().IsCLMode()) { + !C.getDriver().IsCLMode() && !C.getDriver().IsFlangMode()) { CmdArgs.push_back("-defaultlib:libcmt"); CmdArgs.push_back("-defaultlib:oldnames"); } @@ -469,7 +102,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, // 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 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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, @@ -499,6 +130,16 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, Args.MakeArgString(std::string("-libpath:") + WindowsSdkLibPath)); } + if (C.getDriver().IsFlangMode()) { + addFortranRuntimeLibraryPath(TC, Args, CmdArgs); + addFortranRuntimeLibs(TC, CmdArgs); + + // Inform the MSVC linker that we're generating a console application, i.e. + // one with `main` as the "user-defined" entry point. The `main` function is + // defined in flang's runtime libraries. + CmdArgs.push_back("/subsystem:console"); + } + // Add the compiler-rt library directories to libpath if they exist to help // the linker find the various sanitizer, builtin, and profiling runtimes. for (const auto &LibPath : TC.getLibraryPaths()) { @@ -655,6 +296,8 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, A.renderAsInput(Args, CmdArgs); } + addHIPRuntimeLibArgs(TC, Args, CmdArgs); + TC.addProfileRTLibs(Args, CmdArgs); std::vector<const char *> Environment; @@ -695,7 +338,7 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, // 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 +373,12 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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 +411,29 @@ MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple, if (getDriver().getInstalledDir() != getDriver().Dir) getProgramPaths().push_back(getDriver().Dir); + Optional<llvm::StringRef> 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 { @@ -802,8 +459,8 @@ bool MSVCToolChain::IsUnwindTablesDefault(const ArgList &Args) const { // All non-x86_32 Windows targets require unwind tables. However, LLVM // doesn't know how to generate them for all targets, so only enable // the ones that are actually implemented. - return getArch() == llvm::Triple::x86_64 || - getArch() == llvm::Triple::aarch64; + return getArch() == llvm::Triple::x86_64 || getArch() == llvm::Triple::arm || + getArch() == llvm::Triple::thumb || getArch() == llvm::Triple::aarch64; } bool MSVCToolChain::isPICDefault() const { @@ -830,360 +487,60 @@ void MSVCToolChain::AddHIPIncludeArgs(const ArgList &DriverArgs, RocmInstallation.AddHIPIncludeArgs(DriverArgs, CC1Args); } +void MSVCToolChain::AddHIPRuntimeLibArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + CmdArgs.append({Args.MakeArgString(StringRef("-libpath:") + + RocmInstallation.getLibPath()), + "amdhip64.lib"}); +} + void MSVCToolChain::printVerboseInfo(raw_ostream &OS) const { CudaInstallation.print(OS); 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()); +MSVCToolChain::getSubDirectoryPath(llvm::SubDirectoryType Type, + llvm::StringRef SubdirParent) const { + return llvm::getSubDirectoryPath(Type, VSLayout, VCToolChainPath, getArch(), + SubdirParent); } -#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<BYTE> buffer(valueSize); - result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0], - &valueSize); - if (result == ERROR_SUCCESS) { - std::wstring WideValue(reinterpret_cast<const wchar_t *>(buffer.data()), - valueSize / sizeof(wchar_t)); - if (valueSize && WideValue.back() == L'\0') { - WideValue.pop_back(); - } - // The destination buffer must be empty as an invariant of the conversion - // function; but this function is sometimes called in a loop that passes in - // the same buffer, however. Simply clear it out so we can overwrite it. - value.clear(); - return llvm::convertWideToUTF8(WideValue, value); - } - return false; -} -#endif - -/// Read registry string. -/// This also supports a means to look for high-versioned keys by use -/// of a $VERSION placeholder in the key path. -/// $VERSION in the key path is a placeholder for the version number, -/// causing the highest value path to be searched for and used. -/// I.e. "SOFTWARE\\Microsoft\\VisualStudio\\$VERSION". -/// There can be additional characters in the component. Only the numeric -/// characters are compared. This function only searches HKLM. -static bool getSystemRegistryString(const char *keyPath, const char *valueName, - std::string &value, std::string *phValue) { -#ifndef _WIN32 - return false; -#else - HKEY hRootKey = HKEY_LOCAL_MACHINE; - HKEY hKey = NULL; - long lResult; - bool returnValue = false; - - const char *placeHolder = strstr(keyPath, "$VERSION"); - std::string bestName; - // If we have a $VERSION placeholder, do the highest-version search. - if (placeHolder) { - const char *keyEnd = placeHolder - 1; - const char *nextKey = placeHolder; - // Find end of previous key. - while ((keyEnd > keyPath) && (*keyEnd != '\\')) - keyEnd--; - // Find end of key containing $VERSION. - while (*nextKey && (*nextKey != '\\')) - nextKey++; - size_t partialKeyLength = keyEnd - keyPath; - char partialKey[256]; - if (partialKeyLength >= sizeof(partialKey)) - partialKeyLength = sizeof(partialKey) - 1; - strncpy(partialKey, keyPath, partialKeyLength); - partialKey[partialKeyLength] = '\0'; - HKEY hTopKey = NULL; - lResult = RegOpenKeyExA(hRootKey, partialKey, 0, KEY_READ | KEY_WOW64_32KEY, - &hTopKey); - if (lResult == ERROR_SUCCESS) { - char keyName[256]; - double bestValue = 0.0; - DWORD index, size = sizeof(keyName) - 1; - for (index = 0; RegEnumKeyExA(hTopKey, index, keyName, &size, NULL, NULL, - NULL, NULL) == ERROR_SUCCESS; - index++) { - const char *sp = keyName; - while (*sp && !isDigit(*sp)) - sp++; - if (!*sp) - continue; - const char *ep = sp + 1; - while (*ep && (isDigit(*ep) || (*ep == '.'))) - ep++; - char numBuf[32]; - strncpy(numBuf, sp, sizeof(numBuf) - 1); - numBuf[sizeof(numBuf) - 1] = '\0'; - double dvalue = strtod(numBuf, NULL); - if (dvalue > bestValue) { - // Test that InstallDir is indeed there before keeping this index. - // Open the chosen key path remainder. - bestName = keyName; - // Append rest of key. - bestName.append(nextKey); - lResult = RegOpenKeyExA(hTopKey, bestName.c_str(), 0, - KEY_READ | KEY_WOW64_32KEY, &hKey); - if (lResult == ERROR_SUCCESS) { - if (readFullStringValue(hKey, valueName, value)) { - bestValue = dvalue; - if (phValue) - *phValue = bestName; - returnValue = true; - } - RegCloseKey(hKey); - } - } - size = sizeof(keyName) - 1; - } - RegCloseKey(hTopKey); - } - } else { - lResult = - RegOpenKeyExA(hRootKey, keyPath, 0, KEY_READ | KEY_WOW64_32KEY, &hKey); - if (lResult == ERROR_SUCCESS) { - if (readFullStringValue(hKey, valueName, value)) - returnValue = true; - if (phValue) - phValue->clear(); - RegCloseKey(hKey); - } - } - return returnValue; -#endif // _WIN32 +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 +549,12 @@ bool MSVCToolChain::getUniversalCRTLibraryPath(const ArgList &Args, 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 +672,17 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, // 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 +692,9 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 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. @@ -1348,7 +710,7 @@ void MSVCToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, if (major >= 10) { llvm::VersionTuple Tuple; if (!Tuple.tryParse(windowsSDKIncludeVersion) && - Tuple.getSubminor().getValueOr(0) >= 17134) { + Tuple.getSubminor().value_or(0) >= 17134) { AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir, "Include", windowsSDKIncludeVersion, "cppwinrt"); @@ -1389,12 +751,13 @@ VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D, 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)) { - // -fms-compatibility-version=19.14 is default, aka 2017, 15.7 - MSVT = VersionTuple(19, 14); + // -fms-compatibility-version=19.20 is default, aka 2019, 16.x + MSVT = VersionTuple(19, 20); } return MSVT; } @@ -1405,8 +768,8 @@ MSVCToolChain::ComputeEffectiveClangTriple(const ArgList &Args, // The MSVC version doesn't care about the architecture, even though it // may look at the triple internally. VersionTuple MSVT = computeMSVCVersion(/*D=*/nullptr, Args); - MSVT = VersionTuple(MSVT.getMajor(), MSVT.getMinor().getValueOr(0), - MSVT.getSubminor().getValueOr(0)); + MSVT = VersionTuple(MSVT.getMajor(), MSVT.getMinor().value_or(0), + MSVT.getSubminor().value_or(0)); // For the rest of the triple, however, a computed architecture name may // be needed. @@ -1619,7 +982,7 @@ void MSVCToolChain::addClangTargetOptions( Action::OffloadKind DeviceOffloadKind) const { // MSVC STL kindly allows removing all usages of typeid by defining // _HAS_STATIC_RTTI to 0. Do so, when compiling with -fno-rtti - if (DriverArgs.hasArg(options::OPT_fno_rtti, options::OPT_frtti, - /*Default=*/false)) + if (DriverArgs.hasFlag(options::OPT_fno_rtti, options::OPT_frtti, + /*Default=*/false)) CC1Args.push_back("-D_HAS_STATIC_RTTI=0"); } |
