hamzasood updated this revision to Diff 83473.
hamzasood added a comment.

- Modify the environment in some cases in buildLinker to keep link.exe happy.

Thanks @rnk for taking the time to look at this. As suggested:

- Replaced the confusing unique_ptr usage with a struct.
- Renamed llvmArchToSubDirectoryName to llvmArchToWindowsSDKArch.
- Replaced the nested function in getSubDirectoryPath with a static helper.

With regard to _set_com_error_handler, it's a documented 
<https://msdn.microsoft.com/en-us/library/ff357023(v=vs.90).aspx> function so 
it is supported. It's not part of the actual COM API, just a wrapper library 
that sits on top of it. That library is a header only library with inline 
functions, so you can see how it's used. It checks the HRESULTs before 
returning them and if the result isn't successful then it calls the error 
handler; the original HRESULT still gets returned afterwards. You can also see 
from the headers that it isn't being relied on to process any kind of internal 
error, so it should be perfectly safe to block out for a bit.


https://reviews.llvm.org/D28365

Files:
  include/clang/Basic/DiagnosticDriverKinds.td
  lib/Driver/MSVCToolChain.cpp
  lib/Driver/ToolChains.h
  lib/Driver/Tools.cpp

Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -10809,16 +10809,10 @@
                                               const char *Exe,
                                               const char *ClangProgramPath) {
   const auto &MSVC = static_cast<const toolchains::MSVCToolChain &>(TC);
-  std::string visualStudioBinDir;
-  if (MSVC.getVisualStudioBinariesFolder(ClangProgramPath,
-                                         visualStudioBinDir)) {
-    SmallString<128> FilePath(visualStudioBinDir);
-    llvm::sys::path::append(FilePath, Exe);
-    if (llvm::sys::fs::can_execute(FilePath.c_str()))
-      return FilePath.str();
-  }
-
-  return Exe;
+  SmallString<128> FilePath(MSVC.getSubDirectoryPath(toolchains::MSVCToolChain
+                                                     ::SubDirectoryType::Bin));
+  llvm::sys::path::append(FilePath, Exe);
+  return (llvm::sys::fs::can_execute(FilePath) ? FilePath.str() : Exe);
 }
 
 void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
@@ -10843,33 +10837,17 @@
     // 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.
-    std::string VisualStudioDir;
     const auto &MSVC = static_cast<const toolchains::MSVCToolChain &>(TC);
-    if (MSVC.getVisualStudioInstallDir(VisualStudioDir)) {
-      SmallString<128> LibDir(VisualStudioDir);
-      llvm::sys::path::append(LibDir, "VC", "lib");
-      switch (MSVC.getArch()) {
-      case llvm::Triple::x86:
-        // x86 just puts the libraries directly in lib
-        break;
-      case llvm::Triple::x86_64:
-        llvm::sys::path::append(LibDir, "amd64");
-        break;
-      case llvm::Triple::arm:
-        llvm::sys::path::append(LibDir, "arm");
-        break;
-      default:
-        break;
-      }
-      CmdArgs.push_back(
-          Args.MakeArgString(std::string("-libpath:") + LibDir.c_str()));
+    CmdArgs.push_back(Args.MakeArgString(
+                          std::string("-libpath:")
+                          + MSVC.getSubDirectoryPath(toolchains::MSVCToolChain
+                                                     ::SubDirectoryType::Lib)));
 
-      if (MSVC.useUniversalCRT(VisualStudioDir)) {
-        std::string UniversalCRTLibPath;
-        if (MSVC.getUniversalCRTLibraryPath(UniversalCRTLibPath))
-          CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") +
-                                               UniversalCRTLibPath));
-      }
+    if (MSVC.useUniversalCRT()) {
+      std::string UniversalCRTLibPath;
+      if (MSVC.getUniversalCRTLibraryPath(UniversalCRTLibPath))
+        CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:")
+                                             + UniversalCRTLibPath));
     }
 
     std::string WindowsSdkLibPath;
Index: lib/Driver/ToolChains.h
===================================================================
--- lib/Driver/ToolChains.h
+++ lib/Driver/ToolChains.h
@@ -1155,6 +1155,15 @@
   bool isPIEDefault() const override;
   bool isPICDefaultForced() const override;
 
