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.str()); 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 return "arm"; 272 case Triple::ArchType::aarch64: 273 return "arm64"; 274 default: 275 return ""; 276 } 277 } 278 279 const char *archToLegacyVCArch(Triple::ArchType Arch) { 280 switch (Arch) { 281 case Triple::ArchType::x86: 282 // x86 is default in legacy VC toolchains. 283 // e.g. x86 libs are directly in /lib as opposed to /lib/x86. 284 return ""; 285 case Triple::ArchType::x86_64: 286 return "amd64"; 287 case Triple::ArchType::arm: 288 return "arm"; 289 case Triple::ArchType::aarch64: 290 return "arm64"; 291 default: 292 return ""; 293 } 294 } 295 296 const char *archToDevDivInternalArch(Triple::ArchType Arch) { 297 switch (Arch) { 298 case Triple::ArchType::x86: 299 return "i386"; 300 case Triple::ArchType::x86_64: 301 return "amd64"; 302 case Triple::ArchType::arm: 303 return "arm"; 304 case Triple::ArchType::aarch64: 305 return "arm64"; 306 default: 307 return ""; 308 } 309 } 310 311 bool appendArchToWindowsSDKLibPath(int SDKMajor, SmallString<128> LibPath, 312 Triple::ArchType Arch, std::string &path) { 313 if (SDKMajor >= 8) { 314 sys::path::append(LibPath, archToWindowsSDKArch(Arch)); 315 } else { 316 switch (Arch) { 317 // In Windows SDK 7.x, x86 libraries are directly in the Lib folder. 318 case Triple::x86: 319 break; 320 case Triple::x86_64: 321 sys::path::append(LibPath, "x64"); 322 break; 323 case Triple::arm: 324 // It is not necessary to link against Windows SDK 7.x when targeting ARM. 325 return false; 326 default: 327 return false; 328 } 329 } 330 331 path = std::string(LibPath.str()); 332 return true; 333 } 334 335 std::string getSubDirectoryPath(SubDirectoryType Type, ToolsetLayout VSLayout, 336 const std::string &VCToolChainPath, 337 Triple::ArchType TargetArch, 338 StringRef SubdirParent) { 339 const char *SubdirName; 340 const char *IncludeName; 341 switch (VSLayout) { 342 case ToolsetLayout::OlderVS: 343 SubdirName = archToLegacyVCArch(TargetArch); 344 IncludeName = "include"; 345 break; 346 case ToolsetLayout::VS2017OrNewer: 347 SubdirName = archToWindowsSDKArch(TargetArch); 348 IncludeName = "include"; 349 break; 350 case ToolsetLayout::DevDivInternal: 351 SubdirName = archToDevDivInternalArch(TargetArch); 352 IncludeName = "inc"; 353 break; 354 } 355 356 SmallString<256> Path(VCToolChainPath); 357 if (!SubdirParent.empty()) 358 sys::path::append(Path, SubdirParent); 359 360 switch (Type) { 361 case SubDirectoryType::Bin: 362 if (VSLayout == ToolsetLayout::VS2017OrNewer) { 363 // MSVC ships with two linkers: a 32-bit x86 and 64-bit x86 linker. 364 // On x86, pick the linker that corresponds to the current process. 365 // On ARM64, pick the 32-bit x86 linker; the 64-bit one doesn't run 366 // on Windows 10. 367 // 368 // FIXME: Consider using IsWow64GuestMachineSupported to figure out 369 // if we can invoke the 64-bit linker. It's generally preferable 370 // because it won't run out of address-space. 371 const bool HostIsX64 = 372 Triple(sys::getProcessTriple()).getArch() == Triple::x86_64; 373 const char *const HostName = HostIsX64 ? "Hostx64" : "Hostx86"; 374 sys::path::append(Path, "bin", HostName, SubdirName); 375 } else { // OlderVS or DevDivInternal 376 sys::path::append(Path, "bin", SubdirName); 377 } 378 break; 379 case SubDirectoryType::Include: 380 sys::path::append(Path, IncludeName); 381 break; 382 case SubDirectoryType::Lib: 383 sys::path::append(Path, "lib", SubdirName); 384 break; 385 } 386 return std::string(Path.str()); 387 } 388 389 bool useUniversalCRT(ToolsetLayout VSLayout, const std::string &VCToolChainPath, 390 Triple::ArchType TargetArch, vfs::FileSystem &VFS) { 391 SmallString<128> TestPath(getSubDirectoryPath( 392 SubDirectoryType::Include, VSLayout, VCToolChainPath, TargetArch)); 393 sys::path::append(TestPath, "stdlib.h"); 394 return !VFS.exists(TestPath); 395 } 396 397 bool getWindowsSDKDir(vfs::FileSystem &VFS, std::optional<StringRef> WinSdkDir, 398 std::optional<StringRef> WinSdkVersion, 399 std::optional<StringRef> WinSysRoot, std::string &Path, 400 int &Major, std::string &WindowsSDKIncludeVersion, 401 std::string &WindowsSDKLibVersion) { 402 // Trust /winsdkdir and /winsdkversion if present. 403 if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, 404 Path, Major, WindowsSDKIncludeVersion)) { 405 WindowsSDKLibVersion = WindowsSDKIncludeVersion; 406 return true; 407 } 408 409 // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to 410 // registry. 411 412 // Try the Windows registry. 413 std::string RegistrySDKVersion; 414 if (!getSystemRegistryString( 415 "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", 416 "InstallationFolder", Path, &RegistrySDKVersion)) 417 return false; 418 if (Path.empty() || RegistrySDKVersion.empty()) 419 return false; 420 421 WindowsSDKIncludeVersion.clear(); 422 WindowsSDKLibVersion.clear(); 423 Major = 0; 424 std::sscanf(RegistrySDKVersion.c_str(), "v%d.", &Major); 425 if (Major <= 7) 426 return true; 427 if (Major == 8) { 428 // Windows SDK 8.x installs libraries in a folder whose names depend on the 429 // version of the OS you're targeting. By default choose the newest, which 430 // usually corresponds to the version of the OS you've installed the SDK on. 431 const char *Tests[] = {"winv6.3", "win8", "win7"}; 432 for (const char *Test : Tests) { 433 SmallString<128> TestPath(Path); 434 sys::path::append(TestPath, "Lib", Test); 435 if (VFS.exists(TestPath)) { 436 WindowsSDKLibVersion = Test; 437 break; 438 } 439 } 440 return !WindowsSDKLibVersion.empty(); 441 } 442 if (Major == 10) { 443 if (!getWindows10SDKVersionFromPath(VFS, Path, WindowsSDKIncludeVersion)) 444 return false; 445 WindowsSDKLibVersion = WindowsSDKIncludeVersion; 446 return true; 447 } 448 // Unsupported SDK version 449 return false; 450 } 451 452 bool getUniversalCRTSdkDir(vfs::FileSystem &VFS, 453 std::optional<StringRef> WinSdkDir, 454 std::optional<StringRef> WinSdkVersion, 455 std::optional<StringRef> WinSysRoot, 456 std::string &Path, std::string &UCRTVersion) { 457 // If /winsdkdir is passed, use it as location for the UCRT too. 458 // FIXME: Should there be a dedicated /ucrtdir to override /winsdkdir? 459 int Major; 460 if (getWindowsSDKDirViaCommandLine(VFS, WinSdkDir, WinSdkVersion, WinSysRoot, 461 Path, Major, UCRTVersion)) 462 return true; 463 464 // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to 465 // registry. 466 467 // vcvarsqueryregistry.bat for Visual Studio 2015 queries the registry 468 // for the specific key "KitsRoot10". So do we. 469 if (!getSystemRegistryString( 470 "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10", 471 Path, nullptr)) 472 return false; 473 474 return getWindows10SDKVersionFromPath(VFS, Path, UCRTVersion); 475 } 476 477 bool findVCToolChainViaCommandLine(vfs::FileSystem &VFS, 478 std::optional<StringRef> VCToolsDir, 479 std::optional<StringRef> VCToolsVersion, 480 std::optional<StringRef> WinSysRoot, 481 std::string &Path, ToolsetLayout &VSLayout) { 482 // Don't validate the input; trust the value supplied by the user. 483 // The primary motivation is to prevent unnecessary file and registry access. 484 if (VCToolsDir || WinSysRoot) { 485 if (WinSysRoot) { 486 SmallString<128> ToolsPath(*WinSysRoot); 487 sys::path::append(ToolsPath, "VC", "Tools", "MSVC"); 488 std::string ToolsVersion; 489 if (VCToolsVersion) 490 ToolsVersion = VCToolsVersion->str(); 491 else 492 ToolsVersion = getHighestNumericTupleInDirectory(VFS, ToolsPath); 493 sys::path::append(ToolsPath, ToolsVersion); 494 Path = std::string(ToolsPath.str()); 495 } else { 496 Path = VCToolsDir->str(); 497 } 498 VSLayout = ToolsetLayout::VS2017OrNewer; 499 return true; 500 } 501 return false; 502 } 503 504 bool findVCToolChainViaEnvironment(vfs::FileSystem &VFS, std::string &Path, 505 ToolsetLayout &VSLayout) { 506 // These variables are typically set by vcvarsall.bat 507 // when launching a developer command prompt. 508 if (std::optional<std::string> VCToolsInstallDir = 509 sys::Process::GetEnv("VCToolsInstallDir")) { 510 // This is only set by newer Visual Studios, and it leads straight to 511 // the toolchain directory. 512 Path = std::move(*VCToolsInstallDir); 513 VSLayout = ToolsetLayout::VS2017OrNewer; 514 return true; 515 } 516 if (std::optional<std::string> VCInstallDir = 517 sys::Process::GetEnv("VCINSTALLDIR")) { 518 // If the previous variable isn't set but this one is, then we've found 519 // an older Visual Studio. This variable is set by newer Visual Studios too, 520 // so this check has to appear second. 521 // In older Visual Studios, the VC directory is the toolchain. 522 Path = std::move(*VCInstallDir); 523 VSLayout = ToolsetLayout::OlderVS; 524 return true; 525 } 526 527 // We couldn't find any VC environment variables. Let's walk through PATH and 528 // see if it leads us to a VC toolchain bin directory. If it does, pick the 529 // first one that we find. 530 if (std::optional<std::string> PathEnv = sys::Process::GetEnv("PATH")) { 531 SmallVector<StringRef, 8> PathEntries; 532 StringRef(*PathEnv).split(PathEntries, sys::EnvPathSeparator); 533 for (StringRef PathEntry : PathEntries) { 534 if (PathEntry.empty()) 535 continue; 536 537 SmallString<256> ExeTestPath; 538 539 // If cl.exe doesn't exist, then this definitely isn't a VC toolchain. 540 ExeTestPath = PathEntry; 541 sys::path::append(ExeTestPath, "cl.exe"); 542 if (!VFS.exists(ExeTestPath)) 543 continue; 544 545 // cl.exe existing isn't a conclusive test for a VC toolchain; clang also 546 // has a cl.exe. So let's check for link.exe too. 547 ExeTestPath = PathEntry; 548 sys::path::append(ExeTestPath, "link.exe"); 549 if (!VFS.exists(ExeTestPath)) 550 continue; 551 552 // whatever/VC/bin --> old toolchain, VC dir is toolchain dir. 553 StringRef TestPath = PathEntry; 554 bool IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); 555 if (!IsBin) { 556 // Strip any architecture subdir like "amd64". 557 TestPath = sys::path::parent_path(TestPath); 558 IsBin = sys::path::filename(TestPath).equals_insensitive("bin"); 559 } 560 if (IsBin) { 561 StringRef ParentPath = sys::path::parent_path(TestPath); 562 StringRef ParentFilename = sys::path::filename(ParentPath); 563 if (ParentFilename.equals_insensitive("VC")) { 564 Path = std::string(ParentPath); 565 VSLayout = ToolsetLayout::OlderVS; 566 return true; 567 } 568 if (ParentFilename.equals_insensitive("x86ret") || 569 ParentFilename.equals_insensitive("x86chk") || 570 ParentFilename.equals_insensitive("amd64ret") || 571 ParentFilename.equals_insensitive("amd64chk")) { 572 Path = std::string(ParentPath); 573 VSLayout = ToolsetLayout::DevDivInternal; 574 return true; 575 } 576 577 } else { 578 // This could be a new (>=VS2017) toolchain. If it is, we should find 579 // path components with these prefixes when walking backwards through 580 // the path. 581 // Note: empty strings match anything. 582 StringRef ExpectedPrefixes[] = {"", "Host", "bin", "", 583 "MSVC", "Tools", "VC"}; 584 585 auto It = sys::path::rbegin(PathEntry); 586 auto End = sys::path::rend(PathEntry); 587 for (StringRef Prefix : ExpectedPrefixes) { 588 if (It == End) 589 goto NotAToolChain; 590 if (!It->starts_with_insensitive(Prefix)) 591 goto NotAToolChain; 592 ++It; 593 } 594 595 // We've found a new toolchain! 596 // Back up 3 times (/bin/Host/arch) to get the root path. 597 StringRef ToolChainPath(PathEntry); 598 for (int i = 0; i < 3; ++i) 599 ToolChainPath = sys::path::parent_path(ToolChainPath); 600 601 Path = std::string(ToolChainPath); 602 VSLayout = ToolsetLayout::VS2017OrNewer; 603 return true; 604 } 605 606 NotAToolChain: 607 continue; 608 } 609 } 610 return false; 611 } 612 613 bool findVCToolChainViaSetupConfig(vfs::FileSystem &VFS, 614 std::optional<StringRef> VCToolsVersion, 615 std::string &Path, ToolsetLayout &VSLayout) { 616 #if !defined(USE_MSVC_SETUP_API) 617 return false; 618 #else 619 // FIXME: This really should be done once in the top-level program's main 620 // function, as it may have already been initialized with a different 621 // threading model otherwise. 622 sys::InitializeCOMRAII COM(sys::COMThreadingMode::SingleThreaded); 623 HRESULT HR; 624 625 // _com_ptr_t will throw a _com_error if a COM calls fail. 626 // The LLVM coding standards forbid exception handling, so we'll have to 627 // stop them from being thrown in the first place. 628 // The destructor will put the regular error handler back when we leave 629 // this scope. 630 struct SuppressCOMErrorsRAII { 631 static void __stdcall handler(HRESULT hr, IErrorInfo *perrinfo) {} 632 633 SuppressCOMErrorsRAII() { _set_com_error_handler(handler); } 634 635 ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error); } 636 637 } COMErrorSuppressor; 638 639 ISetupConfigurationPtr Query; 640 HR = Query.CreateInstance(__uuidof(SetupConfiguration)); 641 if (FAILED(HR)) 642 return false; 643 644 IEnumSetupInstancesPtr EnumInstances; 645 HR = ISetupConfiguration2Ptr(Query)->EnumAllInstances(&EnumInstances); 646 if (FAILED(HR)) 647 return false; 648 649 ISetupInstancePtr Instance; 650 HR = EnumInstances->Next(1, &Instance, nullptr); 651 if (HR != S_OK) 652 return false; 653 654 ISetupInstancePtr NewestInstance; 655 std::optional<uint64_t> NewestVersionNum; 656 do { 657 bstr_t VersionString; 658 uint64_t VersionNum; 659 HR = Instance->GetInstallationVersion(VersionString.GetAddress()); 660 if (FAILED(HR)) 661 continue; 662 HR = ISetupHelperPtr(Query)->ParseVersion(VersionString, &VersionNum); 663 if (FAILED(HR)) 664 continue; 665 if (!NewestVersionNum || (VersionNum > NewestVersionNum)) { 666 NewestInstance = Instance; 667 NewestVersionNum = VersionNum; 668 } 669 } while ((HR = EnumInstances->Next(1, &Instance, nullptr)) == S_OK); 670 671 if (!NewestInstance) 672 return false; 673 674 bstr_t VCPathWide; 675 HR = NewestInstance->ResolvePath(L"VC", VCPathWide.GetAddress()); 676 if (FAILED(HR)) 677 return false; 678 679 std::string VCRootPath; 680 convertWideToUTF8(std::wstring(VCPathWide), VCRootPath); 681 682 std::string ToolsVersion; 683 if (VCToolsVersion.has_value()) { 684 ToolsVersion = *VCToolsVersion; 685 } else { 686 SmallString<256> ToolsVersionFilePath(VCRootPath); 687 sys::path::append(ToolsVersionFilePath, "Auxiliary", "Build", 688 "Microsoft.VCToolsVersion.default.txt"); 689 690 auto ToolsVersionFile = MemoryBuffer::getFile(ToolsVersionFilePath); 691 if (!ToolsVersionFile) 692 return false; 693 694 ToolsVersion = ToolsVersionFile->get()->getBuffer().rtrim(); 695 } 696 697 698 SmallString<256> ToolchainPath(VCRootPath); 699 sys::path::append(ToolchainPath, "Tools", "MSVC", ToolsVersion); 700 auto Status = VFS.status(ToolchainPath); 701 if (!Status || !Status->isDirectory()) 702 return false; 703 704 Path = std::string(ToolchainPath.str()); 705 VSLayout = ToolsetLayout::VS2017OrNewer; 706 return true; 707 #endif 708 } 709 710 bool findVCToolChainViaRegistry(std::string &Path, ToolsetLayout &VSLayout) { 711 std::string VSInstallPath; 712 if (getSystemRegistryString(R"(SOFTWARE\Microsoft\VisualStudio\$VERSION)", 713 "InstallDir", VSInstallPath, nullptr) || 714 getSystemRegistryString(R"(SOFTWARE\Microsoft\VCExpress\$VERSION)", 715 "InstallDir", VSInstallPath, nullptr)) { 716 if (!VSInstallPath.empty()) { 717 auto pos = VSInstallPath.find(R"(\Common7\IDE)"); 718 if (pos == std::string::npos) 719 return false; 720 SmallString<256> VCPath(StringRef(VSInstallPath.c_str(), pos)); 721 sys::path::append(VCPath, "VC"); 722 723 Path = std::string(VCPath.str()); 724 VSLayout = ToolsetLayout::OlderVS; 725 return true; 726 } 727 } 728 return false; 729 } 730 731 } // namespace llvm 732