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"
5 #include "cmAlgorithms.h"
6 #include "cmDocumentationEntry.h"
7 #include "cmLocalVisualStudio10Generator.h"
8 #include "cmMakefile.h"
9 #include "cmStringAlgorithms.h"
10 #include "cmVSSetupHelper.h"
14 # define HOST_PLATFORM_NAME "ARM64"
15 # define HOST_TOOLS_ARCH ""
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 ""
23 # define HOST_PLATFORM_NAME "x64"
24 # define HOST_TOOLS_ARCH "x64"
26 static bool VSIsWow64()
29 return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
33 static std::string VSHostPlatformName()
35 #ifdef HOST_PLATFORM_NAME
36 return HOST_PLATFORM_NAME;
46 static std::string VSHostArchitecture()
48 #ifdef HOST_TOOLS_ARCH
49 return HOST_TOOLS_ARCH;
59 static unsigned int VSVersionToMajor(
60 cmGlobalVisualStudioGenerator::VSVersion v)
63 case cmGlobalVisualStudioGenerator::VS9:
65 case cmGlobalVisualStudioGenerator::VS10:
67 case cmGlobalVisualStudioGenerator::VS11:
69 case cmGlobalVisualStudioGenerator::VS12:
71 case cmGlobalVisualStudioGenerator::VS14:
73 case cmGlobalVisualStudioGenerator::VS15:
75 case cmGlobalVisualStudioGenerator::VS16:
81 static const char* VSVersionToToolset(
82 cmGlobalVisualStudioGenerator::VSVersion v)
85 case cmGlobalVisualStudioGenerator::VS9:
87 case cmGlobalVisualStudioGenerator::VS10:
89 case cmGlobalVisualStudioGenerator::VS11:
91 case cmGlobalVisualStudioGenerator::VS12:
93 case cmGlobalVisualStudioGenerator::VS14:
95 case cmGlobalVisualStudioGenerator::VS15:
97 case cmGlobalVisualStudioGenerator::VS16:
103 static const char* VSVersionToAndroidToolset(
104 cmGlobalVisualStudioGenerator::VSVersion v)
107 case cmGlobalVisualStudioGenerator::VS9:
108 case cmGlobalVisualStudioGenerator::VS10:
109 case cmGlobalVisualStudioGenerator::VS11:
110 case cmGlobalVisualStudioGenerator::VS12:
112 case cmGlobalVisualStudioGenerator::VS14:
114 case cmGlobalVisualStudioGenerator::VS15:
115 case cmGlobalVisualStudioGenerator::VS16:
121 static const char vs15generatorName[] = "Visual Studio 15 2017";
123 // Map generator name without year to name with year.
124 static const char* cmVS15GenName(const std::string& name, std::string& genName)
126 if (strncmp(name.c_str(), vs15generatorName,
127 sizeof(vs15generatorName) - 6) != 0) {
130 const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
131 if (cmHasLiteralPrefix(p, " 2017")) {
134 genName = std::string(vs15generatorName) + p;
138 class cmGlobalVisualStudioVersionedGenerator::Factory15
139 : public cmGlobalGeneratorFactory
142 std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
143 const std::string& name, bool allowArch, cmake* cm) const override
146 const char* p = cmVS15GenName(name, genName);
148 return std::unique_ptr<cmGlobalGenerator>();
151 return std::unique_ptr<cmGlobalGenerator>(
152 new cmGlobalVisualStudioVersionedGenerator(
153 cmGlobalVisualStudioGenerator::VS15, cm, genName, ""));
155 if (!allowArch || *p++ != ' ') {
156 return std::unique_ptr<cmGlobalGenerator>();
158 if (strcmp(p, "Win64") == 0) {
159 return std::unique_ptr<cmGlobalGenerator>(
160 new cmGlobalVisualStudioVersionedGenerator(
161 cmGlobalVisualStudioGenerator::VS15, cm, genName, "x64"));
163 if (strcmp(p, "ARM") == 0) {
164 return std::unique_ptr<cmGlobalGenerator>(
165 new cmGlobalVisualStudioVersionedGenerator(
166 cmGlobalVisualStudioGenerator::VS15, cm, genName, "ARM"));
168 return std::unique_ptr<cmGlobalGenerator>();
171 void GetDocumentation(cmDocumentationEntry& entry) const override
173 entry.Name = std::string(vs15generatorName) + " [arch]";
174 entry.Brief = "Generates Visual Studio 2017 project files. "
175 "Optional [arch] can be \"Win64\" or \"ARM\".";
178 std::vector<std::string> GetGeneratorNames() const override
180 std::vector<std::string> names;
181 names.push_back(vs15generatorName);
185 std::vector<std::string> GetGeneratorNamesWithPlatform() const override
187 std::vector<std::string> names;
188 names.push_back(vs15generatorName + std::string(" ARM"));
189 names.push_back(vs15generatorName + std::string(" Win64"));
193 bool SupportsToolset() const override { return true; }
194 bool SupportsPlatform() const override { return true; }
196 std::vector<std::string> GetKnownPlatforms() const override
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");
206 std::string GetDefaultPlatformName() const override { return "Win32"; }
209 std::unique_ptr<cmGlobalGeneratorFactory>
210 cmGlobalVisualStudioVersionedGenerator::NewFactory15()
212 return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory15);
215 static const char vs16generatorName[] = "Visual Studio 16 2019";
217 // Map generator name without year to name with year.
218 static const char* cmVS16GenName(const std::string& name, std::string& genName)
220 if (strncmp(name.c_str(), vs16generatorName,
221 sizeof(vs16generatorName) - 6) != 0) {
224 const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
225 if (cmHasLiteralPrefix(p, " 2019")) {
228 genName = std::string(vs16generatorName) + p;
232 class cmGlobalVisualStudioVersionedGenerator::Factory16
233 : public cmGlobalGeneratorFactory
236 std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
237 const std::string& name, bool /*allowArch*/, cmake* cm) const override
240 const char* p = cmVS16GenName(name, genName);
242 return std::unique_ptr<cmGlobalGenerator>();
245 return std::unique_ptr<cmGlobalGenerator>(
246 new cmGlobalVisualStudioVersionedGenerator(
247 cmGlobalVisualStudioGenerator::VS16, cm, genName, ""));
249 return std::unique_ptr<cmGlobalGenerator>();
252 void GetDocumentation(cmDocumentationEntry& entry) const override
254 entry.Name = std::string(vs16generatorName);
255 entry.Brief = "Generates Visual Studio 2019 project files. "
256 "Use -A option to specify architecture.";
259 std::vector<std::string> GetGeneratorNames() const override
261 std::vector<std::string> names;
262 names.push_back(vs16generatorName);
266 std::vector<std::string> GetGeneratorNamesWithPlatform() const override
268 return std::vector<std::string>();
271 bool SupportsToolset() const override { return true; }
272 bool SupportsPlatform() const override { return true; }
274 std::vector<std::string> GetKnownPlatforms() const override
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");
284 std::string GetDefaultPlatformName() const override
286 return VSHostPlatformName();
290 std::unique_ptr<cmGlobalGeneratorFactory>
291 cmGlobalVisualStudioVersionedGenerator::NewFactory16()
293 return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory16);
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))
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();
315 bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
316 const std::string& name) const
319 switch (this->Version) {
320 case cmGlobalVisualStudioGenerator::VS9:
321 case cmGlobalVisualStudioGenerator::VS10:
322 case cmGlobalVisualStudioGenerator::VS11:
323 case cmGlobalVisualStudioGenerator::VS12:
324 case cmGlobalVisualStudioGenerator::VS14:
326 case cmGlobalVisualStudioGenerator::VS15:
327 if (cmVS15GenName(name, genName)) {
328 return genName == this->GetName();
331 case cmGlobalVisualStudioGenerator::VS16:
332 if (cmVS16GenName(name, genName)) {
333 return genName == this->GetName();
340 bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
341 std::string const& i, cmMakefile* mf)
344 if (!this->vsSetupAPIHelper.SetVSInstance(i)) {
345 std::ostringstream e;
346 /* clang-format off */
349 " " << this->GetName() << "\n"
350 "could not find specified instance of Visual Studio:\n"
352 /* clang-format on */
353 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
358 std::string vsInstance;
359 if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
360 std::ostringstream e;
361 /* clang-format off */
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());
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);
382 bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
383 std::string& dir) const
385 return vsSetupAPIHelper.GetVSInstanceInfo(dir);
388 bool cmGlobalVisualStudioVersionedGenerator::GetVSInstanceVersion(
389 unsigned long long& vsInstanceVersion) const
391 return vsSetupAPIHelper.GetVSInstanceVersion(vsInstanceVersion);
394 bool cmGlobalVisualStudioVersionedGenerator::IsDefaultToolset(
395 const std::string& version) const
397 if (version.empty()) {
401 std::string vcToolsetVersion;
402 if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
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;
415 bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const
417 // Supported from Visual Studio 16.7 Preview 3.
418 if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
421 if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
424 unsigned long long const vsInstanceVersion16_7_P2 = 4503631666610212;
425 unsigned long long vsInstanceVersion;
426 return (this->GetVSInstanceVersion(vsInstanceVersion) &&
427 vsInstanceVersion > vsInstanceVersion16_7_P2);
431 cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
434 switch (this->Version) {
435 case cmGlobalVisualStudioGenerator::VS9:
436 case cmGlobalVisualStudioGenerator::VS10:
437 case cmGlobalVisualStudioGenerator::VS11:
438 case cmGlobalVisualStudioGenerator::VS12:
440 case cmGlobalVisualStudioGenerator::VS14:
442 case cmGlobalVisualStudioGenerator::VS15:
443 case cmGlobalVisualStudioGenerator::VS16:
449 std::string cmGlobalVisualStudioVersionedGenerator::GetAuxiliaryToolset() const
451 const char* version = this->GetPlatformToolsetVersion();
453 std::string instancePath;
454 GetVSInstance(instancePath);
455 std::string toolsetDir = instancePath + "/VC/Auxiliary/Build";
457 if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
458 std::string toolsetDot =
459 cmStrCat(toolsetDir, '.', version, "/Microsoft.VCToolsVersion.",
461 if (cmSystemTools::PathExists(toolsetDot)) {
465 std::string toolsetPath =
466 cmStrCat(toolsetDir, sep, version, "/Microsoft.VCToolsVersion.", version,
468 cmSystemTools::ConvertToUnixSlashes(toolsetPath);
474 bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
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);
485 return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
487 // Otherwise we must choose a Win 10 SDK even if we are not targeting
489 return this->SelectWindows10SDK(mf, false);
492 bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
493 std::string& toolset) const
495 if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
496 if (this->IsWindowsStoreToolsetInstalled() &&
497 this->IsWindowsDesktopToolsetInstalled()) {
498 toolset = VSVersionToToolset(this->Version);
504 return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
508 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
511 return vsSetupAPIHelper.IsVSInstalled();
514 bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
517 return vsSetupAPIHelper.IsWin10SDKInstalled();
520 bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
522 // Does the VS installer tool know about one?
523 if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
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",
544 cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersionDefault(
547 return std::string();
550 std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
554 // Ask Visual Studio Installer tool.
556 if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
557 msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
558 if (cmSystemTools::FileExists(msbuild)) {
561 msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
562 if (cmSystemTools::FileExists(msbuild)) {
567 msbuild = "MSBuild.exe";
571 std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
575 // Ask Visual Studio Installer tool.
577 if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
578 devenv = vs + "/Common7/IDE/devenv.com";
579 if (cmSystemTools::FileExists(devenv)) {
584 devenv = "devenv.com";