Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Source / cmGlobalVisualStudioVersionedGenerator.cxx
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 <cstring>
6 #include <set>
7 #include <sstream>
8 #include <utility>
9 #include <vector>
10
11 #include <cmext/string_view>
12
13 #include "cmsys/FStream.hxx"
14 #include "cmsys/Glob.hxx"
15 #include "cmsys/RegularExpression.hxx"
16
17 #include "cmDocumentationEntry.h"
18 #include "cmGlobalGenerator.h"
19 #include "cmGlobalGeneratorFactory.h"
20 #include "cmMakefile.h"
21 #include "cmMessageType.h"
22 #include "cmStateTypes.h"
23 #include "cmStringAlgorithms.h"
24 #include "cmSystemTools.h"
25 #include "cmVSSetupHelper.h"
26 #include "cmake.h"
27
28 #ifndef IMAGE_FILE_MACHINE_ARM64
29 #  define IMAGE_FILE_MACHINE_ARM64 0xaa64 // ARM64 Little-Endian
30 #endif
31
32 static bool VSIsWow64()
33 {
34   BOOL isWow64 = false;
35   return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
36 }
37
38 static bool VSIsArm64Host()
39 {
40   typedef BOOL(WINAPI * CM_ISWOW64PROCESS2)(
41     HANDLE hProcess, USHORT * pProcessMachine, USHORT * pNativeMachine);
42
43 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
44 #  define CM_VS_GCC_DIAGNOSTIC_PUSHED
45 #  pragma GCC diagnostic push
46 #  pragma GCC diagnostic ignored "-Wcast-function-type"
47 #endif
48   static const CM_ISWOW64PROCESS2 s_IsWow64Process2Impl =
49     (CM_ISWOW64PROCESS2)GetProcAddress(
50       GetModuleHandleW(L"api-ms-win-core-wow64-l1-1-1.dll"),
51       "IsWow64Process2");
52 #ifdef CM_VS_GCC_DIAGNOSTIC_PUSHED
53 #  pragma GCC diagnostic pop
54 #  undef CM_VS_GCC_DIAGNOSTIC_PUSHED
55 #endif
56
57   USHORT processMachine, nativeMachine;
58
59   return s_IsWow64Process2Impl != nullptr &&
60     s_IsWow64Process2Impl(GetCurrentProcess(), &processMachine,
61                           &nativeMachine) &&
62     nativeMachine == IMAGE_FILE_MACHINE_ARM64;
63 }
64
65 static bool VSHasDotNETFrameworkArm64()
66 {
67   std::string dotNetArm64;
68   return cmSystemTools::ReadRegistryValue(
69     "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework;InstallRootArm64",
70     dotNetArm64, cmSystemTools::KeyWOW64_64);
71 }
72
73 static bool VSIsWindows11OrGreater()
74 {
75   cmSystemTools::WindowsVersion const windowsVersion =
76     cmSystemTools::GetWindowsVersion();
77   return (windowsVersion.dwMajorVersion > 10 ||
78           (windowsVersion.dwMajorVersion == 10 &&
79            windowsVersion.dwMinorVersion > 0) ||
80           (windowsVersion.dwMajorVersion == 10 &&
81            windowsVersion.dwMinorVersion == 0 &&
82            windowsVersion.dwBuildNumber >= 22000));
83 }
84
85 static std::string VSHostPlatformName()
86 {
87   if (VSIsArm64Host()) {
88     return "ARM64";
89   } else if (VSIsWow64()) {
90     return "x64";
91   } else {
92 #if defined(_M_ARM)
93     return "ARM";
94 #elif defined(_M_IA64)
95     return "Itanium";
96 #elif defined(_WIN64)
97     return "x64";
98 #else
99     return "Win32";
100 #endif
101   }
102 }
103
104 static std::string VSHostArchitecture(
105   cmGlobalVisualStudioGenerator::VSVersion v)
106 {
107   if (VSIsArm64Host()) {
108     return v >= cmGlobalVisualStudioGenerator::VSVersion::VS17 ? "ARM64" : "";
109   } else if (VSIsWow64()) {
110     return "x64";
111   } else {
112 #if defined(_M_ARM)
113     return "";
114 #elif defined(_M_IA64)
115     return "";
116 #elif defined(_WIN64)
117     return "x64";
118 #else
119     return "x86";
120 #endif
121   }
122 }
123
124 static unsigned int VSVersionToMajor(
125   cmGlobalVisualStudioGenerator::VSVersion v)
126 {
127   switch (v) {
128     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
129       return 9;
130     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
131       return 11;
132     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
133       return 12;
134     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
135       return 14;
136     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
137       return 15;
138     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
139       return 16;
140     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
141       return 17;
142   }
143   return 0;
144 }
145
146 static const char* VSVersionToToolset(
147   cmGlobalVisualStudioGenerator::VSVersion v)
148 {
149   switch (v) {
150     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
151       return "v90";
152     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
153       return "v110";
154     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
155       return "v120";
156     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
157       return "v140";
158     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
159       return "v141";
160     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
161       return "v142";
162     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
163       return "v143";
164   }
165   return "";
166 }
167
168 static std::string VSVersionToMajorString(
169   cmGlobalVisualStudioGenerator::VSVersion v)
170 {
171   switch (v) {
172     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
173       return "9";
174     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
175       return "11";
176     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
177       return "12";
178     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
179       return "14";
180     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
181       return "15";
182     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
183       return "16";
184     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
185       return "17";
186   }
187   return "";
188 }
189
190 static const char* VSVersionToAndroidToolset(
191   cmGlobalVisualStudioGenerator::VSVersion v)
192 {
193   switch (v) {
194     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
195     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
196     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
197       return "";
198     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
199       return "Clang_3_8";
200     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
201     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
202     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
203       return "Clang_5_0";
204   }
205   return "";
206 }
207
208 static const char vs15generatorName[] = "Visual Studio 15 2017";
209
210 // Map generator name without year to name with year.
211 static const char* cmVS15GenName(const std::string& name, std::string& genName)
212 {
213   if (strncmp(name.c_str(), vs15generatorName,
214               sizeof(vs15generatorName) - 6) != 0) {
215     return 0;
216   }
217   const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
218   if (cmHasLiteralPrefix(p, " 2017")) {
219     p += 5;
220   }
221   genName = std::string(vs15generatorName) + p;
222   return p;
223 }
224
225 class cmGlobalVisualStudioVersionedGenerator::Factory15
226   : public cmGlobalGeneratorFactory
227 {
228 public:
229   std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
230     const std::string& name, bool allowArch, cmake* cm) const override
231   {
232     std::string genName;
233     const char* p = cmVS15GenName(name, genName);
234     if (!p) {
235       return std::unique_ptr<cmGlobalGenerator>();
236     }
237     if (!*p) {
238       return std::unique_ptr<cmGlobalGenerator>(
239         new cmGlobalVisualStudioVersionedGenerator(
240           cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, ""));
241     }
242     if (!allowArch || *p++ != ' ') {
243       return std::unique_ptr<cmGlobalGenerator>();
244     }
245     if (strcmp(p, "Win64") == 0) {
246       return std::unique_ptr<cmGlobalGenerator>(
247         new cmGlobalVisualStudioVersionedGenerator(
248           cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, "x64"));
249     }
250     if (strcmp(p, "ARM") == 0) {
251       return std::unique_ptr<cmGlobalGenerator>(
252         new cmGlobalVisualStudioVersionedGenerator(
253           cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, "ARM"));
254     }
255     return std::unique_ptr<cmGlobalGenerator>();
256   }
257
258   void GetDocumentation(cmDocumentationEntry& entry) const override
259   {
260     entry.Name = std::string(vs15generatorName) + " [arch]";
261     entry.Brief = "Generates Visual Studio 2017 project files.  "
262                   "Optional [arch] can be \"Win64\" or \"ARM\".";
263   }
264
265   std::vector<std::string> GetGeneratorNames() const override
266   {
267     std::vector<std::string> names;
268     names.push_back(vs15generatorName);
269     return names;
270   }
271
272   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
273   {
274     std::vector<std::string> names;
275     names.push_back(vs15generatorName + std::string(" ARM"));
276     names.push_back(vs15generatorName + std::string(" Win64"));
277     return names;
278   }
279
280   bool SupportsToolset() const override { return true; }
281   bool SupportsPlatform() const override { return true; }
282
283   std::vector<std::string> GetKnownPlatforms() const override
284   {
285     std::vector<std::string> platforms;
286     platforms.emplace_back("x64");
287     platforms.emplace_back("Win32");
288     platforms.emplace_back("ARM");
289     platforms.emplace_back("ARM64");
290     return platforms;
291   }
292
293   std::string GetDefaultPlatformName() const override { return "Win32"; }
294 };
295
296 std::unique_ptr<cmGlobalGeneratorFactory>
297 cmGlobalVisualStudioVersionedGenerator::NewFactory15()
298 {
299   return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory15);
300 }
301
302 static const char vs16generatorName[] = "Visual Studio 16 2019";
303 static const char vs17generatorName[] = "Visual Studio 17 2022";
304
305 // Map generator name without year to name with year.
306 static const char* cmVS16GenName(const std::string& name, std::string& genName)
307 {
308   if (strncmp(name.c_str(), vs16generatorName,
309               sizeof(vs16generatorName) - 6) != 0) {
310     return 0;
311   }
312   const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
313   if (cmHasLiteralPrefix(p, " 2019")) {
314     p += 5;
315   }
316   genName = std::string(vs16generatorName) + p;
317   return p;
318 }
319
320 static const char* cmVS17GenName(const std::string& name, std::string& genName)
321 {
322   if (strncmp(name.c_str(), vs17generatorName,
323               sizeof(vs17generatorName) - 6) != 0) {
324     return 0;
325   }
326   const char* p = name.c_str() + sizeof(vs17generatorName) - 6;
327   if (cmHasLiteralPrefix(p, " 2022")) {
328     p += 5;
329   }
330   genName = std::string(vs17generatorName) + p;
331   return p;
332 }
333
334 class cmGlobalVisualStudioVersionedGenerator::Factory16
335   : public cmGlobalGeneratorFactory
336 {
337 public:
338   std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
339     const std::string& name, bool /*allowArch*/, cmake* cm) const override
340   {
341     std::string genName;
342     const char* p = cmVS16GenName(name, genName);
343     if (!p) {
344       return std::unique_ptr<cmGlobalGenerator>();
345     }
346     if (!*p) {
347       return std::unique_ptr<cmGlobalGenerator>(
348         new cmGlobalVisualStudioVersionedGenerator(
349           cmGlobalVisualStudioGenerator::VSVersion::VS16, cm, genName, ""));
350     }
351     return std::unique_ptr<cmGlobalGenerator>();
352   }
353
354   void GetDocumentation(cmDocumentationEntry& entry) const override
355   {
356     entry.Name = std::string(vs16generatorName);
357     entry.Brief = "Generates Visual Studio 2019 project files.  "
358                   "Use -A option to specify architecture.";
359   }
360
361   std::vector<std::string> GetGeneratorNames() const override
362   {
363     std::vector<std::string> names;
364     names.push_back(vs16generatorName);
365     return names;
366   }
367
368   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
369   {
370     return std::vector<std::string>();
371   }
372
373   bool SupportsToolset() const override { return true; }
374   bool SupportsPlatform() const override { return true; }
375
376   std::vector<std::string> GetKnownPlatforms() const override
377   {
378     std::vector<std::string> platforms;
379     platforms.emplace_back("x64");
380     platforms.emplace_back("Win32");
381     platforms.emplace_back("ARM");
382     platforms.emplace_back("ARM64");
383     platforms.emplace_back("ARM64EC");
384     return platforms;
385   }
386
387   std::string GetDefaultPlatformName() const override
388   {
389     return VSHostPlatformName();
390   }
391 };
392
393 std::unique_ptr<cmGlobalGeneratorFactory>
394 cmGlobalVisualStudioVersionedGenerator::NewFactory16()
395 {
396   return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory16);
397 }
398
399 class cmGlobalVisualStudioVersionedGenerator::Factory17
400   : public cmGlobalGeneratorFactory
401 {
402 public:
403   std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
404     const std::string& name, bool /*allowArch*/, cmake* cm) const override
405   {
406     std::string genName;
407     const char* p = cmVS17GenName(name, genName);
408     if (!p) {
409       return std::unique_ptr<cmGlobalGenerator>();
410     }
411     if (!*p) {
412       return std::unique_ptr<cmGlobalGenerator>(
413         new cmGlobalVisualStudioVersionedGenerator(
414           cmGlobalVisualStudioGenerator::VSVersion::VS17, cm, genName, ""));
415     }
416     return std::unique_ptr<cmGlobalGenerator>();
417   }
418
419   void GetDocumentation(cmDocumentationEntry& entry) const override
420   {
421     entry.Name = std::string(vs17generatorName);
422     entry.Brief = "Generates Visual Studio 2022 project files.  "
423                   "Use -A option to specify architecture.";
424   }
425
426   std::vector<std::string> GetGeneratorNames() const override
427   {
428     std::vector<std::string> names;
429     names.push_back(vs17generatorName);
430     return names;
431   }
432
433   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
434   {
435     return std::vector<std::string>();
436   }
437
438   bool SupportsToolset() const override { return true; }
439   bool SupportsPlatform() const override { return true; }
440
441   std::vector<std::string> GetKnownPlatforms() const override
442   {
443     std::vector<std::string> platforms;
444     platforms.emplace_back("x64");
445     platforms.emplace_back("Win32");
446     platforms.emplace_back("ARM");
447     platforms.emplace_back("ARM64");
448     platforms.emplace_back("ARM64EC");
449     return platforms;
450   }
451
452   std::string GetDefaultPlatformName() const override
453   {
454     return VSHostPlatformName();
455   }
456 };
457
458 std::unique_ptr<cmGlobalGeneratorFactory>
459 cmGlobalVisualStudioVersionedGenerator::NewFactory17()
460 {
461   return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory17);
462 }
463
464 cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
465   VSVersion version, cmake* cm, const std::string& name,
466   std::string const& platformInGeneratorName)
467   : cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
468   , vsSetupAPIHelper(VSVersionToMajor(version))
469 {
470   this->Version = version;
471   this->ExpressEdition = false;
472   this->DefaultPlatformToolset = VSVersionToToolset(this->Version);
473   this->DefaultAndroidToolset = VSVersionToAndroidToolset(this->Version);
474   this->DefaultCLFlagTableName = VSVersionToToolset(this->Version);
475   this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version);
476   this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version);
477   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16) {
478     this->DefaultPlatformName = VSHostPlatformName();
479     this->DefaultPlatformToolsetHostArchitecture =
480       VSHostArchitecture(this->Version);
481   }
482   if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
483     // FIXME: Search for an existing framework?  Under '%ProgramFiles(x86)%',
484     // see 'Reference Assemblies\Microsoft\Framework\.NETFramework'.
485     // Use a version installed by VS 2022 without a separate component.
486     this->DefaultTargetFrameworkVersion = "v4.7.2";
487   }
488 }
489
490 bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
491   const std::string& name) const
492 {
493   std::string genName;
494   switch (this->Version) {
495     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
496     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
497     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
498     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
499       break;
500     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
501       if (cmVS15GenName(name, genName)) {
502         return genName == this->GetName();
503       }
504       break;
505     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
506       if (cmVS16GenName(name, genName)) {
507         return genName == this->GetName();
508       }
509       break;
510     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
511       if (cmVS17GenName(name, genName)) {
512         return genName == this->GetName();
513       }
514       break;
515   }
516   return false;
517 }
518
519 bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
520   std::string const& i, cmMakefile* mf)
521 {
522   if (this->LastGeneratorInstanceString &&
523       i == *(this->LastGeneratorInstanceString)) {
524     return true;
525   }
526
527   if (!this->ParseGeneratorInstance(i, mf)) {
528     return false;
529   }
530
531   if (!this->GeneratorInstanceVersion.empty()) {
532     std::string const majorStr = VSVersionToMajorString(this->Version);
533     cmsys::RegularExpression versionRegex(
534       cmStrCat("^", majorStr, "\\.[0-9]+\\.[0-9]+\\.[0-9]+$"));
535     if (!versionRegex.find(this->GeneratorInstanceVersion)) {
536       std::ostringstream e;
537       /* clang-format off */
538       e <<
539         "Generator\n"
540         "  " << this->GetName() << "\n"
541         "given instance specification\n"
542         "  " << i << "\n"
543         "but the version field is not 4 integer components"
544         " starting in " << majorStr << "."
545         ;
546       /* clang-format on */
547       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
548       return false;
549     }
550   }
551
552   std::string vsInstance;
553   if (!i.empty()) {
554     vsInstance = i;
555     if (!this->vsSetupAPIHelper.SetVSInstance(
556           this->GeneratorInstance, this->GeneratorInstanceVersion)) {
557       std::ostringstream e;
558       /* clang-format off */
559       e <<
560         "Generator\n"
561         "  " << this->GetName() << "\n"
562         "could not find specified instance of Visual Studio:\n"
563         "  " << i;
564       /* clang-format on */
565       if (!this->GeneratorInstance.empty() &&
566           this->GeneratorInstanceVersion.empty() &&
567           cmSystemTools::FileIsDirectory(this->GeneratorInstance)) {
568         e << "\n"
569              "The directory exists, but the instance is not known to the "
570              "Visual Studio Installer, and no 'version=' field was given.";
571       }
572       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
573       return false;
574     }
575   } else if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
576     std::ostringstream e;
577     /* clang-format off */
578     e <<
579       "Generator\n"
580       "  " << this->GetName() << "\n"
581       "could not find any instance of Visual Studio.\n";
582     /* clang-format on */
583     mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
584     return false;
585   }
586
587   // Save the selected instance persistently.
588   std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
589   if (vsInstance != genInstance) {
590     this->CMakeInstance->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", vsInstance,
591                                        "Generator instance identifier.",
592                                        cmStateEnums::INTERNAL);
593   }
594
595   // The selected instance may have a different MSBuild than previously found.
596   this->MSBuildCommandInitialized = false;
597
598   this->LastGeneratorInstanceString = i;
599
600   return true;
601 }
602
603 bool cmGlobalVisualStudioVersionedGenerator::ParseGeneratorInstance(
604   std::string const& is, cmMakefile* mf)
605 {
606   this->GeneratorInstance.clear();
607   this->GeneratorInstanceVersion.clear();
608
609   std::vector<std::string> const fields = cmTokenize(is, ",");
610   std::vector<std::string>::const_iterator fi = fields.begin();
611   if (fi == fields.end()) {
612     return true;
613   }
614
615   // The first field may be the VS instance.
616   if (fi->find('=') == fi->npos) {
617     this->GeneratorInstance = *fi;
618     ++fi;
619   }
620
621   std::set<std::string> handled;
622
623   // The rest of the fields must be key=value pairs.
624   for (; fi != fields.end(); ++fi) {
625     std::string::size_type pos = fi->find('=');
626     if (pos == fi->npos) {
627       std::ostringstream e;
628       /* clang-format off */
629       e <<
630         "Generator\n"
631         "  " << this->GetName() << "\n"
632         "given instance specification\n"
633         "  " << is << "\n"
634         "that contains a field after the first ',' with no '='."
635         ;
636       /* clang-format on */
637       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
638       return false;
639     }
640     std::string const key = fi->substr(0, pos);
641     std::string const value = fi->substr(pos + 1);
642     if (!handled.insert(key).second) {
643       std::ostringstream e;
644       /* clang-format off */
645       e <<
646         "Generator\n"
647         "  " << this->GetName() << "\n"
648         "given instance specification\n"
649         "  " << is << "\n"
650         "that contains duplicate field key '" << key << "'."
651         ;
652       /* clang-format on */
653       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
654       return false;
655     }
656     if (!this->ProcessGeneratorInstanceField(key, value)) {
657       std::ostringstream e;
658       /* clang-format off */
659       e <<
660         "Generator\n"
661         "  " << this->GetName() << "\n"
662         "given instance specification\n"
663         "  " << is << "\n"
664         "that contains invalid field '" << *fi << "'."
665         ;
666       /* clang-format on */
667       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
668       return false;
669     }
670   }
671
672   return true;
673 }
674
675 bool cmGlobalVisualStudioVersionedGenerator::ProcessGeneratorInstanceField(
676   std::string const& key, std::string const& value)
677 {
678   if (key == "version") {
679     this->GeneratorInstanceVersion = value;
680     return true;
681   }
682   return false;
683 }
684
685 bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
686   std::string& dir) const
687 {
688   return vsSetupAPIHelper.GetVSInstanceInfo(dir);
689 }
690
691 cm::optional<std::string>
692 cmGlobalVisualStudioVersionedGenerator::GetVSInstanceVersion() const
693 {
694   cm::optional<std::string> result;
695   std::string vsInstanceVersion;
696   if (vsSetupAPIHelper.GetVSInstanceVersion(vsInstanceVersion)) {
697     result = vsInstanceVersion;
698   }
699   return result;
700 }
701
702 bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const
703 {
704   // Supported from Visual Studio 16.7 Preview 3.
705   if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
706     return true;
707   }
708   if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
709     return false;
710   }
711   static std::string const vsVer16_7_P2 = "16.7.30128.36";
712   cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
713   return (vsVer &&
714           cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_7_P2));
715 }
716
717 bool cmGlobalVisualStudioVersionedGenerator::IsUtf8EncodingSupported() const
718 {
719   // Supported from Visual Studio 16.10 Preview 2.
720   if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
721     return true;
722   }
723   if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
724     return false;
725   }
726   static std::string const vsVer16_10_P2 = "16.10.31213.239";
727   cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
728   return (vsVer &&
729           cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_10_P2));
730 }
731
732 const char*
733 cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
734   const
735 {
736   switch (this->Version) {
737     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
738     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
739     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
740       return "";
741     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
742       return "2.0";
743     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
744     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
745     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
746       return "3.0";
747   }
748   return "";
749 }
750
751 cmGlobalVisualStudioVersionedGenerator::AuxToolset
752 cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
753   std::string& version, std::string& props) const
754 {
755   if (version.empty()) {
756     return AuxToolset::None;
757   }
758
759   std::string instancePath;
760   this->GetVSInstance(instancePath);
761   cmSystemTools::ConvertToUnixSlashes(instancePath);
762
763   // Translate three-component format accepted by "vcvarsall -vcvars_ver=".
764   cmsys::RegularExpression threeComponentRegex(
765     "^([0-9]+\\.[0-9]+)\\.[0-9][0-9][0-9][0-9][0-9]$");
766   // The two-component format represents the two major components of the
767   // three-component format
768   cmsys::RegularExpression twoComponentRegex("^([0-9]+\\.[0-9]+)$");
769   if (threeComponentRegex.find(version)) {
770     // Load "VC/Auxiliary/Build/*/Microsoft.VCToolsVersion.*.txt" files
771     // with two matching components to check their three-component version.
772     std::string const& twoComponent = threeComponentRegex.match(1);
773     std::string pattern =
774       cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
775                "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
776     cmsys::Glob glob;
777     glob.SetRecurseThroughSymlinks(false);
778     if (glob.FindFiles(pattern)) {
779       for (std::string const& txt : glob.GetFiles()) {
780         std::string ver;
781         cmsys::ifstream fin(txt.c_str());
782         if (fin && std::getline(fin, ver)) {
783           // Strip trailing whitespace.
784           ver = ver.substr(0, ver.find_first_not_of("0123456789."));
785           // If the three-component version matches, translate it to
786           // that used by the "Microsoft.VCToolsVersion.*.txt" file name.
787           if (ver == version) {
788             cmsys::RegularExpression extractVersion(
789               "VCToolsVersion\\.([0-9.]+)\\.txt$");
790             if (extractVersion.find(txt)) {
791               version = extractVersion.match(1);
792               break;
793             }
794           }
795         }
796       }
797     }
798   } else if (twoComponentRegex.find(version)) {
799     std::string const& twoComponent = twoComponentRegex.match(1);
800     std::string pattern =
801       cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
802                "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
803     cmsys::Glob glob;
804     glob.SetRecurseThroughSymlinks(false);
805     if (glob.FindFiles(pattern) && !glob.GetFiles().empty()) {
806       // Since we are only using the first two components of the
807       // toolset version, we require a single match.
808       if (glob.GetFiles().size() == 1) {
809         std::string const& txt = glob.GetFiles()[0];
810         std::string ver;
811         cmsys::ifstream fin(txt.c_str());
812         if (fin && std::getline(fin, ver)) {
813           // Strip trailing whitespace.
814           ver = ver.substr(0, ver.find_first_not_of("0123456789."));
815           // We assume the version is correct, since it is the only one that
816           // matched.
817           cmsys::RegularExpression extractVersion(
818             "VCToolsVersion\\.([0-9.]+)\\.txt$");
819           if (extractVersion.find(txt)) {
820             version = extractVersion.match(1);
821           }
822         }
823       } else {
824         props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s);
825         return AuxToolset::PropsIndeterminate;
826       }
827     }
828   }
829
830   if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
831     props = cmStrCat(instancePath, "/VC/Auxiliary/Build."_s, version,
832                      "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
833     if (cmSystemTools::PathExists(props)) {
834       return AuxToolset::PropsExist;
835     }
836   }
837   props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, version,
838                    "/Microsoft.VCToolsVersion."_s, version, ".props"_s);
839   if (cmSystemTools::PathExists(props)) {
840     return AuxToolset::PropsExist;
841   }
842
843   // Accept the toolset version that is default in the current VS version
844   // by matching the name later VS versions will use for the SxS props files.
845   std::string vcToolsetVersion;
846   if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
847     // Accept an exact-match (three-component version).
848     if (version == vcToolsetVersion) {
849       return AuxToolset::Default;
850     }
851
852     // Accept known SxS props file names using four version components
853     // in VS versions later than the current.
854     if (version == "14.28.16.9" && vcToolsetVersion == "14.28.29910") {
855       return AuxToolset::Default;
856     }
857     if (version == "14.29.16.10" && vcToolsetVersion == "14.29.30037") {
858       return AuxToolset::Default;
859     }
860     if (version == "14.29.16.11" && vcToolsetVersion == "14.29.30133") {
861       return AuxToolset::Default;
862     }
863
864     // The first two components of the default toolset version typically
865     // match the name used by later VS versions for the SxS props files.
866     cmsys::RegularExpression twoComponent("^([0-9]+\\.[0-9]+)");
867     if (twoComponent.find(version)) {
868       std::string const versionPrefix = cmStrCat(twoComponent.match(1), '.');
869       if (cmHasPrefix(vcToolsetVersion, versionPrefix)) {
870         return AuxToolset::Default;
871       }
872     }
873   }
874
875   return AuxToolset::PropsMissing;
876 }
877
878 bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
879 {
880   // If the Win 8.1 SDK is installed then we can select a SDK matching
881   // the target Windows version.
882   if (this->IsWin81SDKInstalled()) {
883     // VS 2019 does not default to 8.1 so specify it explicitly when needed.
884     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
885         !cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
886       this->SetWindowsTargetPlatformVersion("8.1", mf);
887       return true;
888     }
889     return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
890   }
891   // Otherwise we must choose a Win 10 SDK even if we are not targeting
892   // Windows 10.
893   return this->SelectWindows10SDK(mf, false);
894 }
895
896 bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
897   std::string& toolset) const
898 {
899   if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
900     if (this->IsWindowsStoreToolsetInstalled() &&
901         this->IsWindowsDesktopToolsetInstalled()) {
902       toolset = VSVersionToToolset(this->Version);
903       return true;
904     } else {
905       return false;
906     }
907   }
908   return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
909     toolset);
910 }
911
912 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
913   const
914 {
915   return vsSetupAPIHelper.IsVSInstalled();
916 }
917
918 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
919   const
920 {
921   return vsSetupAPIHelper.IsWin10SDKInstalled();
922 }
923
924 bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
925 {
926   // Does the VS installer tool know about one?
927   if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
928     return true;
929   }
930
931   // Does the registry know about one (e.g. from VS 2015)?
932   std::string win81Root;
933   if (cmSystemTools::ReadRegistryValue(
934         "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"
935         "Windows Kits\\Installed Roots;KitsRoot81",
936         win81Root, cmSystemTools::KeyWOW64_32) ||
937       cmSystemTools::ReadRegistryValue(
938         "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
939         "Windows Kits\\Installed Roots;KitsRoot81",
940         win81Root, cmSystemTools::KeyWOW64_32)) {
941     return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
942                                      true);
943   }
944   return false;
945 }
946
947 std::string
948 cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersionDefault(
949   cmMakefile*) const
950 {
951   return std::string();
952 }
953
954 cm::optional<std::string>
955 cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommandEarly(cmMakefile* mf)
956 {
957   std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
958   if (!this->SetGeneratorInstance(instance, mf)) {
959     cmSystemTools::SetFatalErrorOccurred();
960     return {};
961   }
962   return this->cmGlobalVisualStudio14Generator::FindMSBuildCommandEarly(mf);
963 }
964
965 std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
966 {
967   std::string msbuild;
968
969   // Ask Visual Studio Installer tool.
970   std::string vs;
971   if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
972     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
973       if (VSIsArm64Host()) {
974         if (VSHasDotNETFrameworkArm64()) {
975           msbuild = vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe";
976           if (cmSystemTools::FileExists(msbuild)) {
977             return msbuild;
978           }
979         }
980         if (VSIsWindows11OrGreater()) {
981           msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
982           if (cmSystemTools::FileExists(msbuild)) {
983             return msbuild;
984           }
985         }
986       } else {
987         msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
988         if (cmSystemTools::FileExists(msbuild)) {
989           return msbuild;
990         }
991       }
992     }
993     msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
994     if (cmSystemTools::FileExists(msbuild)) {
995       return msbuild;
996     }
997     msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
998     if (cmSystemTools::FileExists(msbuild)) {
999       return msbuild;
1000     }
1001   }
1002
1003   msbuild = "MSBuild.exe";
1004   return msbuild;
1005 }
1006
1007 std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
1008 {
1009   std::string devenv;
1010
1011   // Ask Visual Studio Installer tool.
1012   std::string vs;
1013   if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
1014     devenv = vs + "/Common7/IDE/devenv.com";
1015     if (cmSystemTools::FileExists(devenv)) {
1016       return devenv;
1017     }
1018   }
1019
1020   devenv = "devenv.com";
1021   return devenv;
1022 }