pkasting updated this revision to Diff 403265.
pkasting added a comment.
Herald added subscribers: cfe-commits, dexonsmith, mstorsjo.
Herald added a project: clang.

Adds autodetection and full support for other clang-cl switches, but doesn't 
work: config->machine is not set this early, so it's always UNKNOWN.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D118070/new/

https://reviews.llvm.org/D118070

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/lib/Driver/ToolChains/MSVC.cpp
  clang/lib/Driver/ToolChains/MSVCSetupApi.h
  lld/COFF/Driver.cpp
  lld/COFF/Driver.h
  lld/COFF/Options.td
  llvm/include/llvm/Support/MSVCSetupApi.h
  utils/bazel/llvm-project-overlay/clang/BUILD.bazel

Index: utils/bazel/llvm-project-overlay/clang/BUILD.bazel
===================================================================
--- utils/bazel/llvm-project-overlay/clang/BUILD.bazel
+++ utils/bazel/llvm-project-overlay/clang/BUILD.bazel
@@ -1309,9 +1309,6 @@
             "lib/Driver/ToolChains/Arch/*.cpp",
             "lib/Driver/ToolChains/Arch/*.h",
         ],
-        exclude = [
-            "lib/Driver/ToolChains/MSVCSetupApi.h",
-        ],
     ),
     hdrs = glob([
         "include/clang/Driver/*.h",
Index: lld/COFF/Options.td
===================================================================
--- lld/COFF/Options.td
+++ 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<nodefaultlib>;
Index: lld/COFF/Driver.h
===================================================================
--- lld/COFF/Driver.h
+++ lld/COFF/Driver.h
@@ -107,6 +107,9 @@
 
   bool findUnderscoreMangle(StringRef sym);
 
+  // Adds appropriate search paths from `sysroot`.
+  void addWinSysRootLibSearchPaths(const llvm::opt::InputArgList &args);
+
   // Parses LIB environment which contains a list of search paths.
   void addLibSearchPaths();
 
Index: lld/COFF/Driver.cpp
===================================================================
--- lld/COFF/Driver.cpp
+++ lld/COFF/Driver.cpp
@@ -37,6 +37,7 @@
 #include "llvm/Support/BinaryStreamReader.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/Host.h"
 #include "llvm/Support/LEB128.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Parallel.h"
@@ -50,6 +51,44 @@
 #include <future>
 #include <memory>
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#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 "llvm/Support/MSVCSetupApi.h"
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+#include "llvm/Support/COM.h"
+_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration));
+_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2));
+_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper));
+_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances));
+_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance));
+_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2));
+
+// TODO(pkasting): Remove
+#undef IMAGE_SUBSYSTEM_UNKNOWN
+#undef IMAGE_SUBSYSTEM_WINDOWS_GUI
+#undef IMAGE_SUBSYSTEM_WINDOWS_CUI
+#endif
+
 using namespace llvm;
 using namespace llvm::object;
 using namespace llvm::COFF;
@@ -102,7 +141,7 @@
   else if (config->driver)
     ext = ".sys";
 
-  return (sys::path::stem(path) + ext).str();
+  return (path::stem(path) + ext).str();
 }
 
 // Returns true if S matches /crtend.?\.o$/.
@@ -150,6 +189,704 @@
   return sym;
 }
 