+  enum class SubDirectoryType {
+    Bin,
+    Include,
+    Lib,
+  };
+  std::string getSubDirectoryPath(SubDirectoryType Type) const;
+  std::string getSubDirectoryPath(SubDirectoryType Type,
+                                  llvm::Triple::ArchType TargetArch) const;
+
   void
   AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs,
                             llvm::opt::ArgStringList &CC1Args) const override;
@@ -1163,19 +1172,12 @@
       llvm::opt::ArgStringList &CC1Args) const override;
 
   void AddCudaIncludeArgs(const llvm::opt::ArgList &DriverArgs,
-                          llvm::opt::ArgStringList &CC1Args) const override;
+      llvm::opt::ArgStringList &CC1Args) const override;
 
-  bool getWindowsSDKDir(std::string &path, int &major,
-                        std::string &windowsSDKIncludeVersion,
-                        std::string &windowsSDKLibVersion) const;
   bool getWindowsSDKLibraryPath(std::string &path) const;
   /// \brief Check if Universal CRT should be used if available
-  bool useUniversalCRT(std::string &visualStudioDir) const;
-  bool getUniversalCRTSdkDir(std::string &path, std::string &ucrtVersion) const;
+  bool useUniversalCRT() const;
   bool getUniversalCRTLibraryPath(std::string &path) const;
-  bool getVisualStudioInstallDir(std::string &path) const;
-  bool getVisualStudioBinariesFolder(const char *clangProgramPath,
-                                     std::string &path) const;
   VersionTuple
   computeMSVCVersion(const Driver *D,
                      const llvm::opt::ArgList &Args) const override;
@@ -1196,7 +1198,11 @@
 
   Tool *buildLinker() const override;
   Tool *buildAssembler() const override;
+
 private:
+  std::string VCToolChainPath;
+  bool IsVS2017OrNewer;
+
   VersionTuple getMSVCVersionFromTriple() const;
   VersionTuple getMSVCVersionFromExe() const;
 
Index: lib/Driver/MSVCToolChain.cpp
===================================================================
--- lib/Driver/MSVCToolChain.cpp
+++ lib/Driver/MSVCToolChain.cpp
@@ -23,16 +23,20 @@
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
 #include <cstdio>
 
-// Include the necessary headers to interface with the Windows registry and
-// environment.
 #if defined(LLVM_ON_WIN32)
-#define USE_WIN32
+  #define USE_WIN32
+  #if 0
+    #define USE_VS_SETUP_CONFIG
+  #endif
 #endif
 
+// Include the necessary headers to interface with the Windows registry and
+// environment.
 #ifdef USE_WIN32
   #define WIN32_LEAN_AND_MEAN
   #define NOGDI
@@ -42,20 +46,281 @@
   #include <windows.h>
 #endif
 
+// Include the headers needed for the setup config COM stuff and define
+// smart pointers for the interfaces we need.
+#ifdef USE_VS_SETUP_CONFIG
+  #include "clang/Basic/VirtualFileSystem.h"
+  #include "llvm/Support/COM.h"
+  #include <comdef.h>
+  #include <Setup.Configuration.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;
 using namespace llvm::opt;
 
-MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
+// 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);
+
+// Attempts to find the "best" usable VC toolchain.
+static bool findVCToolChainPath(std::string &Path, bool &IsVS2017OrNewer) {
+  // Check the environment first, since that's probably the user telling us
+  // what they want to use. 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);
+    IsVS2017OrNewer = true;
+    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);
+    IsVS2017OrNewer = false;
+    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 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;
+
+      // If cl.exe doesn't exist, then this definitely isn't a VC toolchain.
+      llvm::SmallString<256> PotentialClPath(PathEntry);
+      llvm::sys::path::append(PotentialClPath, "cl.exe");
+      if (!llvm::sys::fs::exists(PotentialClPath))
+        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.
+      llvm::SmallString<256> PotentialLinkPath(PathEntry);
+      llvm::sys::path::append(PotentialLinkPath, "link.exe");
+      if (!llvm::sys::fs::exists(PotentialLinkPath))
+        continue;
+      
+      // whatever/VC/bin --> old toolchain, VC dir is toolchain dir.
+      if (llvm::sys::path::filename(PathEntry) == "bin") {
+        llvm::StringRef ParentPath = llvm::sys::path::parent_path(PathEntry);
+        if (llvm::sys::path::filename(ParentPath) == "VC") {
+          Path = ParentPath;
+          IsVS2017OrNewer = false;
+          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::SmallVector<llvm::StringRef, 7> ExpectedPrefixes =
+            {"", "Host", "bin", "", "MSVC", "Tools", "VC"};
+
+        llvm::sys::path::reverse_iterator
+            It = llvm::sys::path::rbegin(PathEntry),
+            End = llvm::sys::path::rend(PathEntry);
+        for (llvm::StringRef Prefix : ExpectedPrefixes) {
+          if (It == End) goto NotAToolChain;
+          if (!It->startswith(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 = ToolChainPath;
+        IsVS2017OrNewer = true;
+        return true;
+      }
+
+    NotAToolChain:
+      ;
+    }
+  }
+
+  // We reach this point if the environment doesn't lead to a toolchain,
+  // and so we probably aren't being run from a developer command prompt.
+  // The plan from now is to find the newest Visual Studio version we can, and
+  // use its default VC toolchain..
+  // TODO: Perhaps have an option to let the user select the toolchain that
+  //       they want to use?
+  
+#ifdef USE_VS_SETUP_CONFIG
+  // Query the Setup Config server for installs.
+  // This is the preferred way to discover new Visual Studios, as they're no
+  // longer listed in the registry.
+  // An explicit scope is established to ensure the COM pointers get released
+  // as soon as we're done with them.
+  {
+    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 {
+      SuppressCOMErrorsRAII() {
+        _set_com_error_handler([](HRESULT, IErrorInfo *) { }); }
+      ~SuppressCOMErrorsRAII() {
+        _set_com_error_handler(_com_raise_error); }
+    } COMErrorSuppressor;
+
+    ISetupConfigurationPtr Query;
+    HR = Query.CreateInstance(__uuidof(SetupConfiguration));
+    if (FAILED(HR)) goto ConfigQueryUnsuccessful;
+
+    IEnumSetupInstancesPtr EnumInstances;
+    HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances);
+    if (FAILED(HR)) goto ConfigQueryUnsuccessful;
+
+    ISetupInstancePtr Instance;
+    HR = EnumInstances->Next(1, &Instance, nullptr);
+    if (HR != S_OK) goto ConfigQueryUnsuccessful;
+
+    ISetupInstancePtr NewestInstance(Instance);
+    uint64_t NewestVersionNum;
+    {
+      bstr_t VersionString;
+      HR = NewestInstance->GetInstallationVersion(VersionString.GetAddress());
+      if (FAILED(HR)) goto ConfigQueryUnsuccessful;
+      HR = ISetupHelperPtr(Query)->ParseVersion(VersionString,
+                                                &NewestVersionNum);
+      if (FAILED(HR)) goto ConfigQueryUnsuccessful;
+    }
+
+    while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK) {
+      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 (VersionNum > NewestVersionNum) {
+        NewestInstance = Instance;
+        NewestVersionNum = VersionNum;
+      }
+    }
+
+    bstr_t VCPathWide;
+    HR = NewestInstance->ResolvePath(L"VC",
+                                     VCPathWide.GetAddress());
+    if (FAILED(HR)) goto ConfigQueryUnsuccessful;
+
+    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 =
+        clang::vfs::getRealFileSystem()->getBufferForFile(ToolsVersionFilePath);
+    if (!ToolsVersionFile)
+      goto ConfigQueryUnsuccessful;
+
+    llvm::SmallString<256> ToolchainPath(VCRootPath);
+    llvm::sys::path::append(ToolchainPath,
+                            "Tools",
+                            "MSVC",
+                            ToolsVersionFile->get()->getBuffer().rtrim());
+    if (!llvm::sys::fs::is_directory(ToolchainPath))
+      goto ConfigQueryUnsuccessful;
+
+    Path = ToolchainPath.str();
+    IsVS2017OrNewer = true;
+    return true;
+  }
+
+ConfigQueryUnsuccessful:
+  ;
+#endif /*USE_VS_SETUP_CONFIG*/
+
+  // If the environment checks and Setup Config queries are unsuccessful,
+  // our last hope is that we can find an install in the registry.
+  // VS2017 and newer don't get added to the registry. So if we find something
+  // here, we know that it's an older version.
+  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 = VCPath.str();
+      IsVS2017OrNewer = false;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+MSVCToolChain::MSVCToolChain(const Driver &D, const llvm::Triple& Triple,
                              const ArgList &Args)
     : ToolChain(D, Triple, Args), CudaInstallation(D, Triple, Args) {
   getProgramPaths().push_back(getDriver().getInstalledDir());
   if (getDriver().getInstalledDir() != getDriver().Dir)
     getProgramPaths().push_back(getDriver().Dir);
+  findVCToolChainPath(VCToolChainPath, IsVS2017OrNewer);
 }
 
 Tool *MSVCToolChain::buildLinker() const {
+  if (VCToolChainPath.empty()) {
+    getDriver().Diag(clang::diag::err_drv_msvc_not_found);
+    return nullptr;
+  }
+#ifdef USE_WIN32
+  if (IsVS2017OrNewer) {
+    // When cross compiling with VS2017 or newer, link.exe expects to have its
+    // containing bin directory at the top of PATH, followed by the native
+    // target bin dir.
+    // e.g. when compiling for x86 on an x64 host, PATH should start with:
+    // /bin/HostX64/x86;/bin/HostX64/x64
+    llvm::Triple Host(llvm::sys::getProcessTriple());
+    if (Host.getArch() != this->getArch()) {
+      llvm::Optional<std::string> PathEnv = llvm::sys::Process::GetEnv("PATH");
+      std::wstring NewPath;
+      llvm::ConvertUTF8toWide((
+          getSubDirectoryPath(SubDirectoryType::Bin)
+        + llvm::Twine(llvm::sys::EnvPathSeparator)
+        + getSubDirectoryPath(SubDirectoryType::Bin, Host.getArch())
+        + (PathEnv ? (llvm::Twine(llvm::sys::EnvPathSeparator) + *PathEnv) : "")
+      ).str(), NewPath);
+      SetEnvironmentVariable(L"PATH", NewPath.c_str());
+    }
+  }
+#endif
   return new tools::visualstudio::Linker(*this);
 }
 
