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