+static std::string getHighestNumericTupleInDirectory(StringRef Directory) {
+  std::string Highest;
+  VersionTuple HighestTuple;
+
+  std::error_code EC;
+  for (fs::directory_iterator DirIt(Directory, EC), DirEnd;
+       !EC && DirIt != DirEnd; DirIt.increment(EC)) {
+
+    if (!fs::is_directory(DirIt->path()))
+      continue;
+    StringRef CandidateName = path::filename(DirIt->path());
+    VersionTuple Tuple;
+    if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error.
+      continue;
+    if (Tuple > HighestTuple) {
+      HighestTuple = Tuple;
+      Highest = CandidateName.str();
+    }
+  }
+
+  return Highest;
+}
+
+// Windows SDKs and VC Toolchains group their contents into subdirectories based
+// on the target architecture.
+static const char *getWindowsSDKArch() {
+  switch (config->machine) {
+  case I386:
+    return "x86";
+  case AMD64:
+    return "x64";
+  case ARMNT:
+    return "arm";
+  case ARM64:
+    return "arm64";
+  default:
+    return "";
+  }
+}
+
+// Similar to the above function, but for Visual Studios before VS2017.
+static const char *getLegacyVCArch() {
+  switch (config->machine) {
+  case I386:
+    // x86 is default in legacy VC toolchains.
+    // e.g. x86 libs are directly in /lib as opposed to /lib/x86.
+    return "";
+  case AMD64:
+    return "amd64";
+  case ARMNT:
+    return "arm";
+  case ARM64:
+    return "arm64";
+  default:
+    return "";
+  }
+}
+
+// Similar to the above function, but for DevDiv internal builds.
+static const char *getDevDivInternalArch() {
+  switch (config->machine) {
+  case I386:
+    return "i386";
+  case AMD64:
+    return "amd64";
+  case ARMNT:
+    return "arm";
+  case ARM64:
+    return "arm64";
+  default:
+    return "";
+  }
+}
+
+enum class SubDirectoryType {
+  Bin,
+  Include,
+  Lib,
+};
+
+enum class ToolsetLayout {
+  OlderVS,
+  VS2017OrNewer,
+  DevDivInternal,
+};
+
+static std::string getSubDirectoryPath(SubDirectoryType Type,
+                                       ToolsetLayout VSLayout,
+                                       const std::string &VCToolChainPath,
+                                       StringRef SubdirParent = "") {
+  const char *SubdirName;
+  const char *IncludeName;
+  switch (VSLayout) {
+  case ToolsetLayout::OlderVS:
+    SubdirName = getLegacyVCArch();
+    IncludeName = "include";
+    break;
+  case ToolsetLayout::VS2017OrNewer:
+    SubdirName = getWindowsSDKArch();
+    IncludeName = "include";
+    break;
+  case ToolsetLayout::DevDivInternal:
+    SubdirName = getDevDivInternalArch();
+    IncludeName = "inc";
+    break;
+  }
+
+  llvm::SmallString<256> Path(VCToolChainPath);
+  if (!SubdirParent.empty())
+    path::append(Path, SubdirParent);
+
+  switch (Type) {
+  case SubDirectoryType::Bin:
+    if (VSLayout == ToolsetLayout::VS2017OrNewer) {
+      const bool HostIsX64 = llvm::Triple(getProcessTriple()).isArch64Bit();
+      const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86";
+      path::append(Path, "bin", HostName, SubdirName);
+    } else { // OlderVS or DevDivInternal
+      path::append(Path, "bin", SubdirName);
+    }
+    break;
+  case SubDirectoryType::Include:
+    path::append(Path, IncludeName);
+    break;
+  case SubDirectoryType::Lib:
+    path::append(Path, "lib", SubdirName);
+    break;
+  }
+  return std::string(Path.str());
+}
+
+// Check if the Include path of a specified version of Visual Studio contains
+// specific header files. If not, they are probably shipped with Universal CRT.
+static bool useUniversalCRT(ToolsetLayout VSLayout,
+                            const std::string &VCToolChainPath) {
+  SmallString<128> TestPath(getSubDirectoryPath(SubDirectoryType::Include,
+                                                VSLayout, VCToolChainPath));
+  path::append(TestPath, "stdlib.h");
+  return !fs::exists(TestPath);
+}
+
+#ifdef _WIN32
+static bool readFullStringValue(HKEY hkey, const char *valueName,
+                                std::string &value) {
+  std::wstring WideValueName;
+  if (!ConvertUTF8toWide(valueName, WideValueName))
+    return false;
+
+  DWORD result = 0;
+  DWORD valueSize = 0;
+  DWORD type = 0;
+  // First just query for the required size.
+  result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, &type, NULL,
+                            &valueSize);
+  if (result != ERROR_SUCCESS || type != REG_SZ || !valueSize)
+    return false;
+  std::vector<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 convertWideToUTF8(WideValue, value);
+  }
+  return false;
+}
+#endif
+
+/// Read registry string.
+/// This also supports a means to look for high-versioned keys by use
+/// of a $VERSION placeholder in the key path.
+/// $VERSION in the key path is a placeholder for the version number,
+/// causing the highest value path to be searched for and used.
+/// I.e. "SOFTWARE\\Microsoft\\VisualStudio\\$VERSION".
+/// There can be additional characters in the component.  Only the numeric
+/// characters are compared.  This function only searches HKLM.
+static bool getSystemRegistryString(const char *keyPath, const char *valueName,
+                                    std::string &value, std::string *phValue) {
+#ifndef _WIN32
+  return false;
+#else
+  HKEY hRootKey = HKEY_LOCAL_MACHINE;
+  HKEY hKey = NULL;
+  long lResult;
+  bool returnValue = false;
+
+  const char *placeHolder = strstr(keyPath, "$VERSION");
+  std::string bestName;
+  // If we have a $VERSION placeholder, do the highest-version search.
+  if (placeHolder) {
+    const char *keyEnd = placeHolder - 1;
+    const char *nextKey = placeHolder;
+    // Find end of previous key.
+    while ((keyEnd > keyPath) && (*keyEnd != '\\'))
+      keyEnd--;
+    // Find end of key containing $VERSION.
+    while (*nextKey && (*nextKey != '\\'))
+      nextKey++;
+    size_t partialKeyLength = keyEnd - keyPath;
+    char partialKey[256];
+    if (partialKeyLength >= sizeof(partialKey))
+      partialKeyLength = sizeof(partialKey) - 1;
+    strncpy(partialKey, keyPath, partialKeyLength);
+    partialKey[partialKeyLength] = '\0';
+    HKEY hTopKey = NULL;
+    lResult = RegOpenKeyExA(hRootKey, partialKey, 0, KEY_READ | KEY_WOW64_32KEY,
+                            &hTopKey);
+    if (lResult == ERROR_SUCCESS) {
+      char keyName[256];
+      double bestValue = 0.0;
+      DWORD index, size = sizeof(keyName) - 1;
+      for (index = 0; RegEnumKeyExA(hTopKey, index, keyName, &size, NULL, NULL,
+                                    NULL, NULL) == ERROR_SUCCESS;
+           index++) {
+        const char *sp = keyName;
+        while (*sp && !isDigit(*sp))
+          sp++;
+        if (!*sp)
+          continue;
+        const char *ep = sp + 1;
+        while (*ep && (isDigit(*ep) || (*ep == '.')))
+          ep++;
+        char numBuf[32];
+        strncpy(numBuf, sp, sizeof(numBuf) - 1);
+        numBuf[sizeof(numBuf) - 1] = '\0';
+        double dvalue = strtod(numBuf, NULL);
+        if (dvalue > bestValue) {
+          // Test that InstallDir is indeed there before keeping this index.
+          // Open the chosen key path remainder.
+          bestName = keyName;
+          // Append rest of key.
+          bestName.append(nextKey);
+          lResult = RegOpenKeyExA(hTopKey, bestName.c_str(), 0,
+                                  KEY_READ | KEY_WOW64_32KEY, &hKey);
+          if (lResult == ERROR_SUCCESS) {
+            if (readFullStringValue(hKey, valueName, value)) {
+              bestValue = dvalue;
+              if (phValue)
+                *phValue = bestName;
+              returnValue = true;
+            }
+            RegCloseKey(hKey);
+          }
+        }
+        size = sizeof(keyName) - 1;
+      }
+      RegCloseKey(hTopKey);
+    }
+  } else {
+    lResult =
+        RegOpenKeyExA(hRootKey, keyPath, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);
+    if (lResult == ERROR_SUCCESS) {
+      if (readFullStringValue(hKey, valueName, value))
+        returnValue = true;
+      if (phValue)
+        phValue->clear();
+      RegCloseKey(hKey);
+    }
+  }
+  return returnValue;
+#endif // _WIN32
+}
+
+// Find the most recent version of Universal CRT or Windows 10 SDK.
+// vcvarsqueryregistry.bat from Visual Studio 2015 sorts entries in the include
+// directory by name and uses the last one of the list.
+// So we compare entry names lexicographically to find the greatest one.
+static bool getWindows10SDKVersionFromPath(const std::string &SDKPath,
+                                           std::string &SDKVersion) {
+  llvm::SmallString<128> IncludePath(SDKPath);
+  path::append(IncludePath, "Include");
+  SDKVersion = getHighestNumericTupleInDirectory(IncludePath);
+  return !SDKVersion.empty();
+}
+
+static bool getWindowsSDKDirViaCommandLine(const opt::InputArgList &Args,
+                                           std::string &Path, int &Major,
+                                           std::string &Version) {
+  if (auto *A = Args.getLastArg(OPT_winsdkdir, OPT_winsysroot)) {
+    // Don't validate the input; trust the value supplied by the user.
+    // The motivation is to prevent unnecessary file and registry access.
+    llvm::VersionTuple SDKVersion;
+    if (auto *A = Args.getLastArg(OPT_winsdkversion))
+      SDKVersion.tryParse(A->getValue());
+
+    if (A->getOption().getID() == OPT_winsysroot) {
+      llvm::SmallString<128> SDKPath(A->getValue());
+      path::append(SDKPath, "Windows Kits");
+      if (!SDKVersion.empty())
+        path::append(SDKPath, Twine(SDKVersion.getMajor()));
+      else
+        path::append(SDKPath, getHighestNumericTupleInDirectory(SDKPath));
+      Path = std::string(SDKPath.str());
+    } else {
+      Path = A->getValue();
+    }
+
+    if (!SDKVersion.empty()) {
+      Major = SDKVersion.getMajor();
+      Version = SDKVersion.getAsString();
+    } else if (getWindows10SDKVersionFromPath(Path, Version)) {
+      Major = 10;
+    }
+    return true;
+  }
+  return false;
+}
+
+static bool getUniversalCRTSdkDir(const opt::InputArgList &Args,
+                                  std::string &Path, std::string &UCRTVersion) {
+  // If /winsdkdir is passed, use it as location for the UCRT too.
+  // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir?
+  int Major;
+  if (getWindowsSDKDirViaCommandLine(Args, Path, Major, UCRTVersion))
+    return true;
+
+  // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to
+  // registry.
+
+  // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry
+  // for the specific key "KitsRoot10". So do we.
+  if (!getSystemRegistryString(
+          "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10",
+          Path, nullptr))
+    return false;
+
+  return getWindows10SDKVersionFromPath(Path, UCRTVersion);
+}
+
+/// Get Windows SDK installation directory.
+static bool getWindowsSDKDir(const opt::InputArgList &Args,
+                             std::string &Path, int &Major,
+                             std::string &WindowsSDKIncludeVersion,
+                             std::string &WindowsSDKLibVersion) {
+  // Trust /winsdkdir and /winsdkversion if present.
+  if (getWindowsSDKDirViaCommandLine(Args, Path, Major,
+                                     WindowsSDKIncludeVersion)) {
+    WindowsSDKLibVersion = WindowsSDKIncludeVersion;
+    return true;
+  }
+
+  // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to
+  // registry.
+
+  // Try the Windows registry.
+  std::string RegistrySDKVersion;
+  if (!getSystemRegistryString(
+          "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION",
+          "InstallationFolder", Path, &RegistrySDKVersion))
+    return false;
+  if (Path.empty() || RegistrySDKVersion.empty())
+    return false;
+
+  WindowsSDKIncludeVersion.clear();
+  WindowsSDKLibVersion.clear();
+  Major = 0;
+  std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major);
+  if (Major <= 7)
+    return true;
+  if (Major == 8) {
+    // Windows SDK 8.x installs libraries in a folder whose names depend on the
+    // version of the OS you're targeting.  By default choose the newest, which
+    // usually corresponds to the version of the OS you've installed the SDK on.
+    const char *Tests[] = {"winv6.3", "win8", "win7"};
+    for (const char *Test : Tests) {
+      llvm::SmallString<128> TestPath(Path);
+      path::append(TestPath, "Lib", Test);
+      if (fs::exists(TestPath)) {
+        WindowsSDKLibVersion = Test;
+        break;
+      }
+    }
+    return !WindowsSDKLibVersion.empty();
+  }
+  if (Major == 10) {
+    if (!getWindows10SDKVersionFromPath(Path, WindowsSDKIncludeVersion))
+      return false;
+    WindowsSDKLibVersion = WindowsSDKIncludeVersion;
+    return true;
+  }
+  // Unsupported SDK version
+  return false;
+}
+
+static bool getUniversalCRTLibraryPath(const opt::InputArgList &Args,
+                                       std::string &Path) {
+  std::string UniversalCRTSdkPath;
+  std::string UCRTVersion;
+
+  Path.clear();
+  if (!getUniversalCRTSdkDir(Args, UniversalCRTSdkPath, UCRTVersion))
+    return false;
+
+  StringRef ArchName = getWindowsSDKArch();
+  if (ArchName.empty())
+    return false;
+
+  llvm::SmallString<128> LibPath(UniversalCRTSdkPath);
+  path::append(LibPath, "Lib", UCRTVersion, "ucrt", ArchName);
+
+  Path = std::string(LibPath.str());
+  return true;
+}
+
+static bool getWindowsSDKLibraryPath(const opt::InputArgList &Args,
+                                     std::string &Path) {
+  std::string sdkPath;
+  int sdkMajor = 0;
+  std::string windowsSDKIncludeVersion;
+  std::string windowsSDKLibVersion;
+
+  Path.clear();
+  if (!getWindowsSDKDir(Args, sdkPath, sdkMajor, windowsSDKIncludeVersion,
+                        windowsSDKLibVersion))
+    return false;
+
+  llvm::SmallString<128> libPath(sdkPath);
+  path::append(libPath, "Lib");
+  if (sdkMajor >= 8) {
+    path::append(libPath, windowsSDKLibVersion, "um", getWindowsSDKArch());
+  } else {
+    switch (config->machine) {
+    // In Windows SDK 7.x, x86 libraries are directly in the Lib folder.
+    case I386:
+      break;
+    case AMD64:
+      path::append(libPath, "x64");
+      break;
+    case ARMNT:
+      // 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;
+}
+
+ // Check command line arguments to try and find a toolchain.
+static bool
+findVCToolChainViaCommandLine(const opt::InputArgList &Args, std::string &Path,
+                              ToolsetLayout &VSLayout) {
+  // Don't validate the input; trust the value supplied by the user.
+  // The primary motivation is to prevent unnecessary file and registry access.
+  if (auto *A = Args.getLastArg(OPT_vctoolsdir, OPT_winsysroot)) {
+    if (A->getOption().getID() == OPT_winsysroot) {
+      SmallString<128> ToolsPath(A->getValue());
+      path::append(ToolsPath, "VC", "Tools", "MSVC");
+      std::string VCToolsVersion;
+      if (auto *A = Args.getLastArg(OPT_vctoolsversion))
+        VCToolsVersion = A->getValue();
+      else
+        VCToolsVersion = getHighestNumericTupleInDirectory(ToolsPath);
+      llvm::sys::path::append(ToolsPath, VCToolsVersion);
+      Path = std::string(ToolsPath.str());
+    } else {
+      Path = A->getValue();
+    }
+    VSLayout = ToolsetLayout::VS2017OrNewer;
+    return true;
+  }
+  return false;
+}
+
+// Check various environment variables to try and find a toolchain.
+static bool
+findVCToolChainViaEnvironment(std::string &Path, ToolsetLayout &VSLayout) {
+  // These variables are typically set by vcvarsall.bat
+  // when launching a developer command prompt.
+  if (Optional<std::string> VCToolsInstallDir =
+          Process::GetEnv("VCToolsInstallDir")) {
+    // This is only set by newer Visual Studios, and it leads straight to
+    // the toolchain directory.
+    Path = std::move(*VCToolsInstallDir);
+    VSLayout = ToolsetLayout::VS2017OrNewer;
+    return true;
+  }
+  if (Optional<std::string> VCInstallDir = Process::GetEnv("VCINSTALLDIR")) {
+    // If the previous variable isn't set but this one is, then we've found
+    // an older Visual Studio. This variable is set by newer Visual Studios too,
+    // so this check has to appear second.
+    // In older Visual Studios, the VC directory is the toolchain.
+    Path = std::move(*VCInstallDir);
+    VSLayout = ToolsetLayout::OlderVS;
+    return true;
+  }
+
+  // We couldn't find any VC environment variables. Let's walk through PATH and
+  // see if it leads us to a VC toolchain bin directory. If it does, pick the
+  // first one that we find.
+  if (Optional<std::string> PathEnv = Process::GetEnv("PATH")) {
+    SmallVector<StringRef, 8> PathEntries;
+    StringRef(*PathEnv).split(PathEntries, EnvPathSeparator);
+    for (StringRef PathEntry : PathEntries) {
+      if (PathEntry.empty())
+        continue;
+
+      SmallString<256> ExeTestPath;
+
+      // If cl.exe doesn't exist, then this definitely isn't a VC toolchain.
+      ExeTestPath = PathEntry;
+      path::append(ExeTestPath, "cl.exe");
+      if (!fs::exists(ExeTestPath))
+        continue;
+
+      // cl.exe existing isn't a conclusive test for a VC toolchain; clang also
+      // has a cl.exe. So let's check for link.exe too.
+      ExeTestPath = PathEntry;
+      path::append(ExeTestPath, "link.exe");
+      if (!fs::exists(ExeTestPath))
+        continue;
+
+      // whatever/VC/bin --> old toolchain, VC dir is toolchain dir.
+      StringRef TestPath = PathEntry;
+      bool IsBin =
+          path::filename(TestPath).equals_insensitive("bin");
+      if (!IsBin) {
+        // Strip any architecture subdir like "amd64".
+        TestPath = path::parent_path(TestPath);
+        IsBin = path::filename(TestPath).equals_insensitive("bin");
+      }
+      if (IsBin) {
+        StringRef ParentPath = path::parent_path(TestPath);
+        StringRef ParentFilename = path::filename(ParentPath);
+        if (ParentFilename.equals_insensitive("VC")) {
+          Path = std::string(ParentPath);
+          VSLayout = ToolsetLayout::OlderVS;
+          return true;
+        }
+        if (ParentFilename.equals_insensitive("x86ret") ||
+            ParentFilename.equals_insensitive("x86chk") ||
+            ParentFilename.equals_insensitive("amd64ret") ||
+            ParentFilename.equals_insensitive("amd64chk")) {
+          Path = std::string(ParentPath);
+          VSLayout = ToolsetLayout::DevDivInternal;
+          return true;
+        }
+
+      } else {
+        // This could be a new (>=VS2017) toolchain. If it is, we should find
+        // path components with these prefixes when walking backwards through
+        // the path.
+        // Note: empty strings match anything.
+        StringRef ExpectedPrefixes[] =
+            {"", "Host",  "bin", "", "MSVC", "Tools", "VC"};
+
+        auto It = path::rbegin(PathEntry);
+        auto End = path::rend(PathEntry);
+        for (StringRef Prefix : ExpectedPrefixes) {
+          if (It == End)
+            goto NotAToolChain;
+          if (!It->startswith_insensitive(Prefix))
+            goto NotAToolChain;
+          ++It;
+        }
+
+        // We've found a new toolchain!
+        // Back up 3 times (/bin/Host/arch) to get the root path.
+        StringRef ToolChainPath(PathEntry);
+        for (int i = 0; i < 3; ++i)
+          ToolChainPath = path::parent_path(ToolChainPath);
+
+        Path = std::string(ToolChainPath);
+        VSLayout = ToolsetLayout::VS2017OrNewer;
+        return true;
+      }
+
+    NotAToolChain:
+      continue;
+    }
+  }
+  return false;
+}
+
+// Query the Setup Config server for installs, then pick the newest version
+// and find its default VC toolchain.
+// This is the preferred way to discover new Visual Studios, as they're no
+// longer listed in the registry.
+static bool
+findVCToolChainViaSetupConfig(std::string &Path, ToolsetLayout &VSLayout) {
+#if !defined(USE_MSVC_SETUP_API)
+  return false;
+#else
+  // FIXME: This really should be done once in the top-level program's main
+  // function, as it may have already been initialized with a different
+  // threading model otherwise.
+  InitializeCOMRAII COM(COMThreadingMode::SingleThreaded);
+  HRESULT HR;
+
+  // _com_ptr_t will throw a _com_error if a COM calls fail.
+  // The LLVM coding standards forbid exception handling, so we'll have to
+  // stop them from being thrown in the first place.
+  // The destructor will put the regular error handler back when we leave
+  // this scope.
+  struct SuppressCOMErrorsRAII {
+    static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {}
+
+    SuppressCOMErrorsRAII() { _set_com_error_handler(handler); }
+
+    ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); }
+
+  } COMErrorSuppressor;
+
+  ISetupConfigurationPtr Query;
+  HR = Query.CreateInstance(__uuidof(SetupConfiguration));
+  if (FAILED(HR))
+    return false;
+
+  IEnumSetupInstancesPtr EnumInstances;
+  HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances);
+  if (FAILED(HR))
+    return false;
+
+  ISetupInstancePtr Instance;
+  HR = EnumInstances->Next(1, &Instance, nullptr);
+  if (HR != S_OK)
+    return false;
+
+  ISetupInstancePtr NewestInstance;
+  Optional<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;
+  convertWideToUTF8(std::wstring(VCPathWide), VCRootPath);
+
+  SmallString<256> ToolsVersionFilePath(VCRootPath);
+  path::append(ToolsVersionFilePath, "Auxiliary", "Build",
+               "Microsoft.VCToolsVersion.default.txt");
+
+  auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath);
+  if (!ToolsVersionFile)
+    return false;
+
+  SmallString<256> ToolchainPath(VCRootPath);
+  path::append(ToolchainPath, "Tools", "MSVC",
+               ToolsVersionFile->get()->getBuffer().rtrim());
+  if (!fs::is_directory(ToolchainPath))
+    return false;
+
+  Path = std::string(ToolchainPath.str());
+  VSLayout = ToolsetLayout::VS2017OrNewer;
+  return true;
+#endif
+}
+
+// Look in the registry for Visual Studio installs, and use that to get
+// a toolchain path. VS2017 and newer don't get added to the registry.
+// So if we find something here, we know that it's an older version.
+static bool findVCToolChainViaRegistry(std::string &Path,
+                                       ToolsetLayout &VSLayout) {
+  std::string VSInstallPath;
+  if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)",
+                              "InstallDir", VSInstallPath, nullptr) ||
+      getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)",
+                              "InstallDir", VSInstallPath, nullptr)) {
+    if (!VSInstallPath.empty()) {
+      SmallString<256> VCPath(StringRef(
+          VSInstallPath.c_str(), VSInstallPath.find(R"(\Common7\IDE)")));
+      path::append(VCPath, "VC");
+
+      Path = std::string(VCPath.str());
+      VSLayout = ToolsetLayout::OlderVS;
+      return true;
+    }
+  }
+  return false;
+}
+
 bool LinkerDriver::findUnderscoreMangle(StringRef sym) {
   Symbol *s = ctx.symtab.findMangle(mangle(sym));
   return s && !isa<Undefined>(s);
@@ -424,21 +1161,21 @@
   bool hasExt = filename.contains('.');
   for (StringRef dir : searchPaths) {
     SmallString<128> path = dir;
-    sys::path::append(path, filename);
-    if (sys::fs::exists(path.str()))
+    path::append(path, filename);
+    if (fs::exists(path.str()))
       return saver().save(path.str());
     if (!hasExt) {
       path.append(".obj");
-      if (sys::fs::exists(path.str()))
+      if (fs::exists(path.str()))
         return saver().save(path.str());
     }
   }
   return filename;
 }
 