@@ -103,6 +368,82 @@
   CudaInstallation.print(OS);
 }
 
+// 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";
+  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";
+  default:
+    return "";
+  }
+}
+
+// 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::Triple::ArchType TargetArch) const {
+  llvm::SmallString<256> Path(VCToolChainPath);
+  switch (Type) {
+  case SubDirectoryType::Bin:
+    if (IsVS2017OrNewer) {
+      bool HostIsX64 = llvm::Triple(llvm::sys::getProcessTriple()).isArch64Bit();
+      llvm::sys::path::append(Path,
+        "bin",
+        (HostIsX64 ? "HostX64" : "HostX86"),
+        llvmArchToWindowsSDKArch(TargetArch));
+    }
+    else {
+      llvm::sys::path::append(Path,
+        "bin",
+        llvmArchToLegacyVCArch(TargetArch));
+    }
+    break;
+  case SubDirectoryType::Include:
+    llvm::sys::path::append(Path, "include");
+    break;
+  case SubDirectoryType::Lib:
+    llvm::sys::path::append(Path,
+      "lib",
+      IsVS2017OrNewer
+      ? llvmArchToWindowsSDKArch(TargetArch)
+      : llvmArchToLegacyVCArch(TargetArch));
+    break;
+  }
+  return Path.str();
+}
+
+// Same as the above function, but for the arch in this toolchain's triple.
+std::string MSVCToolChain::getSubDirectoryPath(SubDirectoryType Type) const {
+  return getSubDirectoryPath(Type, getArch());
+}
+
 #ifdef USE_WIN32
 static bool readFullStringValue(HKEY hkey, const char *valueName,
                                 std::string &value) {
@@ -232,27 +573,12 @@
 #endif // USE_WIN32
 }
 
-// Convert LLVM's ArchType
-// to the corresponding name of Windows SDK libraries subfolder
-static StringRef getWindowsSDKArch(llvm::Triple::ArchType Arch) {
-  switch (Arch) {
-  case llvm::Triple::x86:
-    return "x86";
-  case llvm::Triple::x86_64:
-    return "x64";
-  case llvm::Triple::arm:
-    return "arm";
-  default:
-    return "";
-  }
-}
-
 // 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 getWindows10SDKVersion(const std::string &SDKPath,
