Imported Upstream version 3.19.4
[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 "cmAlgorithms.h"
6 #include "cmDocumentationEntry.h"
7 #include "cmLocalVisualStudio10Generator.h"
8 #include "cmMakefile.h"
9 #include "cmStringAlgorithms.h"
10 #include "cmVSSetupHelper.h"
11 #include "cmake.h"
12
13 #if defined(_M_ARM64)
14 #  define HOST_PLATFORM_NAME "ARM64"
15 #  define HOST_TOOLS_ARCH ""
16 #elif defined(_M_ARM)
17 #  define HOST_PLATFORM_NAME "ARM"
18 #  define HOST_TOOLS_ARCH ""
19 #elif defined(_M_IA64)
20 #  define HOST_PLATFORM_NAME "Itanium"
21 #  define HOST_TOOLS_ARCH ""
22 #elif defined(_WIN64)
23 #  define HOST_PLATFORM_NAME "x64"
24 #  define HOST_TOOLS_ARCH "x64"
25 #else
26 static bool VSIsWow64()
27 {
28   BOOL isWow64 = false;
29   return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
30 }
31 #endif
32
33 static std::string VSHostPlatformName()
34 {
35 #ifdef HOST_PLATFORM_NAME
36   return HOST_PLATFORM_NAME;
37 #else
38   if (VSIsWow64()) {
39     return "x64";
40   } else {
41     return "Win32";
42   }
43 #endif
44 }
45
46 static std::string VSHostArchitecture()
47 {
48 #ifdef HOST_TOOLS_ARCH
49   return HOST_TOOLS_ARCH;
50 #else
51   if (VSIsWow64()) {
52     return "x64";
53   } else {
54     return "x86";
55   }
56 #endif
57 }
58
59 static unsigned int VSVersionToMajor(
60   cmGlobalVisualStudioGenerator::VSVersion v)
61 {
62   switch (v) {
63     case cmGlobalVisualStudioGenerator::VS9:
64       return 9;
65     case cmGlobalVisualStudioGenerator::VS10:
66       return 10;
67     case cmGlobalVisualStudioGenerator::VS11:
68       return 11;
69     case cmGlobalVisualStudioGenerator::VS12:
70       return 12;
71     case cmGlobalVisualStudioGenerator::VS14:
72       return 14;
73     case cmGlobalVisualStudioGenerator::VS15:
74       return 15;
75     case cmGlobalVisualStudioGenerator::VS16:
76       return 16;
77   }
78   return 0;
79 }
80
81 static const char* VSVersionToToolset(
82   cmGlobalVisualStudioGenerator::VSVersion v)
83 {
84   switch (v) {
85     case cmGlobalVisualStudioGenerator::VS9:
86       return "v90";
87     case cmGlobalVisualStudioGenerator::VS10:
88       return "v100";
89     case cmGlobalVisualStudioGenerator::VS11:
90       return "v110";
91     case cmGlobalVisualStudioGenerator::VS12:
92       return "v120";
93     case cmGlobalVisualStudioGenerator::VS14:
94       return "v140";
95     case cmGlobalVisualStudioGenerator::VS15:
96       return "v141";
97     case cmGlobalVisualStudioGenerator::VS16:
98       return "v142";
99   }
100   return "";
101 }
102
103 static const char* VSVersionToAndroidToolset(
104   cmGlobalVisualStudioGenerator::VSVersion v)
105 {
106   switch (v) {
107     case cmGlobalVisualStudioGenerator::VS9:
108     case cmGlobalVisualStudioGenerator::VS10:
109     case cmGlobalVisualStudioGenerator::VS11:
110     case cmGlobalVisualStudioGenerator::VS12:
111       return "";
112     case cmGlobalVisualStudioGenerator::VS14:
113       return "Clang_3_8";
114     case cmGlobalVisualStudioGenerator::VS15:
115     case cmGlobalVisualStudioGenerator::VS16:
116       return "Clang_5_0";
117   }
118   return "";
119 }
120
121 static const char vs15generatorName[] = "Visual Studio 15 2017";
122
123 // Map generator name without year to name with year.
124 static const char* cmVS15GenName(const std::string& name, std::string& genName)
125 {
126   if (strncmp(name.c_str(), vs15generatorName,
127               sizeof(vs15generatorName) - 6) != 0) {
128     return 0;
129   }
130   const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
131   if (cmHasLiteralPrefix(p, " 2017")) {
132     p += 5;
133   }
134   genName = std::string(vs15generatorName) + p;
135   return p;
136 }
137
138 class cmGlobalVisualStudioVersionedGenerator::Factory15
139   : public cmGlobalGeneratorFactory
140 {
141 public:
142   std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
143     const std::string& name, bool allowArch, cmake* cm) const override
144   {
145     std::string genName;
146     const char* p = cmVS15GenName(name, genName);
147     if (!p) {
148       return std::unique_ptr<cmGlobalGenerator>();
149     }
150     if (!*p) {
151       return std::unique_ptr<cmGlobalGenerator>(
152         new cmGlobalVisualStudioVersionedGenerator(
153           cmGlobalVisualStudioGenerator::VS15, cm, genName, ""));
154     }
155     if (!allowArch || *p++ != ' ') {
156       return std::unique_ptr<cmGlobalGenerator>();
157     }
158     if (strcmp(p, "Win64") == 0) {
159       return std::unique_ptr<cmGlobalGenerator>(
160         new cmGlobalVisualStudioVersionedGenerator(
161           cmGlobalVisualStudioGenerator::VS15, cm, genName, "x64"));
162     }
163     if (strcmp(p, "ARM") == 0) {
164       return std::unique_ptr<cmGlobalGenerator>(
165         new cmGlobalVisualStudioVersionedGenerator(
166           cmGlobalVisualStudioGenerator::VS15, cm, genName, "ARM"));
167     }
168     return std::unique_ptr<cmGlobalGenerator>();
169   }
170
171   void GetDocumentation(cmDocumentationEntry& entry) const override
172   {
173     entry.Name = std::string(vs15generatorName) + " [arch]";
174     entry.Brief = "Generates Visual Studio 2017 project files.  "
175                   "Optional [arch] can be \"Win64\" or \"ARM\".";
176   }
177
178   std::vector<std::string> GetGeneratorNames() const override
179   {
180     std::vector<std::string> names;
181     names.push_back(vs15generatorName);
182     return names;
183   }
184
185   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
186   {
187     std::vector<std::string> names;
188     names.push_back(vs15generatorName + std::string(" ARM"));
189     names.push_back(vs15generatorName + std::string(" Win64"));
190     return names;
191   }
192
193   bool SupportsToolset() const override { return true; }
194   bool SupportsPlatform() const override { return true; }
195
196   std::vector<std::string> GetKnownPlatforms() const override
197   {
198     std::vector<std::string> platforms;
199     platforms.emplace_back("x64");
200     platforms.emplace_back("Win32");
201     platforms.emplace_back("ARM");
202     platforms.emplace_back("ARM64");
203     return platforms;
204   }
205
206   std::string GetDefaultPlatformName() const override { return "Win32"; }
207 };
208
209 std::unique_ptr<cmGlobalGeneratorFactory>
210 cmGlobalVisualStudioVersionedGenerator::NewFactory15()
211 {
212   return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory15);
213 }
214
215 static const char vs16generatorName[] = "Visual Studio 16 2019";
216
217 // Map generator name without year to name with year.
218 static const char* cmVS16GenName(const std::string& name, std::string& genName)
219 {
220   if (strncmp(name.c_str(), vs16generatorName,
221               sizeof(vs16generatorName) - 6) != 0) {
222     return 0;
223   }
224   const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
225   if (cmHasLiteralPrefix(p, " 2019")) {
226     p += 5;
227   }
228   genName = std::string(vs16generatorName) + p;
229   return p;
230 }
231
232 class cmGlobalVisualStudioVersionedGenerator::Factory16
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 = cmVS16GenName(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::VS16, cm, genName, ""));
248     }
249     return std::unique_ptr<cmGlobalGenerator>();
250   }
251
252   void GetDocumentation(cmDocumentationEntry& entry) const override
253   {
254     entry.Name = std::string(vs16generatorName);
255     entry.Brief = "Generates Visual Studio 2019 project files.  "
256                   "Use -A option to specify architecture.";
257   }
258
259   std::vector<std::string> GetGeneratorNames() const override
260   {
261     std::vector<std::string> names;
262     names.push_back(vs16generatorName);
263     return names;
264   }
265
266   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
267   {
268     return std::vector<std::string>();
269   }
270
271   bool SupportsToolset() const override { return true; }
272   bool SupportsPlatform() const override { return true; }
273
274   std::vector<std::string> GetKnownPlatforms() const override
275   {
276     std::vector<std::string> platforms;
277     platforms.emplace_back("x64");
278     platforms.emplace_back("Win32");
279     platforms.emplace_back("ARM");
280     platforms.emplace_back("ARM64");
281     return platforms;
282   }
283
284   std::string GetDefaultPlatformName() const override
285   {
286     return VSHostPlatformName();
287   }
288 };
289
290 std::unique_ptr<cmGlobalGeneratorFactory>
291 cmGlobalVisualStudioVersionedGenerator::NewFactory16()
292 {
293   return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory16);
294 }
295
296 cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
297   VSVersion version, cmake* cm, const std::string& name,
298   std::string const& platformInGeneratorName)
299   : cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
300   , vsSetupAPIHelper(VSVersionToMajor(version))
301 {
302   this->Version = version;
303   this->ExpressEdition = false;
304   this->DefaultPlatformToolset = VSVersionToToolset(this->Version);
305   this->DefaultAndroidToolset = VSVersionToAndroidToolset(this->Version);
306   this->DefaultCLFlagTableName = VSVersionToToolset(this->Version);
307   this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version);
308   this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version);
309   if (this->Version >= cmGlobalVisualStudioGenerator::VS16) {
310     this->DefaultPlatformName = VSHostPlatformName();
311     this->DefaultPlatformToolsetHostArchitecture = VSHostArchitecture();
312   }
313 }
314
315 bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
316   const std::string& name) const
317 {
318   std::string genName;
319   switch (this->Version) {
320     case cmGlobalVisualStudioGenerator::VS9:
321     case cmGlobalVisualStudioGenerator::VS10:
322     case cmGlobalVisualStudioGenerator::VS11:
323     case cmGlobalVisualStudioGenerator::VS12:
324     case cmGlobalVisualStudioGenerator::VS14:
325       break;
326     case cmGlobalVisualStudioGenerator::VS15:
327       if (cmVS15GenName(name, genName)) {
328         return genName == this->GetName();
329       }
330       break;
331     case cmGlobalVisualStudioGenerator::VS16:
332       if (cmVS16GenName(name, genName)) {
333         return genName == this->GetName();
334       }
335       break;
336   }
337   return false;
338 }
339
340 bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
341   std::string const& i, cmMakefile* mf)
342 {
343   if (!i.empty()) {
344     if (!this->vsSetupAPIHelper.SetVSInstance(i)) {
345       std::ostringstream e;
346       /* clang-format off */
347       e <<
348         "Generator\n"
349         "  " << this->GetName() << "\n"
350         "could not find specified instance of Visual Studio:\n"
351         "  " << i;
352       /* clang-format on */
353       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
354       return false;
355     }
356   }
357
358   std::string vsInstance;
359   if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
360     std::ostringstream e;
361     /* clang-format off */
362     e <<
363       "Generator\n"
364       "  " << this->GetName() << "\n"
365       "could not find any instance of Visual Studio.\n";
366     /* clang-format on */
367     mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
368     return false;
369   }
370
371   // Save the selected instance persistently.
372   std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
373   if (vsInstance != genInstance) {
374     this->CMakeInstance->AddCacheEntry(
375       "CMAKE_GENERATOR_INSTANCE", vsInstance.c_str(),
376       "Generator instance identifier.", cmStateEnums::INTERNAL);
377   }
378
379   return true;
380 }
381
382 bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
383   std::string& dir) const
384 {
385   return vsSetupAPIHelper.GetVSInstanceInfo(dir);
386 }
387
388 bool cmGlobalVisualStudioVersionedGenerator::GetVSInstanceVersion(
389   unsigned long long& vsInstanceVersion) const
390 {
391   return vsSetupAPIHelper.GetVSInstanceVersion(vsInstanceVersion);
392 }
393
394 bool cmGlobalVisualStudioVersionedGenerator::IsDefaultToolset(
395   const std::string& version) const
396 {
397   if (version.empty()) {
398     return true;
399   }
400
401   std::string vcToolsetVersion;
402   if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
403
404     cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9]+");
405     if (regex.find(version) && regex.find(vcToolsetVersion)) {
406       const auto majorMinorEnd = vcToolsetVersion.find('.', 3);
407       const auto majorMinor = vcToolsetVersion.substr(0, majorMinorEnd);
408       return version == majorMinor;
409     }
410   }
411
412   return false;
413 }
414
415 bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const
416 {
417   // Supported from Visual Studio 16.7 Preview 3.
418   if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
419     return true;
420   }
421   if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
422     return false;
423   }
424   unsigned long long const vsInstanceVersion16_7_P2 = 4503631666610212;
425   unsigned long long vsInstanceVersion;
426   return (this->GetVSInstanceVersion(vsInstanceVersion) &&
427           vsInstanceVersion > vsInstanceVersion16_7_P2);
428 }
429
430 const char*
431 cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
432   const
433 {
434   switch (this->Version) {
435     case cmGlobalVisualStudioGenerator::VS9:
436     case cmGlobalVisualStudioGenerator::VS10:
437     case cmGlobalVisualStudioGenerator::VS11:
438     case cmGlobalVisualStudioGenerator::VS12:
439       return "";
440     case cmGlobalVisualStudioGenerator::VS14:
441       return "2.0";
442     case cmGlobalVisualStudioGenerator::VS15:
443     case cmGlobalVisualStudioGenerator::VS16:
444       return "3.0";
445   }
446   return "";
447 }
448
449 std::string cmGlobalVisualStudioVersionedGenerator::GetAuxiliaryToolset() const
450 {
451   const char* version = this->GetPlatformToolsetVersion();
452   if (version) {
453     std::string instancePath;
454     GetVSInstance(instancePath);
455     std::string toolsetDir = instancePath + "/VC/Auxiliary/Build";
456     char sep = '/';
457     if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
458       std::string toolsetDot =
459         cmStrCat(toolsetDir, '.', version, "/Microsoft.VCToolsVersion.",
460                  version, ".props");
461       if (cmSystemTools::PathExists(toolsetDot)) {
462         sep = '.';
463       }
464     }
465     std::string toolsetPath =
466       cmStrCat(toolsetDir, sep, version, "/Microsoft.VCToolsVersion.", version,
467                ".props");
468     cmSystemTools::ConvertToUnixSlashes(toolsetPath);
469     return toolsetPath;
470   }
471   return {};
472 }
473
474 bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
475 {
476   // If the Win 8.1 SDK is installed then we can select a SDK matching
477   // the target Windows version.
478   if (this->IsWin81SDKInstalled()) {
479     // VS 2019 does not default to 8.1 so specify it explicitly when needed.
480     if (this->Version >= cmGlobalVisualStudioGenerator::VS16 &&
481         !cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
482       this->SetWindowsTargetPlatformVersion("8.1", mf);
483       return true;
484     }
485     return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
486   }
487   // Otherwise we must choose a Win 10 SDK even if we are not targeting
488   // Windows 10.
489   return this->SelectWindows10SDK(mf, false);
490 }
491
492 bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
493   std::string& toolset) const
494 {
495   if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
496     if (this->IsWindowsStoreToolsetInstalled() &&
497         this->IsWindowsDesktopToolsetInstalled()) {
498       toolset = VSVersionToToolset(this->Version);
499       return true;
500     } else {
501       return false;
502     }
503   }
504   return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
505     toolset);
506 }
507
508 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
509   const
510 {
511   return vsSetupAPIHelper.IsVSInstalled();
512 }
513
514 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
515   const
516 {
517   return vsSetupAPIHelper.IsWin10SDKInstalled();
518 }
519
520 bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
521 {
522   // Does the VS installer tool know about one?
523   if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
524     return true;
525   }
526
527   // Does the registry know about one (e.g. from VS 2015)?
528   std::string win81Root;
529   if (cmSystemTools::ReadRegistryValue(
530         "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"
531         "Windows Kits\\Installed Roots;KitsRoot81",
532         win81Root, cmSystemTools::KeyWOW64_32) ||
533       cmSystemTools::ReadRegistryValue(
534         "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
535         "Windows Kits\\Installed Roots;KitsRoot81",
536         win81Root, cmSystemTools::KeyWOW64_32)) {
537     return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
538                                      true);
539   }
540   return false;
541 }
542
543 std::string
544 cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersionDefault(
545   cmMakefile*) const
546 {
547   return std::string();
548 }
549
550 std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
551 {
552   std::string msbuild;
553
554   // Ask Visual Studio Installer tool.
555   std::string vs;
556   if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
557     msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
558     if (cmSystemTools::FileExists(msbuild)) {
559       return msbuild;
560     }
561     msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
562     if (cmSystemTools::FileExists(msbuild)) {
563       return msbuild;
564     }
565   }
566
567   msbuild = "MSBuild.exe";
568   return msbuild;
569 }
570
571 std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
572 {
573   std::string devenv;
574
575   // Ask Visual Studio Installer tool.
576   std::string vs;
577   if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
578     devenv = vs + "/Common7/IDE/devenv.com";
579     if (cmSystemTools::FileExists(devenv)) {
580       return devenv;
581     }
582   }
583
584   devenv = "devenv.com";
585   return devenv;
586 }