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 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 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 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 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. 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 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 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 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 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 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 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 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 (WinSdkVersion) { 448 // Use the user-provided version as-is. 449 WindowsSDKIncludeVersion = WinSdkVersion->str(); 450 WindowsSDKLibVersion = WindowsSDKIncludeVersion; 451 return true; 452 } 453 454 if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion)) 455 return false; 456 WindowsSDKLibVersion = WindowsSDKIncludeVersion; 457 return true; 458 } 459 // Unsupported SDK version 460 return false; 461 } 462 463 bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, 464 std::optional<StringRef> WinSdkDir, 465 std::optional<StringRef> WinSdkVersion, 466 std::optional<StringRef> WinSysRoot, 467 std::string &Path, std::string &UCRTVersion) { 468 // If /winsdkdir is passed, use it as location for the UCRT too. 469 // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir? 470 int Major; 471 if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, 472 Path, Major, UCRTVersion)) 473 return true; 474 475 // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to 476 // registry. 477 478 // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry 479 // for the specific key "KitsRoot10". So do we. 480 if (!getSystemRegistryString( 481 "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", 482 Path, nullptr)) 483 return false; 484 485 if (WinSdkVersion) { 486 // Use the user-provided version as-is. 487 UCRTVersion = WinSdkVersion->str(); 488 return true; 489 } 490 491 return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion); 492 } 493 494 bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS, 495 std::optional<StringRef> VCToolsDir, 496 std::optional<StringRef> VCToolsVersion, 497 std::optional<StringRef> WinSysRoot, 498 std::string &Path, ToolsetLayout &VSLayout) { 499 // Don't validate the input; trust the value supplied by the user. 500 // The primary motivation is to prevent unnecessary file and registry access. 501 if (VCToolsDir || WinSysRoot) { 502 if (WinSysRoot) { 503 SmallString<128> ToolsPath(*WinSysRoot); 504 sys::path::append(ToolsPath, "VC", "Tools", "MSVC"); 505 std::string ToolsVersion; 506 if (VCToolsVersion) 507 ToolsVersion = VCToolsVersion->str(); 508 else 509 ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath); 510 sys::path::append(ToolsPath, ToolsVersion); 511 Path = std::string(ToolsPath); 512 } else { 513 Path = VCToolsDir->str(); 514 } 515 VSLayout = ToolsetLayout::VS2017OrNewer; 516 return true; 517 } 518 return false; 519 } 520 521 bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path, 522 ToolsetLayout &VSLayout) { 523 // These variables are typically set by vcvarsall.bat 524 // when launching a developer command prompt. 525 if (std::optional<std::string> VCToolsInstallDir = 526 sys::Process::GetEnv("VCToolsInstallDir")) { 527 // This is only set by newer Visual Studios, and it leads straight to 528 // the toolchain directory. 529 Path = std::move(*VCToolsInstallDir); 530 VSLayout = ToolsetLayout::VS2017OrNewer; 531 return true; 532 } 533 if (std::optional<std::string> VCInstallDir = 534 sys::Process::GetEnv("VCINSTALLDIR")) { 535 // If the previous variable isn't set but this one is, then we've found 536 // an older Visual Studio. This variable is set by newer Visual Studios too, 537 // so this check has to appear second. 538 // In older Visual Studios, the VC directory is the toolchain. 539 Path = std::move(*VCInstallDir); 540 VSLayout = ToolsetLayout::OlderVS; 541 return true; 542 } 543 544 // We couldn't find any VC environment variables. Let's walk through PATH and 545 // see if it leads us to a VC toolchain bin directory. If it does, pick the 546 // first one that we find. 547 if (std::optional<std::string> PathEnv = sys::Process::GetEnv("PATH")) { 548 SmallVector<StringRef, 8> PathEntries; 549 StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator); 550 for (StringRef PathEntry : PathEntries) { 551 if (PathEntry.empty()) 552 continue; 553 554 SmallString<256> ExeTestPath; 555 556 // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. 557 ExeTestPath = PathEntry; 558 sys::path::append(ExeTestPath, "cl.exe"); 559 if (!VFS.exists(ExeTestPath)) 560 continue; 561 562 // cl.exe existing isn't a conclusive test for a VC toolchain; clang also 563 // has a cl.exe. So let's check for link.exe too. 564 ExeTestPath = PathEntry; 565 sys::path::append(ExeTestPath, "link.exe"); 566 if (!VFS.exists(ExeTestPath)) 567 continue; 568 569 // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. 570 StringRef TestPath = PathEntry; 571 bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); 572 if (!IsBin) { 573 // Strip any architecture subdir like "amd64". 574 TestPath = sys::path::parent_path(TestPath); 575 IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); 576 } 577 if (IsBin) { 578 StringRef ParentPath = sys::path::parent_path(TestPath); 579 StringRef ParentFilename = sys::path::filename(ParentPath); 580 if (ParentFilename.equals_insensitive("VC")) { 581 Path = std::string(ParentPath); 582 VSLayout = ToolsetLayout::OlderVS; 583 return true; 584 } 585 if (ParentFilename.equals_insensitive("x86ret") || 586 ParentFilename.equals_insensitive("x86chk") || 587 ParentFilename.equals_insensitive("amd64ret") || 588 ParentFilename.equals_insensitive("amd64chk")) { 589 Path = std::string(ParentPath); 590 VSLayout = ToolsetLayout::DevDivInternal; 591 return true; 592 } 593 594 } else { 595 // This could be a new (>=VS2017) toolchain. If it is, we should find 596 // path components with these prefixes when walking backwards through 597 // the path. 598 // Note: empty strings match anything. 599 StringRef ExpectedPrefixes[] = {"", "Host", "bin", "", 600 "MSVC", "Tools", "VC"}; 601 602 auto It = sys::path::rbegin(PathEntry); 603 auto End = sys::path::rend(PathEntry); 604 for (StringRef Prefix : ExpectedPrefixes) { 605 if (It == End) 606 goto NotAToolChain; 607 if (!It->starts_with_insensitive(Prefix)) 608 goto NotAToolChain; 609 ++It; 610 } 611 612 // We've found a new toolchain! 613 // Back up 3 times (/bin/Host/arch) to get the root path. 614 StringRef ToolChainPath(PathEntry); 615 for (int i = 0; i < 3; ++i) 616 ToolChainPath = sys::path::parent_path(ToolChainPath); 617 618 Path = std::string(ToolChainPath); 619 VSLayout = ToolsetLayout::VS2017OrNewer; 620 return true; 621 } 622 623 NotAToolChain: 624 continue; 625 } 626 } 627 return false; 628 } 629 630 bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, 631 std::optional<StringRef> VCToolsVersion, 632 std::string &Path, ToolsetLayout &VSLayout) { 633 #if !defined(USE_MSVC_SETUP_API) 634 return false; 635 #else 636 // FIXME: This really should be done once in the top-level program's main 637 // function, as it may have already been initialized with a different 638 // threading model otherwise. 639 sys::InitializeCOMRAII COM(sys::COMThreadingMode::SingleThreaded); 640 HRESULT HR; 641 642 // _com_ptr_t will throw a _com_error if a COM calls fail. 643 // The LLVM coding standards forbid exception handling, so we'll have to 644 // stop them from being thrown in the first place. 645 // The destructor will put the regular error handler back when we leave 646 // this scope. 647 struct SuppressCOMErrorsRAII { 648 static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {} 649 650 SuppressCOMErrorsRAII() { _set_com_error_handler(handler); } 651 652 ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); } 653 654 } COMErrorSuppressor; 655 656 ISetupConfigurationPtr Query; 657 HR = Query.CreateInstance(__uuidof(SetupConfiguration)); 658 if (FAILED(HR)) 659 return false; 660 661 IEnumSetupInstancesPtr EnumInstances; 662 HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances); 663 if (FAILED(HR)) 664 return false; 665 666 ISetupInstancePtr Instance; 667 HR = EnumInstances->Next(1, &Instance, nullptr); 668 if (HR != S_OK) 669 return false; 670 671 ISetupInstancePtr NewestInstance; 672 std::optional<uint64_t> NewestVersionNum; 673 do { 674 bstr_t VersionString; 675 uint64_t VersionNum; 676 HR = Instance->GetInstallationVersion(VersionString.GetAddress()); 677 if (FAILED(HR)) 678 continue; 679 HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum); 680 if (FAILED(HR)) 681 continue; 682 if (!NewestVersionNum || (VersionNum > NewestVersionNum)) { 683 NewestInstance = Instance; 684 NewestVersionNum = VersionNum; 685 } 686 } while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK); 687 688 if (!NewestInstance) 689 return false; 690 691 bstr_t VCPathWide; 692 HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress()); 693 if (FAILED(HR)) 694 return false; 695 696 std::string VCRootPath; 697 convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); 698 699 std::string ToolsVersion; 700 if (VCToolsVersion.has_value()) { 701 ToolsVersion = *VCToolsVersion; 702 } else { 703 SmallString<256> ToolsVersionFilePath(VCRootPath); 704 sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", 705 "Microsoft.VCToolsVersion.default.txt"); 706 707 auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath); 708 if (!ToolsVersionFile) 709 return false; 710 711 ToolsVersion = ToolsVersionFile->get()->getBuffer().rtrim(); 712 } 713 714 715 SmallString<256> ToolchainPath(VCRootPath); 716 sys::path::append(ToolchainPath, "Tools", "MSVC", ToolsVersion); 717 auto Status = VFS.status(ToolchainPath); 718 if (!Status || !Status->isDirectory()) 719 return false; 720 721 Path = std::string(ToolchainPath.str()); 722 VSLayout = ToolsetLayout::VS2017OrNewer; 723 return true; 724 #endif 725 } 726 727 bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout) { 728 std::string VSInstallPath; 729 if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)", 730 "InstallDir", VSInstallPath, nullptr) || 731 getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)", 732 "InstallDir", VSInstallPath, nullptr)) { 733 if (!VSInstallPath.empty()) { 734 auto pos = VSInstallPath.find(R"(\Common7\IDE)"); 735 if (pos == std::string::npos) 736 return false; 737 SmallString<256> VCPath(StringRef(VSInstallPath.c_str(), pos)); 738 sys::path::append(VCPath, "VC"); 739 740 Path = std::string(VCPath); 741 VSLayout = ToolsetLayout::OlderVS; 742 return true; 743 } 744 } 745 return false; 746 } 747 748 } // namespace llvm 749