-                                   std::string &SDKVersion) {
+static bool getWindows10SDKVersionFromPath(const std::string &SDKPath,
+                                           std::string &SDKVersion) {
   SDKVersion.clear();
 
   std::error_code EC;
@@ -276,9 +602,9 @@
 }
 
 /// \brief Get Windows SDK installation directory.
-bool MSVCToolChain::getWindowsSDKDir(std::string &Path, int &Major,
-                                     std::string &WindowsSDKIncludeVersion,
-                                     std::string &WindowsSDKLibVersion) const {
+static bool getWindowsSDKDir(std::string &Path, int &Major,
+                             std::string &WindowsSDKIncludeVersion,
+                             std::string &WindowsSDKLibVersion) {
   std::string RegistrySDKVersion;
   // Try the Windows registry.
   if (!getSystemRegistryString(
@@ -310,7 +636,7 @@
     return !WindowsSDKLibVersion.empty();
   }
   if (Major == 10) {
-    if (!getWindows10SDKVersion(Path, WindowsSDKIncludeVersion))
+    if (!getWindows10SDKVersionFromPath(Path, WindowsSDKIncludeVersion))
       return false;
     WindowsSDKLibVersion = WindowsSDKIncludeVersion;
     return true;
@@ -333,9 +659,14 @@
 
   llvm::SmallString<128> libPath(sdkPath);
   llvm::sys::path::append(libPath, "Lib");
-  if (sdkMajor <= 7) {
+  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.
+      // In Windows SDK 7.x, x86 libraries are directly in the Lib folder.
     case llvm::Triple::x86:
       break;
     case llvm::Triple::x86_64:
@@ -347,11 +678,6 @@
     default:
       return false;
     }
-  } else {
-    const StringRef archName = getWindowsSDKArch(getArch());
-    if (archName.empty())
-      return false;
-    llvm::sys::path::append(libPath, windowsSDKLibVersion, "um", archName);
   }
 
   path = libPath.str();
@@ -360,24 +686,22 @@
 
 // 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 clang::driver::toolchains::MSVCToolChain::useUniversalCRT(
-    std::string &VisualStudioDir) const {
-  llvm::SmallString<128> TestPath(VisualStudioDir);
-  llvm::sys::path::append(TestPath, "VC\\include\\stdlib.h");
-
+bool MSVCToolChain::useUniversalCRT() const {
+  llvm::SmallString<128> TestPath(getSubDirectoryPath(SubDirectoryType::Include));
+  llvm::sys::path::append(TestPath, "stdlib.h");
   return !llvm::sys::fs::exists(TestPath);
 }
 
-bool MSVCToolChain::getUniversalCRTSdkDir(std::string &Path,
-                                          std::string &UCRTVersion) const {
+static bool getUniversalCRTSdkDir(std::string &Path,
+                                  std::string &UCRTVersion) {
   // 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))
+          "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
+          "KitsRoot10", Path, nullptr))
     return false;
 
-  return getWindows10SDKVersion(Path, UCRTVersion);
+  return getWindows10SDKVersionFromPath(Path, UCRTVersion);
 }
 
 bool MSVCToolChain::getUniversalCRTLibraryPath(std::string &Path) const {
@@ -388,7 +712,7 @@
   if (!getUniversalCRTSdkDir(UniversalCRTSdkPath, UCRTVersion))
     return false;
 
-  StringRef ArchName = getWindowsSDKArch(getArch());
+  StringRef ArchName = llvmArchToWindowsSDKArch(getArch());
   if (ArchName.empty())
     return false;
 
@@ -399,89 +723,6 @@
   return true;
 }
 
-// Get the location to use for Visual Studio binaries.  The location priority
-// is: %VCINSTALLDIR% > %PATH% > newest copy of Visual Studio installed on
-// system (as reported by the registry).
-bool MSVCToolChain::getVisualStudioBinariesFolder(const char *clangProgramPath,
-                                                  std::string &path) const {
-  path.clear();
-
-  SmallString<128> BinDir;
-
-  // First check the environment variables that vsvars32.bat sets.
-  llvm::Optional<std::string> VcInstallDir =
-      llvm::sys::Process::GetEnv("VCINSTALLDIR");
-  if (VcInstallDir.hasValue()) {
-    BinDir = VcInstallDir.getValue();
-    llvm::sys::path::append(BinDir, "bin");
-  } else {
-    // Next walk the PATH, trying to find a cl.exe in the path.  If we find one,
-    // use that.  However, make sure it's not clang's cl.exe.
-    llvm::Optional<std::string> OptPath = llvm::sys::Process::GetEnv("PATH");
-    if (OptPath.hasValue()) {
-      const char EnvPathSeparatorStr[] = {llvm::sys::EnvPathSeparator, '\0'};
-      SmallVector<StringRef, 8> PathSegments;
-      llvm::SplitString(OptPath.getValue(), PathSegments, EnvPathSeparatorStr);
-
-      for (StringRef PathSegment : PathSegments) {
-        if (PathSegment.empty())
-          continue;
-
-        SmallString<128> FilePath(PathSegment);
-        llvm::sys::path::append(FilePath, "cl.exe");
-        // Checking if cl.exe exists is a small optimization over calling
-        // can_execute, which really only checks for existence but will also do
-        // extra checks for cl.exe.exe.  These add up when walking a long path.
-        if (llvm::sys::fs::exists(FilePath.c_str()) &&
-            !llvm::sys::fs::equivalent(FilePath.c_str(), clangProgramPath)) {
-          // If we found it on the PATH, use it exactly as is with no
-          // modifications.
-          path = PathSegment;
-          return true;
-        }
-      }
-    }
-
-    std::string installDir;
-    // With no VCINSTALLDIR and nothing on the PATH, if we can't find it in the
-    // registry then we have no choice but to fail.
-    if (!getVisualStudioInstallDir(installDir))
-      return false;
-
-    // Regardless of what binary we're ultimately trying to find, we make sure
-    // that this is a Visual Studio directory by checking for cl.exe.  We use
-    // cl.exe instead of other binaries like link.exe because programs such as
-    // GnuWin32 also have a utility called link.exe, so cl.exe is the least
-    // ambiguous.
-    BinDir = installDir;
-    llvm::sys::path::append(BinDir, "VC", "bin");
-    SmallString<128> ClPath(BinDir);
-    llvm::sys::path::append(ClPath, "cl.exe");
-
-    if (!llvm::sys::fs::can_execute(ClPath.c_str()))
-      return false;
-  }
-
-  if (BinDir.empty())
-    return false;
-
-  switch (getArch()) {
-  case llvm::Triple::x86:
-    break;
-  case llvm::Triple::x86_64:
-    llvm::sys::path::append(BinDir, "amd64");
-    break;
-  case llvm::Triple::arm:
-    llvm::sys::path::append(BinDir, "arm");
-    break;
-  default:
-    // Whatever this is, Visual Studio doesn't have a toolchain for it.
-    return false;
-  }
-  path = BinDir.str();
-  return true;
-}
-
 VersionTuple MSVCToolChain::getMSVCVersionFromTriple() const {
   unsigned Major, Minor, Micro;
   getTriple().getEnvironmentVersion(Major, Minor, Micro);
@@ -493,10 +734,7 @@
 VersionTuple MSVCToolChain::getMSVCVersionFromExe() const {
   VersionTuple Version;
 #ifdef USE_WIN32
-  std::string BinPath;
-  if (!getVisualStudioBinariesFolder("", BinPath))
-    return Version;
-  SmallString<128> ClExe(BinPath);
+  SmallString<128> ClExe(getSubDirectoryPath(SubDirectoryType::Bin));
   llvm::sys::path::append(ClExe, "cl.exe");
 
   std::wstring ClExeWide;
@@ -529,62 +767,6 @@
   return Version;
 }
 
-// Get Visual Studio installation directory.
-bool MSVCToolChain::getVisualStudioInstallDir(std::string &path) const {
-  // First check the environment variables that vsvars32.bat sets.
-  if (llvm::Optional<std::string> VcInstallDir =
-          llvm::sys::Process::GetEnv("VCINSTALLDIR")) {
-    path = std::move(*VcInstallDir);
-    path = path.substr(0, path.find("\\VC"));
-    return true;
-  }
-
-  std::string vsIDEInstallDir;
-  std::string vsExpressIDEInstallDir;
-  // Then try the windows registry.
-  bool hasVCDir =
-      getSystemRegistryString("SOFTWARE\\Microsoft\\VisualStudio\\$VERSION",
-                              "InstallDir", vsIDEInstallDir, nullptr);
-  if (hasVCDir && !vsIDEInstallDir.empty()) {
-    path = vsIDEInstallDir.substr(0, vsIDEInstallDir.find("\\Common7\\IDE"));
-    return true;
-  }
-
-  bool hasVCExpressDir =
-      getSystemRegistryString("SOFTWARE\\Microsoft\\VCExpress\\$VERSION",
-                              "InstallDir", vsExpressIDEInstallDir, nullptr);
-  if (hasVCExpressDir && !vsExpressIDEInstallDir.empty()) {
-    path = vsExpressIDEInstallDir.substr(
-        0, vsIDEInstallDir.find("\\Common7\\IDE"));
-    return true;
-  }
-
-  // Try the environment.
-  std::string vcomntools;
-  if (llvm::Optional<std::string> vs120comntools =
-          llvm::sys::Process::GetEnv("VS120COMNTOOLS"))
-    vcomntools = std::move(*vs120comntools);
-  else if (llvm::Optional<std::string> vs100comntools =
-               llvm::sys::Process::GetEnv("VS100COMNTOOLS"))
-    vcomntools = std::move(*vs100comntools);
-  else if (llvm::Optional<std::string> vs90comntools =
-               llvm::sys::Process::GetEnv("VS90COMNTOOLS"))
-    vcomntools = std::move(*vs90comntools);
-  else if (llvm::Optional<std::string> vs80comntools =
-               llvm::sys::Process::GetEnv("VS80COMNTOOLS"))
-    vcomntools = std::move(*vs80comntools);
-
-  // Find any version we can.
-  if (!vcomntools.empty()) {
-    size_t p = vcomntools.find("\\Common7\\Tools");
-    if (p != std::string::npos)
-      vcomntools.resize(p);
-    path = std::move(vcomntools);
-    return true;
-  }
-  return false;
-}
-
 void MSVCToolChain::AddSystemIncludeWithSubfolder(
     const ArgList &DriverArgs, ArgStringList &CC1Args,
     const std::string &folder, const Twine &subfolder1, const Twine &subfolder2,
@@ -623,14 +805,14 @@
       return;
   }
 
-  std::string VSDir;
-
   // When built with access to the proper Windows APIs, try to actually find
   // the correct include paths first.
-  if (getVisualStudioInstallDir(VSDir)) {
-    AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, VSDir, "VC\\include");
+  if (!VCToolChainPath.empty()) {
+    addSystemInclude(DriverArgs,
+                     CC1Args,
+                     getSubDirectoryPath(SubDirectoryType::Include));
 
-    if (useUniversalCRT(VSDir)) {
+    if (useUniversalCRT()) {
       std::string UniversalCRTSdkPath;
       std::string UCRTVersion;
       if (getUniversalCRTSdkDir(UniversalCRTSdkPath, UCRTVersion)) {
@@ -661,9 +843,8 @@
         AddSystemIncludeWithSubfolder(DriverArgs, CC1Args, WindowsSDKDir,
                                       "include");
       }
-    } else {
-      addSystemInclude(DriverArgs, CC1Args, VSDir);
     }
+
     return;
   }
 
Index: include/clang/Basic/DiagnosticDriverKinds.td
===================================================================
--- include/clang/Basic/DiagnosticDriverKinds.td
+++ include/clang/Basic/DiagnosticDriverKinds.td
@@ -277,4 +277,8 @@
 def err_drv_unsupported_linker : Error<"unsupported value '%0' for -linker option">;
 def err_drv_defsym_invalid_format : Error<"defsym must be of the form: sym=value: %0">;
 def err_drv_defsym_invalid_symval : Error<"Value is not an integer: %0">;
+
+def err_drv_msvc_not_found : Error<
+  "unable to find a Visual Studio installation. "
+  "Try re-running Clang from a devleoper command prompt">;
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to