Index: lib/Driver/ToolChains/MSVC.h =================================================================== --- lib/Driver/ToolChains/MSVC.h +++ lib/Driver/ToolChains/MSVC.h @@ -14,6 +14,7 @@ #include "clang/Driver/Compilation.h" #include "clang/Driver/Tool.h" #include "clang/Driver/ToolChain.h" +#include "llvm/ADT/SmallString.h" namespace clang { namespace driver { @@ -117,6 +118,7 @@ SanitizerMask getSupportedSanitizers() const override; void printVerboseInfo(raw_ostream &OS) const override; + llvm::SmallString<128> getMSVCLinkPath() const { return linkPath; } protected: void AddSystemIncludeWithSubfolder(const llvm::opt::ArgList &DriverArgs, @@ -128,10 +130,14 @@ Tool *buildLinker() const override; Tool *buildAssembler() const override; + bool findVCToolChainViaEnvironment(std::string &Path, + bool &IsVS2017OrNewer); private: std::string VCToolChainPath; bool IsVS2017OrNewer = false; CudaInstallationDetector CudaInstallation; + llvm::SmallString<128> linkPath; + mutable VersionTuple MSVT; }; } // end namespace toolchains Index: lib/Driver/ToolChains/MSVC.cpp =================================================================== --- lib/Driver/ToolChains/MSVC.cpp +++ lib/Driver/ToolChains/MSVC.cpp @@ -69,14 +69,15 @@ using namespace clang; using namespace llvm::opt; +static VersionTuple getMSVCVersionFromExe(const std::string &BinDir); // 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); // Check various environment variables to try and find a toolchain. -static bool findVCToolChainViaEnvironment(std::string &Path, - bool &IsVS2017OrNewer) { +bool MSVCToolChain::findVCToolChainViaEnvironment(std::string &Path, + bool &IsVS2017OrNewer) { // These variables are typically set by vcvarsall.bat // when launching a developer command prompt. if (llvm::Optional VCToolsInstallDir = @@ -117,6 +118,10 @@ if (!llvm::sys::fs::exists(ExeTestPath)) continue; + MSVT = getMSVCVersionFromExe(PathEntry); + if (MSVT.empty()) + 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; @@ -124,53 +129,31 @@ if (!llvm::sys::fs::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_lower("bin"); - if (!IsBin) { - // Strip any architecture subdir like "amd64". + // The install directory will be the parent of the include directory, + // which must be an higher up in the path from cl.exe. + llvm::SmallString<256> TestPath = PathEntry; + while(!TestPath.empty()) { TestPath = llvm::sys::path::parent_path(TestPath); - IsBin = llvm::sys::path::filename(TestPath).equals_lower("bin"); - } - if (IsBin) { - llvm::StringRef ParentPath = llvm::sys::path::parent_path(TestPath); - if (llvm::sys::path::filename(ParentPath) == "VC") { - Path = ParentPath; - IsVS2017OrNewer = false; - return true; + llvm::SmallString<256> IncludePath = TestPath; + llvm::sys::path::append(IncludePath, "include"); + if (llvm::sys::fs::exists(IncludePath)) { + Path = TestPath.str(); + break; } - - } 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(Prefix)) - goto NotAToolChain; - ++It; + IncludePath = TestPath; + llvm::sys::path::append(IncludePath, "inc"); + if (llvm::sys::fs::exists(IncludePath)) { + Path = TestPath.str(); + break; } - - // 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 = ToolChainPath; - IsVS2017OrNewer = true; - return true; } - NotAToolChain: - continue; + if (Path.empty()) + return false; + + linkPath = ExeTestPath; + IsVS2017OrNewer = (MSVT.getMajor() >= 19 && MSVT.getMinor()); + return true; } } return false; @@ -467,7 +450,9 @@ // If we're using the MSVC linker, it's not sufficient to just use link // from the program PATH, because other environments like GnuWin32 install // their own link.exe which may come first. - linkPath = FindVisualStudioExecutable(TC, "link.exe"); + linkPath = TC.getMSVCLinkPath(); + if (linkPath.empty()) + linkPath = FindVisualStudioExecutable(TC, "link.exe"); #ifdef USE_WIN32 // When cross-compiling with VS2017 or newer, link.exe expects to have @@ -1220,6 +1205,8 @@ VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D, const ArgList &Args) const { + if (!this->MSVT.empty()) + return this->MSVT; bool IsWindowsMSVC = getTriple().isWindowsMSVCEnvironment(); VersionTuple MSVT = ToolChain::computeMSVCVersion(D, Args); if (MSVT.empty()) @@ -1241,7 +1228,8 @@ types::ID InputType) const { // The MSVC version doesn't care about the architecture, even though it // may look at the triple internally. - VersionTuple MSVT = computeMSVCVersion(/*D=*/nullptr, Args); + if (MSVT.empty()) + MSVT = computeMSVCVersion(/*D=*/nullptr, Args); MSVT = VersionTuple(MSVT.getMajor(), MSVT.getMinor().getValueOr(0), MSVT.getSubminor().getValueOr(0));