1 //===-- MSVCPaths.cpp - MSVC path-parsing helpers -------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
26 #include "llvm/Support/ConvertUTF.h"
30 #define WIN32_LEAN_AND_MEAN
39 // Don't support SetupApi on MinGW.
40 #define USE_MSVC_SETUP_API
42 // Make sure this comes before MSVCSetupApi.h
45 #include "llvm/Support/COM.h"
47 #pragma clang diagnostic push
48 #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
50 #include "llvm/WindowsDriver/MSVCSetupApi.h"
52 #pragma clang diagnostic pop
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
));
63 getHighestNumericTupleInDirectory(llvm::vfs::FileSystem
&VFS
,
64 llvm::StringRef Directory
) {
66 llvm::VersionTuple HighestTuple
;
69 for (llvm::vfs::directory_iterator DirIt
= VFS
.dir_begin(Directory
, EC
),
71 !EC
&& DirIt
!= DirEnd
; DirIt
.increment(EC
)) {
72 auto Status
= VFS
.status(DirIt
->path());
73 if (!Status
|| !Status
->isDirectory())
75 llvm::StringRef CandidateName
= llvm::sys::path::filename(DirIt
->path());
76 llvm::VersionTuple Tuple
;
77 if (Tuple
.tryParse(CandidateName
)) // tryParse() returns true on error.
79 if (Tuple
> HighestTuple
) {
81 Highest
= CandidateName
.str();
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();
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
;
107 SDKVersion
.tryParse(*WinSdkVersion
);
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()));
115 llvm::sys::path::append(
116 SDKPath
, getHighestNumericTupleInDirectory(VFS
, SDKPath
));
117 Path
= std::string(SDKPath
);
119 Path
= WinSdkDir
->str();
122 if (!SDKVersion
.empty()) {
123 Major
= SDKVersion
.getMajor();
124 Version
= SDKVersion
.getAsString();
125 } else if (getWindows10SDKVersionFromPath(VFS
, Path
, Version
)) {
134 static bool readFullStringValue(HKEY hkey
, const char *valueName
,
135 std::string
&value
) {
136 std::wstring WideValueName
;
137 if (!llvm::ConvertUTF8toWide(valueName
, WideValueName
))
143 // First just query for the required size.
144 result
= RegQueryValueExW(hkey
, WideValueName
.c_str(), NULL
, &type
, NULL
,
146 if (result
!= ERROR_SUCCESS
|| type
!= REG_SZ
|| !valueSize
)
148 std::vector
<BYTE
> buffer(valueSize
);
149 result
= RegQueryValueExW(hkey
, WideValueName
.c_str(), NULL
, NULL
, &buffer
[0],
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();
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.
161 return llvm::convertWideToUTF8(WideValue
, value
);
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
) {
180 HKEY hRootKey
= HKEY_LOCAL_MACHINE
;
183 bool returnValue
= false;
185 const char *placeHolder
= strstr(keyPath
, "$VERSION");
186 std::string bestName
;
187 // If we have a $VERSION placeholder, do the highest-version search.
189 const char *keyEnd
= placeHolder
- 1;
190 const char *nextKey
= placeHolder
;
191 // Find end of previous key.
192 while ((keyEnd
> keyPath
) && (*keyEnd
!= '\\'))
194 // Find end of key containing $VERSION.
195 while (*nextKey
&& (*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';
204 lResult
= RegOpenKeyExA(hRootKey
, partialKey
, 0, KEY_READ
| KEY_WOW64_32KEY
,
206 if (lResult
== ERROR_SUCCESS
) {
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
;
213 const char *sp
= keyName
;
214 while (*sp
&& !llvm::isDigit(*sp
))
218 const char *ep
= sp
+ 1;
219 while (*ep
&& (llvm::isDigit(*ep
) || (*ep
== '.')))
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.
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
)) {
243 size
= sizeof(keyName
) - 1;
245 RegCloseKey(hTopKey
);
249 RegOpenKeyExA(hRootKey
, keyPath
, 0, KEY_READ
| KEY_WOW64_32KEY
, &hKey
);
250 if (lResult
== ERROR_SUCCESS
) {
251 if (readFullStringValue(hKey
, valueName
, value
))
264 const char *archToWindowsSDKArch(Triple::ArchType Arch
) {
266 case Triple::ArchType::x86
:
268 case Triple::ArchType::x86_64
:
270 case Triple::ArchType::arm
:
272 case Triple::ArchType::aarch64
:
279 const char *archToLegacyVCArch(Triple::ArchType 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.
285 case Triple::ArchType::x86_64
:
287 case Triple::ArchType::arm
:
289 case Triple::ArchType::aarch64
:
296 const char *archToDevDivInternalArch(Triple::ArchType Arch
) {
298 case Triple::ArchType::x86
:
300 case Triple::ArchType::x86_64
:
302 case Triple::ArchType::arm
:
304 case Triple::ArchType::aarch64
:
311 bool appendArchToWindowsSDKLibPath(int SDKMajor
, SmallString
<128> LibPath
,
312 Triple::ArchType Arch
, std::string
&path
) {
314 sys::path::append(LibPath
, archToWindowsSDKArch(Arch
));
317 // In Windows SDK 7.x, x86 libraries are directly in the Lib folder.
321 sys::path::append(LibPath
, "x64");
324 // It is not necessary to link against Windows SDK 7.x when targeting ARM.
331 path
= std::string(LibPath
);
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
;
342 case ToolsetLayout::OlderVS
:
343 SubdirName
= archToLegacyVCArch(TargetArch
);
344 IncludeName
= "include";
346 case ToolsetLayout::VS2017OrNewer
:
347 SubdirName
= archToWindowsSDKArch(TargetArch
);
348 IncludeName
= "include";
350 case ToolsetLayout::DevDivInternal
:
351 SubdirName
= archToDevDivInternalArch(TargetArch
);
356 SmallString
<256> Path(VCToolChainPath
);
357 if (!SubdirParent
.empty())
358 sys::path::append(Path
, SubdirParent
);
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
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
);
379 case SubDirectoryType::Include
:
380 sys::path::append(Path
, IncludeName
);
382 case SubDirectoryType::Lib
:
383 sys::path::append(Path
, "lib", SubdirName
);
386 return std::string(Path
);
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
);
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
;
409 // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to
412 // Try the Windows registry.
413 std::string RegistrySDKVersion
;
414 if (!getSystemRegistryString(
415 "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION",
416 "InstallationFolder", Path
, &RegistrySDKVersion
))
418 if (Path
.empty() || RegistrySDKVersion
.empty())
421 WindowsSDKIncludeVersion
.clear();
422 WindowsSDKLibVersion
.clear();
424 std::sscanf(RegistrySDKVersion
.c_str(), "v%d.", &Major
);
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
;
440 return !WindowsSDKLibVersion
.empty();
443 if (!getWindows10SDKVersionFromPath(VFS
, Path
, WindowsSDKIncludeVersion
))
445 WindowsSDKLibVersion
= WindowsSDKIncludeVersion
;
448 // Unsupported SDK version
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?
460 if (getWindowsSDKDirViaCommandLine(VFS
, WinSdkDir
, WinSdkVersion
, WinSysRoot
,
461 Path
, Major
, UCRTVersion
))
464 // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to
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",
474 return getWindows10SDKVersionFromPath(VFS
, Path
, UCRTVersion
);
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
) {
486 SmallString
<128> ToolsPath(*WinSysRoot
);
487 sys::path::append(ToolsPath
, "VC", "Tools", "MSVC");
488 std::string ToolsVersion
;
490 ToolsVersion
= VCToolsVersion
->str();
492 ToolsVersion
= getHighestNumericTupleInDirectory(VFS
, ToolsPath
);
493 sys::path::append(ToolsPath
, ToolsVersion
);
494 Path
= std::string(ToolsPath
);
496 Path
= VCToolsDir
->str();
498 VSLayout
= ToolsetLayout::VS2017OrNewer
;
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
;
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
;
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())
537 SmallString
<256> ExeTestPath
;
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
))
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
))
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");
556 // Strip any architecture subdir like "amd64".
557 TestPath
= sys::path::parent_path(TestPath
);
558 IsBin
= sys::path::filename(TestPath
).equals_insensitive("bin");
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
;
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
;
578 // This could be a new (>=VS2017) toolchain. If it is, we should find
579 // path components with these prefixes when walking backwards through
581 // Note: empty strings match anything.
582 StringRef ExpectedPrefixes
[] = {"", "Host", "bin", "",
583 "MSVC", "Tools", "VC"};
585 auto It
= sys::path::rbegin(PathEntry
);
586 auto End
= sys::path::rend(PathEntry
);
587 for (StringRef Prefix
: ExpectedPrefixes
) {
590 if (!It
->starts_with_insensitive(Prefix
))
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
);
601 Path
= std::string(ToolChainPath
);
602 VSLayout
= ToolsetLayout::VS2017OrNewer
;
613 bool findVCToolChainViaSetupConfig(vfs::FileSystem
&VFS
,
614 std::optional
<StringRef
> VCToolsVersion
,
615 std::string
&Path
, ToolsetLayout
&VSLayout
) {
616 #if !defined(USE_MSVC_SETUP_API)
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
);
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
630 struct SuppressCOMErrorsRAII
{
631 static void __stdcall
handler(HRESULT hr
, IErrorInfo
*perrinfo
) {}
633 SuppressCOMErrorsRAII() { _set_com_error_handler(handler
); }
635 ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error
); }
637 } COMErrorSuppressor
;
639 ISetupConfigurationPtr Query
;
640 HR
= Query
.CreateInstance(__uuidof(SetupConfiguration
));
644 IEnumSetupInstancesPtr EnumInstances
;
645 HR
= ISetupConfiguration2Ptr(Query
)->EnumAllInstances(&EnumInstances
);
649 ISetupInstancePtr Instance
;
650 HR
= EnumInstances
->Next(1, &Instance
, nullptr);
654 ISetupInstancePtr NewestInstance
;
655 std::optional
<uint64_t> NewestVersionNum
;
657 bstr_t VersionString
;
659 HR
= Instance
->GetInstallationVersion(VersionString
.GetAddress());
662 HR
= ISetupHelperPtr(Query
)->ParseVersion(VersionString
, &VersionNum
);
665 if (!NewestVersionNum
|| (VersionNum
> NewestVersionNum
)) {
666 NewestInstance
= Instance
;
667 NewestVersionNum
= VersionNum
;
669 } while ((HR
= EnumInstances
->Next(1, &Instance
, nullptr)) == S_OK
);
675 HR
= NewestInstance
->ResolvePath(L
"VC", VCPathWide
.GetAddress());
679 std::string VCRootPath
;
680 convertWideToUTF8(std::wstring(VCPathWide
), VCRootPath
);
682 std::string ToolsVersion
;
683 if (VCToolsVersion
.has_value()) {
684 ToolsVersion
= *VCToolsVersion
;
686 SmallString
<256> ToolsVersionFilePath(VCRootPath
);
687 sys::path::append(ToolsVersionFilePath
, "Auxiliary", "Build",
688 "Microsoft.VCToolsVersion.default.txt");
690 auto ToolsVersionFile
= MemoryBuffer::getFile(ToolsVersionFilePath
);
691 if (!ToolsVersionFile
)
694 ToolsVersion
= ToolsVersionFile
->get()->getBuffer().rtrim();
698 SmallString
<256> ToolchainPath(VCRootPath
);
699 sys::path::append(ToolchainPath
, "Tools", "MSVC", ToolsVersion
);
700 auto Status
= VFS
.status(ToolchainPath
);
701 if (!Status
|| !Status
->isDirectory())
704 Path
= std::string(ToolchainPath
.str());
705 VSLayout
= ToolsetLayout::VS2017OrNewer
;
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
)
720 SmallString
<256> VCPath(StringRef(VSInstallPath
.c_str(), pos
));
721 sys::path::append(VCPath
, "VC");
723 Path
= std::string(VCPath
);
724 VSLayout
= ToolsetLayout::OlderVS
;