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