xref: /freebsd/contrib/llvm-project/llvm/lib/WindowsDriver/MSVCPaths.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1  //===-- MSVCPaths.cpp - MSVC path-parsing helpers -------------------------===//
2  //
3  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4  // See https://llvm.org/LICENSE.txt for license information.
5  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6  //
7  //===----------------------------------------------------------------------===//
8  
9  #include "llvm/WindowsDriver/MSVCPaths.h"
10  #include "llvm/ADT/SmallString.h"
11  #include "llvm/ADT/SmallVector.h"
12  #include "llvm/ADT/StringExtras.h"
13  #include "llvm/ADT/StringRef.h"
14  #include "llvm/ADT/Twine.h"
15  #include "llvm/Support/Path.h"
16  #include "llvm/Support/Process.h"
17  #include "llvm/Support/Program.h"
18  #include "llvm/Support/VersionTuple.h"
19  #include "llvm/Support/VirtualFileSystem.h"
20  #include "llvm/TargetParser/Host.h"
21  #include "llvm/TargetParser/Triple.h"
22  #include <optional>
23  #include <string>
24  
25  #ifdef _WIN32
26  #include "llvm/Support/ConvertUTF.h"
27  #endif
28  
29  #ifdef _WIN32
30  #define WIN32_LEAN_AND_MEAN
31  #define NOGDI
32  #ifndef NOMINMAX
33  #define NOMINMAX
34  #endif
35  #include <windows.h>
36  #endif
37  
38  #ifdef _MSC_VER
39  // Don't support SetupApi on MinGW.
40  #define USE_MSVC_SETUP_API
41  
42  // Make sure this comes before MSVCSetupApi.h
43  #include <comdef.h>
44  
45  #include "llvm/Support/COM.h"
46  #ifdef __clang__
47  #pragma clang diagnostic push
48  #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
49  #endif
50  #include "llvm/WindowsDriver/MSVCSetupApi.h"
51  #ifdef __clang__
52  #pragma clang diagnostic pop
53  #endif
54  _COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration));
55  _COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2));
56  _COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper));
57  _COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances));
58  _COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance));
59  _COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2));
60  #endif
61  
62  static std::string
getHighestNumericTupleInDirectory(llvm::vfs::FileSystem & VFS,llvm::StringRef Directory)63  getHighestNumericTupleInDirectory(llvm::vfs::FileSystem &VFS,
64                                    llvm::StringRef Directory) {
65    std::string Highest;
66    llvm::VersionTuple HighestTuple;
67  
68    std::error_code EC;
69    for (llvm::vfs::directory_iterator DirIt = VFS.dir_begin(Directory, EC),
70                                       DirEnd;
71         !EC && DirIt != DirEnd; DirIt.increment(EC)) {
72      auto Status = VFS.status(DirIt->path());
73      if (!Status || !Status->isDirectory())
74        continue;
75      llvm::StringRef CandidateName = llvm::sys::path::filename(DirIt->path());
76      llvm::VersionTuple Tuple;
77      if (Tuple.tryParse(CandidateName)) // tryParse() returns true on error.
78        continue;
79      if (Tuple > HighestTuple) {
80        HighestTuple = Tuple;
81        Highest = CandidateName.str();
82      }
83    }
84  
85    return Highest;
86  }
87  
getWindows10SDKVersionFromPath(llvm::vfs::FileSystem & VFS,const std::string & SDKPath,std::string & SDKVersion)88  static bool getWindows10SDKVersionFromPath(llvm::vfs::FileSystem &VFS,
89                                             const std::string &SDKPath,
90                                             std::string &SDKVersion) {
91    llvm::SmallString<128> IncludePath(SDKPath);
92    llvm::sys::path::append(IncludePath, "Include");
93    SDKVersion = getHighestNumericTupleInDirectory(VFS, IncludePath);
94    return !SDKVersion.empty();
95  }
96  
getWindowsSDKDirViaCommandLine(llvm::vfs::FileSystem & VFS,std::optional<llvm::StringRef> WinSdkDir,std::optional<llvm::StringRef> WinSdkVersion,std::optional<llvm::StringRef> WinSysRoot,std::string & Path,int & Major,std::string & Version)97  static bool getWindowsSDKDirViaCommandLine(
98      llvm::vfs::FileSystem &VFS, std::optional<llvm::StringRef> WinSdkDir,
99      std::optional<llvm::StringRef> WinSdkVersion,
100      std::optional<llvm::StringRef> WinSysRoot, std::string &Path, int &Major,
101      std::string &Version) {
102    if (WinSdkDir || WinSysRoot) {
103      // Don't validate the input; trust the value supplied by the user.
104      // The motivation is to prevent unnecessary file and registry access.
105      llvm::VersionTuple SDKVersion;
106      if (WinSdkVersion)
107        SDKVersion.tryParse(*WinSdkVersion);
108  
109      if (WinSysRoot) {
110        llvm::SmallString<128> SDKPath(*WinSysRoot);
111        llvm::sys::path::append(SDKPath, "Windows Kits");
112        if (!SDKVersion.empty())
113          llvm::sys::path::append(SDKPath, llvm::Twine(SDKVersion.getMajor()));
114        else
115          llvm::sys::path::append(
116              SDKPath, getHighestNumericTupleInDirectory(VFS, SDKPath));
117        Path = std::string(SDKPath);
118      } else {
119        Path = WinSdkDir->str();
120      }
121  
122      if (!SDKVersion.empty()) {
123        Major = SDKVersion.getMajor();
124        Version = SDKVersion.getAsString();
125      } else if (getWindows10SDKVersionFromPath(VFS, Path, Version)) {
126        Major = 10;
127      }
128      return true;
129    }
130    return false;
131  }
132  
133  #ifdef _WIN32
readFullStringValue(HKEY hkey,const char * valueName,std::string & value)134  static bool readFullStringValue(HKEY hkey, const char *valueName,
135                                  std::string &value) {
136    std::wstring WideValueName;
137    if (!llvm::ConvertUTF8toWide(valueName, WideValueName))
138      return false;
139  
140    DWORD result = 0;
141    DWORD valueSize = 0;
142    DWORD type = 0;
143    // First just query for the required size.
144    result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, &type, NULL,
145                              &valueSize);
146    if (result != ERROR_SUCCESS || type != REG_SZ || !valueSize)
147      return false;
148    std::vector<BYTE> buffer(valueSize);
149    result = RegQueryValueExW(hkey, WideValueName.c_str(), NULL, NULL, &buffer[0],
150                              &valueSize);
151    if (result == ERROR_SUCCESS) {
152      std::wstring WideValue(reinterpret_cast<const wchar_t *>(buffer.data()),
153                             valueSize / sizeof(wchar_t));
154      if (valueSize && WideValue.back() == L'\0') {
155        WideValue.pop_back();
156      }
157      // The destination buffer must be empty as an invariant of the conversion
158      // function; but this function is sometimes called in a loop that passes in
159      // the same buffer, however. Simply clear it out so we can overwrite it.
160      value.clear();
161      return llvm::convertWideToUTF8(WideValue, value);
162    }
163    return false;
164  }
165  #endif
166  
167  /// Read registry string.
168  /// This also supports a means to look for high-versioned keys by use
169  /// of a $VERSION placeholder in the key path.
170  /// $VERSION in the key path is a placeholder for the version number,
171  /// causing the highest value path to be searched for and used.
172  /// I.e. "SOFTWARE\\Microsoft\\VisualStudio\\$VERSION".
173  /// There can be additional characters in the component.  Only the numeric
174  /// characters are compared.  This function only searches HKLM.
getSystemRegistryString(const char * keyPath,const char * valueName,std::string & value,std::string * phValue)175  static bool getSystemRegistryString(const char *keyPath, const char *valueName,
176                                      std::string &value, std::string *phValue) {
177  #ifndef _WIN32
178    return false;
179  #else
180    HKEY hRootKey = HKEY_LOCAL_MACHINE;
181    HKEY hKey = NULL;
182    long lResult;
183    bool returnValue = false;
184  
185    const char *placeHolder = strstr(keyPath, "$VERSION");
186    std::string bestName;
187    // If we have a $VERSION placeholder, do the highest-version search.
188    if (placeHolder) {
189      const char *keyEnd = placeHolder - 1;
190      const char *nextKey = placeHolder;
191      // Find end of previous key.
192      while ((keyEnd > keyPath) && (*keyEnd != '\\'))
193        keyEnd--;
194      // Find end of key containing $VERSION.
195      while (*nextKey && (*nextKey != '\\'))
196        nextKey++;
197      size_t partialKeyLength = keyEnd - keyPath;
198      char partialKey[256];
199      if (partialKeyLength >= sizeof(partialKey))
200        partialKeyLength = sizeof(partialKey) - 1;
201      strncpy(partialKey, keyPath, partialKeyLength);
202      partialKey[partialKeyLength] = '\0';
203      HKEY hTopKey = NULL;
204      lResult = RegOpenKeyExA(hRootKey, partialKey, 0, KEY_READ | KEY_WOW64_32KEY,
205                              &hTopKey);
206      if (lResult == ERROR_SUCCESS) {
207        char keyName[256];
208        double bestValue = 0.0;
209        DWORD index, size = sizeof(keyName) - 1;
210        for (index = 0; RegEnumKeyExA(hTopKey, index, keyName, &size, NULL, NULL,
211                                      NULL, NULL) == ERROR_SUCCESS;
212             index++) {
213          const char *sp = keyName;
214          while (*sp && !llvm::isDigit(*sp))
215            sp++;
216          if (!*sp)
217            continue;
218          const char *ep = sp + 1;
219          while (*ep && (llvm::isDigit(*ep) || (*ep == '.')))
220            ep++;
221          char numBuf[32];
222          strncpy(numBuf, sp, sizeof(numBuf) - 1);
223          numBuf[sizeof(numBuf) - 1] = '\0';
224          double dvalue = strtod(numBuf, NULL);
225          if (dvalue > bestValue) {
226            // Test that InstallDir is indeed there before keeping this index.
227            // Open the chosen key path remainder.
228            bestName = keyName;
229            // Append rest of key.
230            bestName.append(nextKey);
231            lResult = RegOpenKeyExA(hTopKey, bestName.c_str(), 0,
232                                    KEY_READ | KEY_WOW64_32KEY, &hKey);
233            if (lResult == ERROR_SUCCESS) {
234              if (readFullStringValue(hKey, valueName, value)) {
235                bestValue = dvalue;
236                if (phValue)
237                  *phValue = bestName;
238                returnValue = true;
239              }
240              RegCloseKey(hKey);
241            }
242          }
243          size = sizeof(keyName) - 1;
244        }
245        RegCloseKey(hTopKey);
246      }
247    } else {
248      lResult =
249          RegOpenKeyExA(hRootKey, keyPath, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);
250      if (lResult == ERROR_SUCCESS) {
251        if (readFullStringValue(hKey, valueName, value))
252          returnValue = true;
253        if (phValue)
254          phValue->clear();
255        RegCloseKey(hKey);
256      }
257    }
258    return returnValue;
259  #endif // _WIN32
260  }
261  
262  namespace llvm {
263  
archToWindowsSDKArch(Triple::ArchType Arch)264  const char *archToWindowsSDKArch(Triple::ArchType Arch) {
265    switch (Arch) {
266    case Triple::ArchType::x86:
267      return "x86";
268    case Triple::ArchType::x86_64:
269      return "x64";
270    case Triple::ArchType::arm:
271    case Triple::ArchType::thumb:
272      return "arm";
273    case Triple::ArchType::aarch64:
274      return "arm64";
275    default:
276      return "";
277    }
278  }
279  
archToLegacyVCArch(Triple::ArchType Arch)280  const char *archToLegacyVCArch(Triple::ArchType Arch) {
281    switch (Arch) {
282    case Triple::ArchType::x86:
283      // x86 is default in legacy VC toolchains.
284      // e.g. x86 libs are directly in /lib as opposed to /lib/x86.
285      return "";
286    case Triple::ArchType::x86_64:
287      return "amd64";
288    case Triple::ArchType::arm:
289    case Triple::ArchType::thumb:
290      return "arm";
291    case Triple::ArchType::aarch64:
292      return "arm64";
293    default:
294      return "";
295    }
296  }
297  
archToDevDivInternalArch(Triple::ArchType Arch)298  const char *archToDevDivInternalArch(Triple::ArchType Arch) {
299    switch (Arch) {
300    case Triple::ArchType::x86:
301      return "i386";
302    case Triple::ArchType::x86_64:
303      return "amd64";
304    case Triple::ArchType::arm:
305    case Triple::ArchType::thumb:
306      return "arm";
307    case Triple::ArchType::aarch64:
308      return "arm64";
309    default:
310      return "";
311    }
312  }
313  
appendArchToWindowsSDKLibPath(int SDKMajor,SmallString<128> LibPath,Triple::ArchType Arch,std::string & path)314  bool appendArchToWindowsSDKLibPath(int SDKMajor, SmallString<128> LibPath,
315                                     Triple::ArchType Arch, std::string &path) {
316    if (SDKMajor >= 8) {
317      sys::path::append(LibPath, archToWindowsSDKArch(Arch));
318    } else {
319      switch (Arch) {
320      // In Windows SDK 7.x, x86 libraries are directly in the Lib folder.
321      case Triple::x86:
322        break;
323      case Triple::x86_64:
324        sys::path::append(LibPath, "x64");
325        break;
326      case Triple::arm:
327      case Triple::thumb:
328        // It is not necessary to link against Windows SDK 7.x when targeting ARM.
329        return false;
330      default:
331        return false;
332      }
333    }
334  
335    path = std::string(LibPath);
336    return true;
337  }
338  
getSubDirectoryPath(SubDirectoryType Type,ToolsetLayout VSLayout,const std::string & VCToolChainPath,Triple::ArchType TargetArch,StringRef SubdirParent)339  std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout,
340                                  const std::string &VCToolChainPath,
341                                  Triple::ArchType TargetArch,
342                                  StringRef SubdirParent) {
343    const char *SubdirName;
344    const char *IncludeName;
345    switch (VSLayout) {
346    case ToolsetLayout::OlderVS:
347      SubdirName = archToLegacyVCArch(TargetArch);
348      IncludeName = "include";
349      break;
350    case ToolsetLayout::VS2017OrNewer:
351      SubdirName = archToWindowsSDKArch(TargetArch);
352      IncludeName = "include";
353      break;
354    case ToolsetLayout::DevDivInternal:
355      SubdirName = archToDevDivInternalArch(TargetArch);
356      IncludeName = "inc";
357      break;
358    }
359  
360    SmallString<256> Path(VCToolChainPath);
361    if (!SubdirParent.empty())
362      sys::path::append(Path, SubdirParent);
363  
364    switch (Type) {
365    case SubDirectoryType::Bin:
366      if (VSLayout == ToolsetLayout::VS2017OrNewer) {
367        // MSVC ships with two linkers: a 32-bit x86 and 64-bit x86 linker.
368        // On x86, pick the linker that corresponds to the current process.
369        // On ARM64, pick the 32-bit x86 linker; the 64-bit one doesn't run
370        // on Windows 10.
371        //
372        // FIXME: Consider using IsWow64GuestMachineSupported to figure out
373        // if we can invoke the 64-bit linker. It's generally preferable
374        // because it won't run out of address-space.
375        const bool HostIsX64 =
376            Triple(sys::getProcessTriple()).getArch() == Triple::x86_64;
377        const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86";
378        sys::path::append(Path, "bin", HostName, SubdirName);
379      } else { // OlderVS or DevDivInternal
380        sys::path::append(Path, "bin", SubdirName);
381      }
382      break;
383    case SubDirectoryType::Include:
384      sys::path::append(Path, IncludeName);
385      break;
386    case SubDirectoryType::Lib:
387      sys::path::append(Path, "lib", SubdirName);
388      break;
389    }
390    return std::string(Path);
391  }
392  
useUniversalCRT(ToolsetLayout VSLayout,const std::string & VCToolChainPath,Triple::ArchType TargetArch,vfs::FileSystem & VFS)393  bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath,
394                       Triple::ArchType TargetArch, vfs::FileSystem &VFS) {
395    SmallString<128> TestPath(getSubDirectoryPath(
396        SubDirectoryType::Include, VSLayout, VCToolChainPath, TargetArch));
397    sys::path::append(TestPath, "stdlib.h");
398    return !VFS.exists(TestPath);
399  }
400  
getWindowsSDKDir(vfs::FileSystem & VFS,std::optional<StringRef> WinSdkDir,std::optional<StringRef> WinSdkVersion,std::optional<StringRef> WinSysRoot,std::string & Path,int & Major,std::string & WindowsSDKIncludeVersion,std::string & WindowsSDKLibVersion)401  bool getWindowsSDKDir(vfs::FileSystem &VFS, std::optional<StringRef> WinSdkDir,
402                        std::optional<StringRef> WinSdkVersion,
403                        std::optional<StringRef> WinSysRoot, std::string &Path,
404                        int &Major, std::string &WindowsSDKIncludeVersion,
405                        std::string &WindowsSDKLibVersion) {
406    // Trust /winsdkdir and /winsdkversion if present.
407    if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot,
408                                       Path, Major, WindowsSDKIncludeVersion)) {
409      WindowsSDKLibVersion = WindowsSDKIncludeVersion;
410      return true;
411    }
412  
413    // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to
414    // registry.
415  
416    // Try the Windows registry.
417    std::string RegistrySDKVersion;
418    if (!getSystemRegistryString(
419            "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION",
420            "InstallationFolder", Path, &RegistrySDKVersion))
421      return false;
422    if (Path.empty() || RegistrySDKVersion.empty())
423      return false;
424  
425    WindowsSDKIncludeVersion.clear();
426    WindowsSDKLibVersion.clear();
427    Major = 0;
428    std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major);
429    if (Major <= 7)
430      return true;
431    if (Major == 8) {
432      // Windows SDK 8.x installs libraries in a folder whose names depend on the
433      // version of the OS you're targeting.  By default choose the newest, which
434      // usually corresponds to the version of the OS you've installed the SDK on.
435      const char *Tests[] = {"winv6.3", "win8", "win7"};
436      for (const char *Test : Tests) {
437        SmallString<128> TestPath(Path);
438        sys::path::append(TestPath, "Lib", Test);
439        if (VFS.exists(TestPath)) {
440          WindowsSDKLibVersion = Test;
441          break;
442        }
443      }
444      return !WindowsSDKLibVersion.empty();
445    }
446    if (Major == 10) {
447      if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion))
448        return false;
449      WindowsSDKLibVersion = WindowsSDKIncludeVersion;
450      return true;
451    }
452    // Unsupported SDK version
453    return false;
454  }
455  
getUniversalCRTSdkDir(vfs::FileSystem & VFS,std::optional<StringRef> WinSdkDir,std::optional<StringRef> WinSdkVersion,std::optional<StringRef> WinSysRoot,std::string & Path,std::string & UCRTVersion)456  bool getUniversalCRTSdkDir(vfs::FileSystem &VFS,
457                             std::optional<StringRef> WinSdkDir,
458                             std::optional<StringRef> WinSdkVersion,
459                             std::optional<StringRef> WinSysRoot,
460                             std::string &Path, std::string &UCRTVersion) {
461    // If /winsdkdir is passed, use it as location for the UCRT too.
462    // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir?
463    int Major;
464    if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot,
465                                       Path, Major, UCRTVersion))
466      return true;
467  
468    // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to
469    // registry.
470  
471    // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry
472    // for the specific key "KitsRoot10". So do we.
473    if (!getSystemRegistryString(
474            "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10",
475            Path, nullptr))
476      return false;
477  
478    return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion);
479  }
480  
findVCToolChainViaCommandLine(vfs::FileSystem & VFS,std::optional<StringRef> VCToolsDir,std::optional<StringRef> VCToolsVersion,std::optional<StringRef> WinSysRoot,std::string & Path,ToolsetLayout & VSLayout)481  bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS,
482                                     std::optional<StringRef> VCToolsDir,
483                                     std::optional<StringRef> VCToolsVersion,
484                                     std::optional<StringRef> WinSysRoot,
485                                     std::string &Path, ToolsetLayout &VSLayout) {
486    // Don't validate the input; trust the value supplied by the user.
487    // The primary motivation is to prevent unnecessary file and registry access.
488    if (VCToolsDir || WinSysRoot) {
489      if (WinSysRoot) {
490        SmallString<128> ToolsPath(*WinSysRoot);
491        sys::path::append(ToolsPath, "VC", "Tools", "MSVC");
492        std::string ToolsVersion;
493        if (VCToolsVersion)
494          ToolsVersion = VCToolsVersion->str();
495        else
496          ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath);
497        sys::path::append(ToolsPath, ToolsVersion);
498        Path = std::string(ToolsPath);
499      } else {
500        Path = VCToolsDir->str();
501      }
502      VSLayout = ToolsetLayout::VS2017OrNewer;
503      return true;
504    }
505    return false;
506  }
507  
findVCToolChainViaEnvironment(vfs::FileSystem & VFS,std::string & Path,ToolsetLayout & VSLayout)508  bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path,
509                                     ToolsetLayout &VSLayout) {
510    // These variables are typically set by vcvarsall.bat
511    // when launching a developer command prompt.
512    if (std::optional<std::string> VCToolsInstallDir =
513            sys::Process::GetEnv("VCToolsInstallDir")) {
514      // This is only set by newer Visual Studios, and it leads straight to
515      // the toolchain directory.
516      Path = std::move(*VCToolsInstallDir);
517      VSLayout = ToolsetLayout::VS2017OrNewer;
518      return true;
519    }
520    if (std::optional<std::string> VCInstallDir =
521            sys::Process::GetEnv("VCINSTALLDIR")) {
522      // If the previous variable isn't set but this one is, then we've found
523      // an older Visual Studio. This variable is set by newer Visual Studios too,
524      // so this check has to appear second.
525      // In older Visual Studios, the VC directory is the toolchain.
526      Path = std::move(*VCInstallDir);
527      VSLayout = ToolsetLayout::OlderVS;
528      return true;
529    }
530  
531    // We couldn't find any VC environment variables. Let's walk through PATH and
532    // see if it leads us to a VC toolchain bin directory. If it does, pick the
533    // first one that we find.
534    if (std::optional<std::string> PathEnv = sys::Process::GetEnv("PATH")) {
535      SmallVector<StringRef, 8> PathEntries;
536      StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator);
537      for (StringRef PathEntry : PathEntries) {
538        if (PathEntry.empty())
539          continue;
540  
541        SmallString<256> ExeTestPath;
542  
543        // If cl.exe doesn't exist, then this definitely isn't a VC toolchain.
544        ExeTestPath = PathEntry;
545        sys::path::append(ExeTestPath, "cl.exe");
546        if (!VFS.exists(ExeTestPath))
547          continue;
548  
549        // cl.exe existing isn't a conclusive test for a VC toolchain; clang also
550        // has a cl.exe. So let's check for link.exe too.
551        ExeTestPath = PathEntry;
552        sys::path::append(ExeTestPath, "link.exe");
553        if (!VFS.exists(ExeTestPath))
554          continue;
555  
556        // whatever/VC/bin --> old toolchain, VC dir is toolchain dir.
557        StringRef TestPath = PathEntry;
558        bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin");
559        if (!IsBin) {
560          // Strip any architecture subdir like "amd64".
561          TestPath = sys::path::parent_path(TestPath);
562          IsBin = sys::path::filename(TestPath).equals_insensitive("bin");
563        }
564        if (IsBin) {
565          StringRef ParentPath = sys::path::parent_path(TestPath);
566          StringRef ParentFilename = sys::path::filename(ParentPath);
567          if (ParentFilename.equals_insensitive("VC")) {
568            Path = std::string(ParentPath);
569            VSLayout = ToolsetLayout::OlderVS;
570            return true;
571          }
572          if (ParentFilename.equals_insensitive("x86ret") ||
573              ParentFilename.equals_insensitive("x86chk") ||
574              ParentFilename.equals_insensitive("amd64ret") ||
575              ParentFilename.equals_insensitive("amd64chk")) {
576            Path = std::string(ParentPath);
577            VSLayout = ToolsetLayout::DevDivInternal;
578            return true;
579          }
580  
581        } else {
582          // This could be a new (>=VS2017) toolchain. If it is, we should find
583          // path components with these prefixes when walking backwards through
584          // the path.
585          // Note: empty strings match anything.
586          StringRef ExpectedPrefixes[] = {"",     "Host",  "bin", "",
587                                          "MSVC", "Tools", "VC"};
588  
589          auto It = sys::path::rbegin(PathEntry);
590          auto End = sys::path::rend(PathEntry);
591          for (StringRef Prefix : ExpectedPrefixes) {
592            if (It == End)
593              goto NotAToolChain;
594            if (!It->starts_with_insensitive(Prefix))
595              goto NotAToolChain;
596            ++It;
597          }
598  
599          // We've found a new toolchain!
600          // Back up 3 times (/bin/Host/arch) to get the root path.
601          StringRef ToolChainPath(PathEntry);
602          for (int i = 0; i < 3; ++i)
603            ToolChainPath = sys::path::parent_path(ToolChainPath);
604  
605          Path = std::string(ToolChainPath);
606          VSLayout = ToolsetLayout::VS2017OrNewer;
607          return true;
608        }
609  
610      NotAToolChain:
611        continue;
612      }
613    }
614    return false;
615  }
616  
findVCToolChainViaSetupConfig(vfs::FileSystem & VFS,std::optional<StringRef> VCToolsVersion,std::string & Path,ToolsetLayout & VSLayout)617  bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS,
618                                     std::optional<StringRef> VCToolsVersion,
619                                     std::string &Path, ToolsetLayout &VSLayout) {
620  #if !defined(USE_MSVC_SETUP_API)
621    return false;
622  #else
623    // FIXME: This really should be done once in the top-level program's main
624    // function, as it may have already been initialized with a different
625    // threading model otherwise.
626    sys::InitializeCOMRAII COM(sys::COMThreadingMode::SingleThreaded);
627    HRESULT HR;
628  
629    // _com_ptr_t will throw a _com_error if a COM calls fail.
630    // The LLVM coding standards forbid exception handling, so we'll have to
631    // stop them from being thrown in the first place.
632    // The destructor will put the regular error handler back when we leave
633    // this scope.
634    struct SuppressCOMErrorsRAII {
635      static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {}
636  
637      SuppressCOMErrorsRAII() { _set_com_error_handler(handler); }
638  
639      ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); }
640  
641    } COMErrorSuppressor;
642  
643    ISetupConfigurationPtr Query;
644    HR = Query.CreateInstance(__uuidof(SetupConfiguration));
645    if (FAILED(HR))
646      return false;
647  
648    IEnumSetupInstancesPtr EnumInstances;
649    HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances);
650    if (FAILED(HR))
651      return false;
652  
653    ISetupInstancePtr Instance;
654    HR = EnumInstances->Next(1, &Instance, nullptr);
655    if (HR != S_OK)
656      return false;
657  
658    ISetupInstancePtr NewestInstance;
659    std::optional<uint64_t> NewestVersionNum;
660    do {
661      bstr_t VersionString;
662      uint64_t VersionNum;
663      HR = Instance->GetInstallationVersion(VersionString.GetAddress());
664      if (FAILED(HR))
665        continue;
666      HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum);
667      if (FAILED(HR))
668        continue;
669      if (!NewestVersionNum || (VersionNum > NewestVersionNum)) {
670        NewestInstance = Instance;
671        NewestVersionNum = VersionNum;
672      }
673    } while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK);
674  
675    if (!NewestInstance)
676      return false;
677  
678    bstr_t VCPathWide;
679    HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress());
680    if (FAILED(HR))
681      return false;
682  
683    std::string VCRootPath;
684    convertWideToUTF8(std::wstring(VCPathWide), VCRootPath);
685  
686    std::string ToolsVersion;
687    if (VCToolsVersion.has_value()) {
688      ToolsVersion = *VCToolsVersion;
689    } else {
690      SmallString<256> ToolsVersionFilePath(VCRootPath);
691      sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build",
692                        "Microsoft.VCToolsVersion.default.txt");
693  
694      auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath);
695      if (!ToolsVersionFile)
696        return false;
697  
698      ToolsVersion = ToolsVersionFile->get()->getBuffer().rtrim();
699    }
700  
701  
702    SmallString<256> ToolchainPath(VCRootPath);
703    sys::path::append(ToolchainPath, "Tools", "MSVC", ToolsVersion);
704    auto Status = VFS.status(ToolchainPath);
705    if (!Status || !Status->isDirectory())
706      return false;
707  
708    Path = std::string(ToolchainPath.str());
709    VSLayout = ToolsetLayout::VS2017OrNewer;
710    return true;
711  #endif
712  }
713  
findVCToolChainViaRegistry(std::string & Path,ToolsetLayout & VSLayout)714  bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout) {
715    std::string VSInstallPath;
716    if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)",
717                                "InstallDir", VSInstallPath, nullptr) ||
718        getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)",
719                                "InstallDir", VSInstallPath, nullptr)) {
720      if (!VSInstallPath.empty()) {
721        auto pos = VSInstallPath.find(R"(\Common7\IDE)");
722        if (pos == std::string::npos)
723          return false;
724        SmallString<256> VCPath(StringRef(VSInstallPath.c_str(), pos));
725        sys::path::append(VCPath, "VC");
726  
727        Path = std::string(VCPath);
728        VSLayout = ToolsetLayout::OlderVS;
729        return true;
730      }
731    }
732    return false;
733  }
734  
735  } // namespace llvm
736