-static Optional<sys::fs::UniqueID> getUniqueID(StringRef path) {
-  sys::fs::UniqueID ret;
-  if (sys::fs::getUniqueID(path, ret))
+static Optional<fs::UniqueID> getUniqueID(StringRef path) {
+  fs::UniqueID ret;
+  if (fs::getUniqueID(path, ret))
     return None;
   return ret;
 }
@@ -448,14 +1185,14 @@
 Optional<StringRef> LinkerDriver::findFile(StringRef filename) {
   StringRef path = doFindFile(filename);
 
-  if (Optional<sys::fs::UniqueID> id = getUniqueID(path)) {
+  if (Optional<fs::UniqueID> id = getUniqueID(path)) {
     bool seen = !visitedFiles.insert(*id).second;
     if (seen)
       return None;
   }
 
   if (path.endswith_insensitive(".lib"))
-    visitedLibs.insert(std::string(sys::path::filename(path)));
+    visitedLibs.insert(std::string(path::filename(path)));
   return path;
 }
 
@@ -466,7 +1203,7 @@
     return filename;
 
   SmallString<128> s = filename;
-  sys::path::replace_extension(s, ".a");
+  path::replace_extension(s, ".a");
   StringRef libName = saver().save("lib" + s.str());
   return doFindFile(libName);
 }
@@ -498,12 +1235,59 @@
   if (config->noDefaultLibs.count(path.lower()))
     return None;
 
-  if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+  if (Optional<fs::UniqueID> id = getUniqueID(path))
     if (!visitedFiles.insert(*id).second)
       return None;
   return path;
 }
 
