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
:
271 case Triple::ArchType::thumb
:
273 case Triple::ArchType::aarch64
:
280 const char *archToLegacyVCArch(Triple::ArchType 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.
286 case Triple::ArchType::x86_64
:
288 case Triple::ArchType::arm
:
289 case Triple::ArchType::thumb
:
291 case Triple::ArchType::aarch64
:
298 const char *archToDevDivInternalArch(Triple::ArchType Arch
) {
300 case Triple::ArchType::x86
:
302 case Triple::ArchType::x86_64
:
304 case Triple::ArchType::arm
:
305 case Triple::ArchType::thumb
:
307 case Triple::ArchType::aarch64
:
314 bool appendArchToWindowsSDKLibPath(int SDKMajor
, SmallString
<128> LibPath
,
315 Triple::ArchType Arch
, std::string
&path
) {
317 sys::path::append(LibPath
, archToWindowsSDKArch(Arch
));
320 // In Windows SDK 7.x, x86 libraries are directly in the Lib folder.
324 sys::path::append(LibPath
, "x64");
328 // It is not necessary to link against Windows SDK 7.x when targeting ARM.
335 path
= std::string(LibPath
);
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
;
346 case ToolsetLayout::OlderVS
:
347 SubdirName
= archToLegacyVCArch(TargetArch
);
348 IncludeName
= "include";
350 case ToolsetLayout::VS2017OrNewer
:
351 SubdirName
= archToWindowsSDKArch(TargetArch
);
352 IncludeName
= "include";
354 case ToolsetLayout::DevDivInternal
:
355 SubdirName
= archToDevDivInternalArch(TargetArch
);
360 SmallString
<256> Path(VCToolChainPath
);
361 if (!SubdirParent
.empty())
362 sys::path::append(Path
, SubdirParent
);
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
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
);
383 case SubDirectoryType::Include
:
384 sys::path::append(Path
, IncludeName
);
386 case SubDirectoryType::Lib
:
387 sys::path::append(Path
, "lib", SubdirName
);
390 return std::string(Path
);
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
);
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
;
413 // FIXME: Try env vars (%WindowsSdkDir%, %UCRTVersion%) before going to
416 // Try the Windows registry.
417 std::string RegistrySDKVersion
;
418 if (!getSystemRegistryString(
419 "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION",
420 "InstallationFolder", Path
, &RegistrySDKVersion
))
422 if (Path
.empty() || RegistrySDKVersion
.empty())
425 WindowsSDKIncludeVersion
.clear();
426 WindowsSDKLibVersion
.clear();
428 std::sscanf(RegistrySDKVersion
.c_str(), "v%d.", &Major
);
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
;
444 return !WindowsSDKLibVersion
.empty();
447 if (!getWindows10SDKVersionFromPath(VFS
, Path
, WindowsSDKIncludeVersion
))
449 WindowsSDKLibVersion
= WindowsSDKIncludeVersion
;
452 // Unsupported SDK version
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?
464 if (getWindowsSDKDirViaCommandLine(VFS
, WinSdkDir
, WinSdkVersion
, WinSysRoot
,
465 Path
, Major
, UCRTVersion
))
468 // FIXME: Try env vars (%UniversalCRTSdkDir%, %UCRTVersion%) before going to
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",
478 return getWindows10SDKVersionFromPath(VFS
, Path
, UCRTVersion
);
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
) {
490 SmallString
<128> ToolsPath(*WinSysRoot
);
491 sys::path::append(ToolsPath
, "VC", "Tools", "MSVC");
492 std::string ToolsVersion
;
494 ToolsVersion
= VCToolsVersion
->str();
496 ToolsVersion
= getHighestNumericTupleInDirectory(VFS
, ToolsPath
);
497 sys::path::append(ToolsPath
, ToolsVersion
);
498 Path
= std::string(ToolsPath
);
500 Path
= VCToolsDir
->str();
502 VSLayout
= ToolsetLayout::VS2017OrNewer
;
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
;
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
;
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())
541 SmallString
<256> ExeTestPath
;
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
))
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
))
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");
560 // Strip any architecture subdir like "amd64".
561 TestPath
= sys::path::parent_path(TestPath
);
562 IsBin
= sys::path::filename(TestPath
).equals_insensitive("bin");
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
;
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
;
582 // This could be a new (>=VS2017) toolchain. If it is, we should find
583 // path components with these prefixes when walking backwards through
585 // Note: empty strings match anything.
586 StringRef ExpectedPrefixes
[] = {"", "Host", "bin", "",
587 "MSVC", "Tools", "VC"};
589 auto It
= sys::path::rbegin(PathEntry
);
590 auto End
= sys::path::rend(PathEntry
);
591 for (StringRef Prefix
: ExpectedPrefixes
) {
594 if (!It
->starts_with_insensitive(Prefix
))
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
);
605 Path
= std::string(ToolChainPath
);
606 VSLayout
= ToolsetLayout::VS2017OrNewer
;
617 bool findVCToolChainViaSetupConfig(vfs::FileSystem
&VFS
,
618 std::optional
<StringRef
> VCToolsVersion
,
619 std::string
&Path
, ToolsetLayout
&VSLayout
) {
620 #if !defined(USE_MSVC_SETUP_API)
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
);
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
634 struct SuppressCOMErrorsRAII
{
635 static void __stdcall
handler(HRESULT hr
, IErrorInfo
*perrinfo
) {}
637 SuppressCOMErrorsRAII() { _set_com_error_handler(handler
); }
639 ~SuppressCOMErrorsRAII() { _set_com_error_handler(_com_raise_error
); }
641 } COMErrorSuppressor
;
643 ISetupConfigurationPtr Query
;
644 HR
= Query
.CreateInstance(__uuidof(SetupConfiguration
));
648 IEnumSetupInstancesPtr EnumInstances
;
649 HR
= ISetupConfiguration2Ptr(Query
)->EnumAllInstances(&EnumInstances
);
653 ISetupInstancePtr Instance
;
654 HR
= EnumInstances
->Next(1, &Instance
, nullptr);
658 ISetupInstancePtr NewestInstance
;
659 std::optional
<uint64_t> NewestVersionNum
;
661 bstr_t VersionString
;
663 HR
= Instance
->GetInstallationVersion(VersionString
.GetAddress());
666 HR
= ISetupHelperPtr(Query
)->ParseVersion(VersionString
, &VersionNum
);
669 if (!NewestVersionNum
|| (VersionNum
> NewestVersionNum
)) {
670 NewestInstance
= Instance
;
671 NewestVersionNum
= VersionNum
;
673 } while ((HR
= EnumInstances
->Next(1, &Instance
, nullptr)) == S_OK
);
679 HR
= NewestInstance
->ResolvePath(L
"VC", VCPathWide
.GetAddress());
683 std::string VCRootPath
;
684 convertWideToUTF8(std::wstring(VCPathWide
), VCRootPath
);
686 std::string ToolsVersion
;
687 if (VCToolsVersion
.has_value()) {
688 ToolsVersion
= *VCToolsVersion
;
690 SmallString
<256> ToolsVersionFilePath(VCRootPath
);
691 sys::path::append(ToolsVersionFilePath
, "Auxiliary", "Build",
692 "Microsoft.VCToolsVersion.default.txt");
694 auto ToolsVersionFile
= MemoryBuffer::getFile(ToolsVersionFilePath
);
695 if (!ToolsVersionFile
)
698 ToolsVersion
= ToolsVersionFile
->get()->getBuffer().rtrim();
702 SmallString
<256> ToolchainPath(VCRootPath
);
703 sys::path::append(ToolchainPath
, "Tools", "MSVC", ToolsVersion
);
704 auto Status
= VFS
.status(ToolchainPath
);
705 if (!Status
|| !Status
->isDirectory())
708 Path
= std::string(ToolchainPath
.str());
709 VSLayout
= ToolsetLayout::VS2017OrNewer
;
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
)
724 SmallString
<256> VCPath(StringRef(VSInstallPath
.c_str(), pos
));
725 sys::path::append(VCPath
, "VC");
727 Path
= std::string(VCPath
);
728 VSLayout
= ToolsetLayout::OlderVS
;