Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/WindowsDriver/MSVCPaths.cpp
35233 views
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
737