+void LinkerDriver::addWinSysRootLibSearchPaths(const opt::InputArgList &Args) {
+  ToolsetLayout VSLayout = ToolsetLayout::OlderVS;
+  std::string VCToolChainPath;
+  // 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(Args, VCToolChainPath, VSLayout) ||
+      (!Args.hasArg(OPT_lldignoreenv) &&
+       findVCToolChainViaEnvironment(VCToolChainPath, VSLayout)) ||
+      findVCToolChainViaSetupConfig(VCToolChainPath, VSLayout) ||
+      findVCToolChainViaRegistry(VCToolChainPath, VSLayout);
+
+  // 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)) {
+    llvm::SmallString<128> DIAPath(A->getValue());
+    if (A->getOption().getID() == OPT_winsysroot)
+      path::append(DIAPath, "DIA SDK");
+
+    // The DIA SDK always uses the legacy vc arch, even in new MSVC versions.
+    path::append(DIAPath, "lib", getLegacyVCArch());
+    searchPaths.push_back(DIAPath);
+  }
+  if (Args.hasArg(OPT_lldignoreenv) || !Process::GetEnv("LIB") ||
+      Args.getLastArg(OPT_vctoolsdir, OPT_winsysroot)) {
+    searchPaths.push_back(
+        getSubDirectoryPath(SubDirectoryType::Lib, VSLayout, VCToolChainPath));
+    searchPaths.push_back(getSubDirectoryPath(SubDirectoryType::Lib, VSLayout,
+                                              VCToolChainPath, "atlmfc"));
+  }
+  if (Args.hasArg(OPT_lldignoreenv) || !Process::GetEnv("LIB") ||
+      Args.getLastArg(OPT_winsdkdir, OPT_winsysroot)) {
+    if (useUniversalCRT(VSLayout, VCToolChainPath)) {
+      std::string UniversalCRTLibPath;
+      if (getUniversalCRTLibraryPath(Args, UniversalCRTLibPath))
+        searchPaths.push_back(UniversalCRTLibPath);
+    }
+    std::string WindowsSdkLibPath;
+    if (getWindowsSDKLibraryPath(Args, WindowsSdkLibPath))
+      searchPaths.push_back(WindowsSdkLibPath);
+  }
+}
+
 // Parses LIB environment which contains a list of search paths.
 void LinkerDriver::addLibSearchPaths() {
   Optional<std::string> envOpt = Process::GetEnv("LIB");
@@ -626,6 +1410,7 @@
     case OPT_INPUT:
     case OPT_defaultlib:
     case OPT_libpath:
+    case OPT_winsysroot:
       break;
     case OPT_call_graph_ordering_file:
     case OPT_deffile:
@@ -651,7 +1436,7 @@
     case OPT_pdb:
     case OPT_pdbstripped:
     case OPT_out:
-      os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n";
+      os << arg->getSpelling() << path::filename(arg->getValue()) << "\n";
       break;
     default:
       os << toString(*arg) << "\n";
@@ -759,7 +1544,7 @@
   if (!config->implib.empty())
     return std::string(config->implib);
   SmallString<128> out = StringRef(config->outputFile);
-  sys::path::replace_extension(out, ".lib");
+  path::replace_extension(out, ".lib");
   return std::string(out.str());
 }
 
