1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmGlobalVisualStudioVersionedGenerator.h"
4
5 #include <cmext/string_view>
6
7 #include "cmsys/FStream.hxx"
8 #include "cmsys/Glob.hxx"
9
10 #include "cmAlgorithms.h"
11 #include "cmDocumentationEntry.h"
12 #include "cmLocalVisualStudio10Generator.h"
13 #include "cmMakefile.h"
14 #include "cmStringAlgorithms.h"
15 #include "cmVSSetupHelper.h"
16 #include "cmake.h"
17
18 #if defined(_M_ARM64)
19 # define HOST_PLATFORM_NAME "ARM64"
20 # define HOST_TOOLS_ARCH ""
21 #elif defined(_M_ARM)
22 # define HOST_PLATFORM_NAME "ARM"
23 # define HOST_TOOLS_ARCH ""
24 #elif defined(_M_IA64)
25 # define HOST_PLATFORM_NAME "Itanium"
26 # define HOST_TOOLS_ARCH ""
27 #elif defined(_WIN64)
28 # define HOST_PLATFORM_NAME "x64"
29 # define HOST_TOOLS_ARCH "x64"
30 #else
VSIsWow64()31 static bool VSIsWow64()
32 {
33 BOOL isWow64 = false;
34 return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
35 }
36 #endif
37
VSHostPlatformName()38 static std::string VSHostPlatformName()
39 {
40 #ifdef HOST_PLATFORM_NAME
41 return HOST_PLATFORM_NAME;
42 #else
43 if (VSIsWow64()) {
44 return "x64";
45 } else {
46 return "Win32";
47 }
48 #endif
49 }
50
VSHostArchitecture()51 static std::string VSHostArchitecture()
52 {
53 #ifdef HOST_TOOLS_ARCH
54 return HOST_TOOLS_ARCH;
55 #else
56 if (VSIsWow64()) {
57 return "x64";
58 } else {
59 return "x86";
60 }
61 #endif
62 }
63
VSVersionToMajor(cmGlobalVisualStudioGenerator::VSVersion v)64 static unsigned int VSVersionToMajor(
65 cmGlobalVisualStudioGenerator::VSVersion v)
66 {
67 switch (v) {
68 case cmGlobalVisualStudioGenerator::VS9:
69 return 9;
70 case cmGlobalVisualStudioGenerator::VS10:
71 return 10;
72 case cmGlobalVisualStudioGenerator::VS11:
73 return 11;
74 case cmGlobalVisualStudioGenerator::VS12:
75 return 12;
76 case cmGlobalVisualStudioGenerator::VS14:
77 return 14;
78 case cmGlobalVisualStudioGenerator::VS15:
79 return 15;
80 case cmGlobalVisualStudioGenerator::VS16:
81 return 16;
82 case cmGlobalVisualStudioGenerator::VS17:
83 return 17;
84 }
85 return 0;
86 }
87
VSVersionToToolset(cmGlobalVisualStudioGenerator::VSVersion v)88 static const char* VSVersionToToolset(
89 cmGlobalVisualStudioGenerator::VSVersion v)
90 {
91 switch (v) {
92 case cmGlobalVisualStudioGenerator::VS9:
93 return "v90";
94 case cmGlobalVisualStudioGenerator::VS10:
95 return "v100";
96 case cmGlobalVisualStudioGenerator::VS11:
97 return "v110";
98 case cmGlobalVisualStudioGenerator::VS12:
99 return "v120";
100 case cmGlobalVisualStudioGenerator::VS14:
101 return "v140";
102 case cmGlobalVisualStudioGenerator::VS15:
103 return "v141";
104 case cmGlobalVisualStudioGenerator::VS16:
105 return "v142";
106 case cmGlobalVisualStudioGenerator::VS17:
107 return "v143";
108 }
109 return "";
110 }
111
VSVersionToAndroidToolset(cmGlobalVisualStudioGenerator::VSVersion v)112 static const char* VSVersionToAndroidToolset(
113 cmGlobalVisualStudioGenerator::VSVersion v)
114 {
115 switch (v) {
116 case cmGlobalVisualStudioGenerator::VS9:
117 case cmGlobalVisualStudioGenerator::VS10:
118 case cmGlobalVisualStudioGenerator::VS11:
119 case cmGlobalVisualStudioGenerator::VS12:
120 return "";
121 case cmGlobalVisualStudioGenerator::VS14:
122 return "Clang_3_8";
123 case cmGlobalVisualStudioGenerator::VS15:
124 case cmGlobalVisualStudioGenerator::VS16:
125 case cmGlobalVisualStudioGenerator::VS17:
126 return "Clang_5_0";
127 }
128 return "";
129 }
130
131 static const char vs15generatorName[] = "Visual Studio 15 2017";
132
133 // Map generator name without year to name with year.
cmVS15GenName(const std::string & name,std::string & genName)134 static const char* cmVS15GenName(const std::string& name, std::string& genName)
135 {
136 if (strncmp(name.c_str(), vs15generatorName,
137 sizeof(vs15generatorName) - 6) != 0) {
138 return 0;
139 }
140 const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
141 if (cmHasLiteralPrefix(p, " 2017")) {
142 p += 5;
143 }
144 genName = std::string(vs15generatorName) + p;
145 return p;
146 }
147
148 class cmGlobalVisualStudioVersionedGenerator::Factory15
149 : public cmGlobalGeneratorFactory
150 {
151 public:
CreateGlobalGenerator(const std::string & name,bool allowArch,cmake * cm) const152 std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
153 const std::string& name, bool allowArch, cmake* cm) const override
154 {
155 std::string genName;
156 const char* p = cmVS15GenName(name, genName);
157 if (!p) {
158 return std::unique_ptr<cmGlobalGenerator>();
159 }
160 if (!*p) {
161 return std::unique_ptr<cmGlobalGenerator>(
162 new cmGlobalVisualStudioVersionedGenerator(
163 cmGlobalVisualStudioGenerator::VS15, cm, genName, ""));
164 }
165 if (!allowArch || *p++ != ' ') {
166 return std::unique_ptr<cmGlobalGenerator>();
167 }
168 if (strcmp(p, "Win64") == 0) {
169 return std::unique_ptr<cmGlobalGenerator>(
170 new cmGlobalVisualStudioVersionedGenerator(
171 cmGlobalVisualStudioGenerator::VS15, cm, genName, "x64"));
172 }
173 if (strcmp(p, "ARM") == 0) {
174 return std::unique_ptr<cmGlobalGenerator>(
175 new cmGlobalVisualStudioVersionedGenerator(
176 cmGlobalVisualStudioGenerator::VS15, cm, genName, "ARM"));
177 }
178 return std::unique_ptr<cmGlobalGenerator>();
179 }
180
GetDocumentation(cmDocumentationEntry & entry) const181 void GetDocumentation(cmDocumentationEntry& entry) const override
182 {
183 entry.Name = std::string(vs15generatorName) + " [arch]";
184 entry.Brief = "Generates Visual Studio 2017 project files. "
185 "Optional [arch] can be \"Win64\" or \"ARM\".";
186 }
187
GetGeneratorNames() const188 std::vector<std::string> GetGeneratorNames() const override
189 {
190 std::vector<std::string> names;
191 names.push_back(vs15generatorName);
192 return names;
193 }
194
GetGeneratorNamesWithPlatform() const195 std::vector<std::string> GetGeneratorNamesWithPlatform() const override
196 {
197 std::vector<std::string> names;
198 names.push_back(vs15generatorName + std::string(" ARM"));
199 names.push_back(vs15generatorName + std::string(" Win64"));
200 return names;
201 }
202
SupportsToolset() const203 bool SupportsToolset() const override { return true; }
SupportsPlatform() const204 bool SupportsPlatform() const override { return true; }
205
GetKnownPlatforms() const206 std::vector<std::string> GetKnownPlatforms() const override
207 {
208 std::vector<std::string> platforms;
209 platforms.emplace_back("x64");
210 platforms.emplace_back("Win32");
211 platforms.emplace_back("ARM");
212 platforms.emplace_back("ARM64");
213 return platforms;
214 }
215
GetDefaultPlatformName() const216 std::string GetDefaultPlatformName() const override { return "Win32"; }
217 };
218
219 std::unique_ptr<cmGlobalGeneratorFactory>
NewFactory15()220 cmGlobalVisualStudioVersionedGenerator::NewFactory15()
221 {
222 return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory15);
223 }
224
225 static const char vs16generatorName[] = "Visual Studio 16 2019";
226 static const char vs17generatorName[] = "Visual Studio 17 2022";
227
228 // Map generator name without year to name with year.
cmVS16GenName(const std::string & name,std::string & genName)229 static const char* cmVS16GenName(const std::string& name, std::string& genName)
230 {
231 if (strncmp(name.c_str(), vs16generatorName,
232 sizeof(vs16generatorName) - 6) != 0) {
233 return 0;
234 }
235 const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
236 if (cmHasLiteralPrefix(p, " 2019")) {
237 p += 5;
238 }
239 genName = std::string(vs16generatorName) + p;
240 return p;
241 }
242
cmVS17GenName(const std::string & name,std::string & genName)243 static const char* cmVS17GenName(const std::string& name, std::string& genName)
244 {
245 if (strncmp(name.c_str(), vs17generatorName,
246 sizeof(vs17generatorName) - 6) != 0) {
247 return 0;
248 }
249 const char* p = name.c_str() + sizeof(vs17generatorName) - 6;
250 if (cmHasLiteralPrefix(p, " 2022")) {
251 p += 5;
252 }
253 genName = std::string(vs17generatorName) + p;
254 return p;
255 }
256
257 class cmGlobalVisualStudioVersionedGenerator::Factory16
258 : public cmGlobalGeneratorFactory
259 {
260 public:
CreateGlobalGenerator(const std::string & name,bool,cmake * cm) const261 std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
262 const std::string& name, bool /*allowArch*/, cmake* cm) const override
263 {
264 std::string genName;
265 const char* p = cmVS16GenName(name, genName);
266 if (!p) {
267 return std::unique_ptr<cmGlobalGenerator>();
268 }
269 if (!*p) {
270 return std::unique_ptr<cmGlobalGenerator>(
271 new cmGlobalVisualStudioVersionedGenerator(
272 cmGlobalVisualStudioGenerator::VS16, cm, genName, ""));
273 }
274 return std::unique_ptr<cmGlobalGenerator>();
275 }
276
GetDocumentation(cmDocumentationEntry & entry) const277 void GetDocumentation(cmDocumentationEntry& entry) const override
278 {
279 entry.Name = std::string(vs16generatorName);
280 entry.Brief = "Generates Visual Studio 2019 project files. "
281 "Use -A option to specify architecture.";
282 }
283
GetGeneratorNames() const284 std::vector<std::string> GetGeneratorNames() const override
285 {
286 std::vector<std::string> names;
287 names.push_back(vs16generatorName);
288 return names;
289 }
290
GetGeneratorNamesWithPlatform() const291 std::vector<std::string> GetGeneratorNamesWithPlatform() const override
292 {
293 return std::vector<std::string>();
294 }
295
SupportsToolset() const296 bool SupportsToolset() const override { return true; }
SupportsPlatform() const297 bool SupportsPlatform() const override { return true; }
298
GetKnownPlatforms() const299 std::vector<std::string> GetKnownPlatforms() const override
300 {
301 std::vector<std::string> platforms;
302 platforms.emplace_back("x64");
303 platforms.emplace_back("Win32");
304 platforms.emplace_back("ARM");
305 platforms.emplace_back("ARM64");
306 platforms.emplace_back("ARM64EC");
307 return platforms;
308 }
309
GetDefaultPlatformName() const310 std::string GetDefaultPlatformName() const override
311 {
312 return VSHostPlatformName();
313 }
314 };
315
316 std::unique_ptr<cmGlobalGeneratorFactory>
NewFactory16()317 cmGlobalVisualStudioVersionedGenerator::NewFactory16()
318 {
319 return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory16);
320 }
321
322 class cmGlobalVisualStudioVersionedGenerator::Factory17
323 : public cmGlobalGeneratorFactory
324 {
325 public:
CreateGlobalGenerator(const std::string & name,bool,cmake * cm) const326 std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
327 const std::string& name, bool /*allowArch*/, cmake* cm) const override
328 {
329 std::string genName;
330 const char* p = cmVS17GenName(name, genName);
331 if (!p) {
332 return std::unique_ptr<cmGlobalGenerator>();
333 }
334 if (!*p) {
335 return std::unique_ptr<cmGlobalGenerator>(
336 new cmGlobalVisualStudioVersionedGenerator(
337 cmGlobalVisualStudioGenerator::VS17, cm, genName, ""));
338 }
339 return std::unique_ptr<cmGlobalGenerator>();
340 }
341
GetDocumentation(cmDocumentationEntry & entry) const342 void GetDocumentation(cmDocumentationEntry& entry) const override
343 {
344 entry.Name = std::string(vs17generatorName);
345 entry.Brief = "Generates Visual Studio 2022 project files. "
346 "Use -A option to specify architecture.";
347 }
348
GetGeneratorNames() const349 std::vector<std::string> GetGeneratorNames() const override
350 {
351 std::vector<std::string> names;
352 names.push_back(vs17generatorName);
353 return names;
354 }
355
GetGeneratorNamesWithPlatform() const356 std::vector<std::string> GetGeneratorNamesWithPlatform() const override
357 {
358 return std::vector<std::string>();
359 }
360
SupportsToolset() const361 bool SupportsToolset() const override { return true; }
SupportsPlatform() const362 bool SupportsPlatform() const override { return true; }
363
GetKnownPlatforms() const364 std::vector<std::string> GetKnownPlatforms() const override
365 {
366 std::vector<std::string> platforms;
367 platforms.emplace_back("x64");
368 platforms.emplace_back("Win32");
369 platforms.emplace_back("ARM");
370 platforms.emplace_back("ARM64");
371 platforms.emplace_back("ARM64EC");
372 return platforms;
373 }
374
GetDefaultPlatformName() const375 std::string GetDefaultPlatformName() const override
376 {
377 return VSHostPlatformName();
378 }
379 };
380
381 std::unique_ptr<cmGlobalGeneratorFactory>
NewFactory17()382 cmGlobalVisualStudioVersionedGenerator::NewFactory17()
383 {
384 return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory17);
385 }
386
cmGlobalVisualStudioVersionedGenerator(VSVersion version,cmake * cm,const std::string & name,std::string const & platformInGeneratorName)387 cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
388 VSVersion version, cmake* cm, const std::string& name,
389 std::string const& platformInGeneratorName)
390 : cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
391 , vsSetupAPIHelper(VSVersionToMajor(version))
392 {
393 this->Version = version;
394 this->ExpressEdition = false;
395 this->DefaultPlatformToolset = VSVersionToToolset(this->Version);
396 this->DefaultAndroidToolset = VSVersionToAndroidToolset(this->Version);
397 this->DefaultCLFlagTableName = VSVersionToToolset(this->Version);
398 this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version);
399 this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version);
400 if (this->Version >= cmGlobalVisualStudioGenerator::VS16) {
401 this->DefaultPlatformName = VSHostPlatformName();
402 this->DefaultPlatformToolsetHostArchitecture = VSHostArchitecture();
403 }
404 if (this->Version >= cmGlobalVisualStudioGenerator::VS17) {
405 // FIXME: Search for an existing framework? Under '%ProgramFiles(x86)%',
406 // see 'Reference Assemblies\Microsoft\Framework\.NETFramework'.
407 // Use a version installed by VS 2022 without a separate component.
408 this->DefaultTargetFrameworkVersion = "v4.7.2";
409 }
410 }
411
MatchesGeneratorName(const std::string & name) const412 bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
413 const std::string& name) const
414 {
415 std::string genName;
416 switch (this->Version) {
417 case cmGlobalVisualStudioGenerator::VS9:
418 case cmGlobalVisualStudioGenerator::VS10:
419 case cmGlobalVisualStudioGenerator::VS11:
420 case cmGlobalVisualStudioGenerator::VS12:
421 case cmGlobalVisualStudioGenerator::VS14:
422 break;
423 case cmGlobalVisualStudioGenerator::VS15:
424 if (cmVS15GenName(name, genName)) {
425 return genName == this->GetName();
426 }
427 break;
428 case cmGlobalVisualStudioGenerator::VS16:
429 if (cmVS16GenName(name, genName)) {
430 return genName == this->GetName();
431 }
432 break;
433 case cmGlobalVisualStudioGenerator::VS17:
434 if (cmVS17GenName(name, genName)) {
435 return genName == this->GetName();
436 }
437 break;
438 }
439 return false;
440 }
441
SetGeneratorInstance(std::string const & i,cmMakefile * mf)442 bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
443 std::string const& i, cmMakefile* mf)
444 {
445 if (this->LastGeneratorInstanceString &&
446 i == *(this->LastGeneratorInstanceString)) {
447 return true;
448 }
449
450 if (!i.empty()) {
451 if (!this->vsSetupAPIHelper.SetVSInstance(i)) {
452 std::ostringstream e;
453 /* clang-format off */
454 e <<
455 "Generator\n"
456 " " << this->GetName() << "\n"
457 "could not find specified instance of Visual Studio:\n"
458 " " << i;
459 /* clang-format on */
460 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
461 return false;
462 }
463 }
464
465 std::string vsInstance;
466 if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
467 std::ostringstream e;
468 /* clang-format off */
469 e <<
470 "Generator\n"
471 " " << this->GetName() << "\n"
472 "could not find any instance of Visual Studio.\n";
473 /* clang-format on */
474 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
475 return false;
476 }
477
478 // Save the selected instance persistently.
479 std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
480 if (vsInstance != genInstance) {
481 this->CMakeInstance->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", vsInstance,
482 "Generator instance identifier.",
483 cmStateEnums::INTERNAL);
484 }
485
486 // The selected instance may have a different MSBuild than previously found.
487 this->MSBuildCommandInitialized = false;
488
489 this->LastGeneratorInstanceString = i;
490
491 return true;
492 }
493
GetVSInstance(std::string & dir) const494 bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
495 std::string& dir) const
496 {
497 return vsSetupAPIHelper.GetVSInstanceInfo(dir);
498 }
499
500 cm::optional<std::string>
GetVSInstanceVersion() const501 cmGlobalVisualStudioVersionedGenerator::GetVSInstanceVersion() const
502 {
503 cm::optional<std::string> result;
504 std::string vsInstanceVersion;
505 if (vsSetupAPIHelper.GetVSInstanceVersion(vsInstanceVersion)) {
506 result = vsInstanceVersion;
507 }
508 return result;
509 }
510
IsStdOutEncodingSupported() const511 bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const
512 {
513 // Supported from Visual Studio 16.7 Preview 3.
514 if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
515 return true;
516 }
517 if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
518 return false;
519 }
520 static std::string const vsVer16_7_P2 = "16.7.30128.36";
521 cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
522 return (vsVer &&
523 cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_7_P2));
524 }
525
IsUtf8EncodingSupported() const526 bool cmGlobalVisualStudioVersionedGenerator::IsUtf8EncodingSupported() const
527 {
528 // Supported from Visual Studio 16.10 Preview 2.
529 if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
530 return true;
531 }
532 if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
533 return false;
534 }
535 static std::string const vsVer16_10_P2 = "16.10.31213.239";
536 cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
537 return (vsVer &&
538 cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_10_P2));
539 }
540
541 const char*
GetAndroidApplicationTypeRevision() const542 cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
543 const
544 {
545 switch (this->Version) {
546 case cmGlobalVisualStudioGenerator::VS9:
547 case cmGlobalVisualStudioGenerator::VS10:
548 case cmGlobalVisualStudioGenerator::VS11:
549 case cmGlobalVisualStudioGenerator::VS12:
550 return "";
551 case cmGlobalVisualStudioGenerator::VS14:
552 return "2.0";
553 case cmGlobalVisualStudioGenerator::VS15:
554 case cmGlobalVisualStudioGenerator::VS16:
555 case cmGlobalVisualStudioGenerator::VS17:
556 return "3.0";
557 }
558 return "";
559 }
560
561 cmGlobalVisualStudioVersionedGenerator::AuxToolset
FindAuxToolset(std::string & version,std::string & props) const562 cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
563 std::string& version, std::string& props) const
564 {
565 if (version.empty()) {
566 return AuxToolset::None;
567 }
568
569 std::string instancePath;
570 this->GetVSInstance(instancePath);
571 cmSystemTools::ConvertToUnixSlashes(instancePath);
572
573 // Translate three-component format accepted by "vcvarsall -vcvars_ver=".
574 cmsys::RegularExpression threeComponent(
575 "^([0-9]+\\.[0-9]+)\\.[0-9][0-9][0-9][0-9][0-9]$");
576 if (threeComponent.find(version)) {
577 // Load "VC/Auxiliary/Build/*/Microsoft.VCToolsVersion.*.txt" files
578 // with two matching components to check their three-component version.
579 std::string const& twoComponent = threeComponent.match(1);
580 std::string pattern =
581 cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
582 "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
583 cmsys::Glob glob;
584 glob.SetRecurseThroughSymlinks(false);
585 if (glob.FindFiles(pattern)) {
586 for (std::string const& txt : glob.GetFiles()) {
587 std::string ver;
588 cmsys::ifstream fin(txt.c_str());
589 if (fin && std::getline(fin, ver)) {
590 // Strip trailing whitespace.
591 ver = ver.substr(0, ver.find_first_not_of("0123456789."));
592 // If the three-component version matches, translate it to
593 // that used by the "Microsoft.VCToolsVersion.*.txt" file name.
594 if (ver == version) {
595 cmsys::RegularExpression extractVersion(
596 "VCToolsVersion\\.([0-9.]+)\\.txt$");
597 if (extractVersion.find(txt)) {
598 version = extractVersion.match(1);
599 break;
600 }
601 }
602 }
603 }
604 }
605 }
606
607 if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
608 props = cmStrCat(instancePath, "/VC/Auxiliary/Build."_s, version,
609 "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
610 if (cmSystemTools::PathExists(props)) {
611 return AuxToolset::PropsExist;
612 }
613 }
614 props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, version,
615 "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
616 if (cmSystemTools::PathExists(props)) {
617 return AuxToolset::PropsExist;
618 }
619
620 // Accept the toolset version that is default in the current VS version
621 // by matching the name later VS versions will use for the SxS props files.
622 std::string vcToolsetVersion;
623 if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
624 // Accept an exact-match (three-component version).
625 if (version == vcToolsetVersion) {
626 return AuxToolset::Default;
627 }
628
629 // Accept known SxS props file names using four version components
630 // in VS versions later than the current.
631 if (version == "14.28.16.9" && vcToolsetVersion == "14.28.29910") {
632 return AuxToolset::Default;
633 }
634 if (version == "14.29.16.10" && vcToolsetVersion == "14.29.30037") {
635 return AuxToolset::Default;
636 }
637 if (version == "14.29.16.11" && vcToolsetVersion == "14.29.30133") {
638 return AuxToolset::Default;
639 }
640
641 // The first two components of the default toolset version typically
642 // match the name used by later VS versions for the SxS props files.
643 cmsys::RegularExpression twoComponent("^([0-9]+\\.[0-9]+)");
644 if (twoComponent.find(version)) {
645 std::string const versionPrefix = cmStrCat(twoComponent.match(1), '.');
646 if (cmHasPrefix(vcToolsetVersion, versionPrefix)) {
647 return AuxToolset::Default;
648 }
649 }
650 }
651
652 return AuxToolset::PropsMissing;
653 }
654
InitializeWindows(cmMakefile * mf)655 bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
656 {
657 // If the Win 8.1 SDK is installed then we can select a SDK matching
658 // the target Windows version.
659 if (this->IsWin81SDKInstalled()) {
660 // VS 2019 does not default to 8.1 so specify it explicitly when needed.
661 if (this->Version >= cmGlobalVisualStudioGenerator::VS16 &&
662 !cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
663 this->SetWindowsTargetPlatformVersion("8.1", mf);
664 return true;
665 }
666 return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
667 }
668 // Otherwise we must choose a Win 10 SDK even if we are not targeting
669 // Windows 10.
670 return this->SelectWindows10SDK(mf, false);
671 }
672
SelectWindowsStoreToolset(std::string & toolset) const673 bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
674 std::string& toolset) const
675 {
676 if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
677 if (this->IsWindowsStoreToolsetInstalled() &&
678 this->IsWindowsDesktopToolsetInstalled()) {
679 toolset = VSVersionToToolset(this->Version);
680 return true;
681 } else {
682 return false;
683 }
684 }
685 return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
686 toolset);
687 }
688
IsWindowsDesktopToolsetInstalled() const689 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
690 const
691 {
692 return vsSetupAPIHelper.IsVSInstalled();
693 }
694
IsWindowsStoreToolsetInstalled() const695 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
696 const
697 {
698 return vsSetupAPIHelper.IsWin10SDKInstalled();
699 }
700
IsWin81SDKInstalled() const701 bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
702 {
703 // Does the VS installer tool know about one?
704 if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
705 return true;
706 }
707
708 // Does the registry know about one (e.g. from VS 2015)?
709 std::string win81Root;
710 if (cmSystemTools::ReadRegistryValue(
711 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"
712 "Windows Kits\\Installed Roots;KitsRoot81",
713 win81Root, cmSystemTools::KeyWOW64_32) ||
714 cmSystemTools::ReadRegistryValue(
715 "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
716 "Windows Kits\\Installed Roots;KitsRoot81",
717 win81Root, cmSystemTools::KeyWOW64_32)) {
718 return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
719 true);
720 }
721 return false;
722 }
723
724 std::string
GetWindows10SDKMaxVersionDefault(cmMakefile *) const725 cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersionDefault(
726 cmMakefile*) const
727 {
728 return std::string();
729 }
730
731 cm::optional<std::string>
FindMSBuildCommandEarly(cmMakefile * mf)732 cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommandEarly(cmMakefile* mf)
733 {
734 std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
735 if (!this->SetGeneratorInstance(instance, mf)) {
736 cmSystemTools::SetFatalErrorOccured();
737 return {};
738 }
739 return this->cmGlobalVisualStudio14Generator::FindMSBuildCommandEarly(mf);
740 }
741
FindMSBuildCommand()742 std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
743 {
744 std::string msbuild;
745
746 // Ask Visual Studio Installer tool.
747 std::string vs;
748 if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
749 if (this->Version >= cmGlobalVisualStudioGenerator::VS17) {
750 msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
751 if (cmSystemTools::FileExists(msbuild)) {
752 return msbuild;
753 }
754 }
755 msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
756 if (cmSystemTools::FileExists(msbuild)) {
757 return msbuild;
758 }
759 msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
760 if (cmSystemTools::FileExists(msbuild)) {
761 return msbuild;
762 }
763 }
764
765 msbuild = "MSBuild.exe";
766 return msbuild;
767 }
768
FindDevEnvCommand()769 std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
770 {
771 std::string devenv;
772
773 // Ask Visual Studio Installer tool.
774 std::string vs;
775 if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
776 devenv = vs + "/Common7/IDE/devenv.com";
777 if (cmSystemTools::FileExists(devenv)) {
778 return devenv;
779 }
780 }
781
782 devenv = "devenv.com";
783 return devenv;
784 }
785