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 (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion)) 448 return false; 449 WindowsSDKLibVersion = WindowsSDKIncludeVersion; 450 return true; 451 } 452 // Unsupported SDK version 453 return false; 454 } 455 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 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 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 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 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