@@ -774,14 +1559,13 @@
   SmallString<128> out;
 
   if (config->importName.empty()) {
-    out.assign(sys::path::filename(config->outputFile));
+    out.assign(path::filename(config->outputFile));
     if (asLib)
-      sys::path::replace_extension(out, ".dll");
+      path::replace_extension(out, ".dll");
   } else {
     out.assign(config->importName);
-    if (!sys::path::has_extension(out))
-      sys::path::replace_extension(out,
-                                   (config->dll || asLib) ? ".dll" : ".exe");
+    if (!path::has_extension(out))
+      path::replace_extension(out, (config->dll || asLib) ? ".dll" : ".exe");
   }
 
   return std::string(out.str());
@@ -824,7 +1608,7 @@
 
   SmallString<128> tmpName;
   if (std::error_code ec =
-          sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName))
+          fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName))
     fatal("cannot create temporary file for import library " + path + ": " +
           ec.message());
 
@@ -838,9 +1622,9 @@
       tmpName, /*IsText=*/false, /*RequiresNullTerminator=*/false));
   if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) {
     oldBuf->reset();
-    checkError(errorCodeToError(sys::fs::rename(tmpName, path)));
+    checkError(errorCodeToError(fs::rename(tmpName, path)));
   } else {
-    sys::fs::remove(tmpName);
+    fs::remove(tmpName);
   }
 }
 
@@ -1089,10 +1873,9 @@
 // vars.
 static void parsePDBAltPath(StringRef altPath) {
   SmallString<128> buf;
-  StringRef pdbBasename =
-      sys::path::filename(config->pdbPath, sys::path::Style::windows);
+  StringRef pdbBasename = path::filename(config->pdbPath, path::Style::windows);
   StringRef binaryExtension =
-      sys::path::extension(config->outputFile, sys::path::Style::windows);
+      path::extension(config->outputFile, path::Style::windows);
   if (!binaryExtension.empty())
     binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'.
 
@@ -1223,7 +2006,7 @@
 
   if (auto *arg = args.getLastArg(OPT_linkrepro)) {
     SmallString<64> path = StringRef(arg->getValue());
-    sys::path::append(path, "repro.tar");
+    path::append(path, "repro.tar");
     return std::string(path);
   }
 
@@ -1314,7 +2097,7 @@
   // Handle /linkrepro and /reproduce.
   if (Optional<std::string> path = getReproduceFile(args)) {
     Expected<std::unique_ptr<TarWriter>> errOrWriter =
-        TarWriter::create(*path, sys::path::stem(*path));
+        TarWriter::create(*path, path::stem(*path));
 
     if (errOrWriter) {
       tar = std::move(*errOrWriter);
@@ -1335,6 +2118,7 @@
   searchPaths.push_back("");
   for (auto *arg : args.filtered(OPT_libpath))
     searchPaths.push_back(arg->getValue());
+  addWinSysRootLibSearchPaths(args);
   if (!args.hasArg(OPT_lldignoreenv))
     addLibSearchPaths();
 
@@ -1788,10 +2572,10 @@
   if (errorCount())
     return;
 
-  std::set<sys::fs::UniqueID> wholeArchives;
+  std::set<fs::UniqueID> wholeArchives;
   for (auto *arg : args.filtered(OPT_wholearchive_file))
     if (Optional<StringRef> path = doFindFile(arg->getValue()))
-      if (Optional<sys::fs::UniqueID> id = getUniqueID(*path))
+      if (Optional<fs::UniqueID> id = getUniqueID(*path))
         wholeArchives.insert(*id);
 
   // A predicate returning true if a given path is an argument for
@@ -1801,7 +2585,7 @@
   auto isWholeArchive = [&](StringRef path) -> bool {
     if (args.hasArg(OPT_wholearchive_flag))
       return true;
-    if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+    if (Optional<fs::UniqueID> id = getUniqueID(path))
       return wholeArchives.count(*id);
     return false;
   };
@@ -1967,7 +2751,7 @@
     // Put the PDB next to the image if no /pdb flag was passed.
     if (config->pdbPath.empty()) {
       config->pdbPath = config->outputFile;
-      sys::path::replace_extension(config->pdbPath, ".pdb");
+      path::replace_extension(config->pdbPath, ".pdb");
     }
 
     // The embedded PDB path should be the absolute path to the PDB if no
@@ -1978,8 +2762,8 @@
       // It's important to make the path absolute and remove dots.  This path
       // will eventually be written into the PE header, and certain Microsoft
       // tools won't work correctly if these assumptions are not held.
-      sys::fs::make_absolute(config->pdbAltPath);
-      sys::path::remove_dots(config->pdbAltPath);
+      fs::make_absolute(config->pdbAltPath);
+      path::remove_dots(config->pdbAltPath);
     } else {
       // Don't do this earlier, so that Config->OutputFile is ready.
       parsePDBAltPath(config->pdbAltPath);
Index: clang/lib/Driver/ToolChains/MSVC.cpp
===================================================================
--- clang/lib/Driver/ToolChains/MSVC.cpp
+++ clang/lib/Driver/ToolChains/MSVC.cpp
@@ -51,7 +51,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
 #endif
-#include "MSVCSetupApi.h"
+#include "llvm/Support/MSVCSetupApi.h"
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
Index: clang/docs/tools/clang-formatted-files.txt
===================================================================
--- clang/docs/tools/clang-formatted-files.txt
+++ clang/docs/tools/clang-formatted-files.txt
@@ -285,7 +285,6 @@
 clang/lib/Driver/ToolChains/Minix.h
 clang/lib/Driver/ToolChains/MipsLinux.cpp
 clang/lib/Driver/ToolChains/MSP430.h
-clang/lib/Driver/ToolChains/MSVCSetupApi.h
 clang/lib/Driver/ToolChains/PPCFreeBSD.cpp
 clang/lib/Driver/ToolChains/PPCFreeBSD.h
 clang/lib/Driver/ToolChains/PPCLinux.h
@@ -5150,6 +5149,7 @@
 llvm/include/llvm/Support/MSP430AttributeParser.h
 llvm/include/llvm/Support/MSP430Attributes.h
 llvm/include/llvm/Support/MSVCErrorWorkarounds.h
+llvm/include/llvm/Support/MSVCSetupApi.h
 llvm/include/llvm/Support/Parallel.h
 llvm/include/llvm/Support/PGOOptions.h
 llvm/include/llvm/Support/PointerLikeTypeTraits.h
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to