cmGlobalVisualStudioVersionedGenerator.cxx 14.2 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmGlobalVisualStudioVersionedGenerator.h"
4 5

#include "cmAlgorithms.h"
6
#include "cmDocumentationEntry.h"
7 8
#include "cmLocalVisualStudio10Generator.h"
#include "cmMakefile.h"
9
#include "cmVSSetupHelper.h"
10
#include "cmake.h"
11

12
#if defined(_M_ARM64)
13
#  define HOST_PLATFORM_NAME "ARM64"
14
#  define HOST_TOOLS_ARCH ""
15
#elif defined(_M_ARM)
16
#  define HOST_PLATFORM_NAME "ARM"
17
#  define HOST_TOOLS_ARCH ""
18
#elif defined(_M_IA64)
19
#  define HOST_PLATFORM_NAME "Itanium"
20
#  define HOST_TOOLS_ARCH ""
21 22 23
#elif defined(_WIN64)
#  define HOST_PLATFORM_NAME "x64"
#  define HOST_TOOLS_ARCH "x64"
24
#else
25 26 27 28 29
static bool VSIsWow64()
{
  BOOL isWow64 = false;
  return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
}
30 31
#endif

32 33 34 35 36
static std::string VSHostPlatformName()
{
#ifdef HOST_PLATFORM_NAME
  return HOST_PLATFORM_NAME;
#else
37
  if (VSIsWow64()) {
38 39 40 41 42 43 44
    return "x64";
  } else {
    return "Win32";
  }
#endif
}

45 46 47 48 49
static std::string VSHostArchitecture()
{
#ifdef HOST_TOOLS_ARCH
  return HOST_TOOLS_ARCH;
#else
50
  if (VSIsWow64()) {
51 52 53 54 55 56 57
    return "x64";
  } else {
    return "x86";
  }
#endif
}

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
static unsigned int VSVersionToMajor(
  cmGlobalVisualStudioGenerator::VSVersion v)
{
  switch (v) {
    case cmGlobalVisualStudioGenerator::VS9:
      return 9;
    case cmGlobalVisualStudioGenerator::VS10:
      return 10;
    case cmGlobalVisualStudioGenerator::VS11:
      return 11;
    case cmGlobalVisualStudioGenerator::VS12:
      return 12;
    case cmGlobalVisualStudioGenerator::VS14:
      return 14;
    case cmGlobalVisualStudioGenerator::VS15:
      return 15;
74 75
    case cmGlobalVisualStudioGenerator::VS16:
      return 16;
76 77 78 79
  }
  return 0;
}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
static const char* VSVersionToToolset(
  cmGlobalVisualStudioGenerator::VSVersion v)
{
  switch (v) {
    case cmGlobalVisualStudioGenerator::VS9:
      return "v90";
    case cmGlobalVisualStudioGenerator::VS10:
      return "v100";
    case cmGlobalVisualStudioGenerator::VS11:
      return "v110";
    case cmGlobalVisualStudioGenerator::VS12:
      return "v120";
    case cmGlobalVisualStudioGenerator::VS14:
      return "v140";
    case cmGlobalVisualStudioGenerator::VS15:
      return "v141";
    case cmGlobalVisualStudioGenerator::VS16:
97
      return "v142";
98 99 100 101
  }
  return "";
}

102
static const char vs15generatorName[] = "Visual Studio 15 2017";
103 104 105 106 107

// Map generator name without year to name with year.
static const char* cmVS15GenName(const std::string& name, std::string& genName)
{
  if (strncmp(name.c_str(), vs15generatorName,
108
              sizeof(vs15generatorName) - 6) != 0) {
109 110
    return 0;
  }
111 112 113 114
  const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
  if (cmHasLiteralPrefix(p, " 2017")) {
    p += 5;
  }
115 116 117 118
  genName = std::string(vs15generatorName) + p;
  return p;
}

119
class cmGlobalVisualStudioVersionedGenerator::Factory15
120 121 122
  : public cmGlobalGeneratorFactory
{
public:
Vitaly Stakhovsky's avatar
Vitaly Stakhovsky committed
123 124
  cmGlobalGenerator* CreateGlobalGenerator(const std::string& name,
                                           cmake* cm) const override
125 126 127 128 129 130 131
  {
    std::string genName;
    const char* p = cmVS15GenName(name, genName);
    if (!p) {
      return 0;
    }
    if (!*p) {
132 133
      return new cmGlobalVisualStudioVersionedGenerator(
        cmGlobalVisualStudioGenerator::VS15, cm, genName, "");
134 135 136 137 138
    }
    if (*p++ != ' ') {
      return 0;
    }
    if (strcmp(p, "Win64") == 0) {
139 140
      return new cmGlobalVisualStudioVersionedGenerator(
        cmGlobalVisualStudioGenerator::VS15, cm, genName, "x64");
141 142
    }
    if (strcmp(p, "ARM") == 0) {
143 144
      return new cmGlobalVisualStudioVersionedGenerator(
        cmGlobalVisualStudioGenerator::VS15, cm, genName, "ARM");
145 146 147 148
    }
    return 0;
  }

Vitaly Stakhovsky's avatar
Vitaly Stakhovsky committed
149
  void GetDocumentation(cmDocumentationEntry& entry) const override
150 151
  {
    entry.Name = std::string(vs15generatorName) + " [arch]";
152
    entry.Brief = "Generates Visual Studio 2017 project files.  "
153 154 155
                  "Optional [arch] can be \"Win64\" or \"ARM\".";
  }

156
  std::vector<std::string> GetGeneratorNames() const override
157
  {
158
    std::vector<std::string> names;
159
    names.push_back(vs15generatorName);
160 161 162 163 164 165
    return names;
  }

  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  {
    std::vector<std::string> names;
166 167
    names.push_back(vs15generatorName + std::string(" ARM"));
    names.push_back(vs15generatorName + std::string(" Win64"));
168
    return names;
169 170
  }

171 172
  bool SupportsToolset() const override { return true; }
  bool SupportsPlatform() const override { return true; }
173 174 175 176 177 178 179 180 181 182

  std::vector<std::string> GetKnownPlatforms() const override
  {
    std::vector<std::string> platforms;
    platforms.emplace_back("x64");
    platforms.emplace_back("Win32");
    platforms.emplace_back("ARM");
    platforms.emplace_back("ARM64");
    return platforms;
  }
183 184

  std::string GetDefaultPlatformName() const override { return "Win32"; }
185 186
};

187 188
cmGlobalGeneratorFactory*
cmGlobalVisualStudioVersionedGenerator::NewFactory15()
189
{
190
  return new Factory15;
191 192
}

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
static const char vs16generatorName[] = "Visual Studio 16 2019";

// Map generator name without year to name with year.
static const char* cmVS16GenName(const std::string& name, std::string& genName)
{
  if (strncmp(name.c_str(), vs16generatorName,
              sizeof(vs16generatorName) - 6) != 0) {
    return 0;
  }
  const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
  if (cmHasLiteralPrefix(p, " 2019")) {
    p += 5;
  }
  genName = std::string(vs16generatorName) + p;
  return p;
}

class cmGlobalVisualStudioVersionedGenerator::Factory16
  : public cmGlobalGeneratorFactory
{
public:
  virtual cmGlobalGenerator* CreateGlobalGenerator(const std::string& name,
                                                   cmake* cm) const
  {
    std::string genName;
    const char* p = cmVS16GenName(name, genName);
    if (!p) {
      return 0;
    }
    if (!*p) {
      return new cmGlobalVisualStudioVersionedGenerator(
        cmGlobalVisualStudioGenerator::VS16, cm, genName, "");
    }
    return 0;
  }

  virtual void GetDocumentation(cmDocumentationEntry& entry) const
  {
    entry.Name = std::string(vs16generatorName);
    entry.Brief = "Generates Visual Studio 2019 project files.  "
                  "Use -A option to specify architecture.";
  }

236
  std::vector<std::string> GetGeneratorNames() const override
237
  {
238
    std::vector<std::string> names;
239
    names.push_back(vs16generatorName);
240 241 242 243 244 245
    return names;
  }

  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  {
    return std::vector<std::string>();
246 247 248 249
  }

  bool SupportsToolset() const override { return true; }
  bool SupportsPlatform() const override { return true; }
250 251 252 253 254 255 256 257 258 259

  std::vector<std::string> GetKnownPlatforms() const override
  {
    std::vector<std::string> platforms;
    platforms.emplace_back("x64");
    platforms.emplace_back("Win32");
    platforms.emplace_back("ARM");
    platforms.emplace_back("ARM64");
    return platforms;
  }
260 261 262 263 264

  std::string GetDefaultPlatformName() const override
  {
    return VSHostPlatformName();
  }
265 266 267 268 269 270 271 272
};

cmGlobalGeneratorFactory*
cmGlobalVisualStudioVersionedGenerator::NewFactory16()
{
  return new Factory16;
}

273
cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
274
  VSVersion version, cmake* cm, const std::string& name,
275 276
  std::string const& platformInGeneratorName)
  : cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
277
  , vsSetupAPIHelper(VSVersionToMajor(version))
278
{
279
  this->Version = version;
280 281 282 283 284 285
  this->ExpressEdition = false;
  this->DefaultPlatformToolset = VSVersionToToolset(this->Version);
  this->DefaultCLFlagTableName = VSVersionToToolset(this->Version);
  this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version);
  this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version);
  if (this->Version >= cmGlobalVisualStudioGenerator::VS16) {
286
    this->DefaultPlatformName = VSHostPlatformName();
287
    this->DefaultPlatformToolsetHostArchitecture = VSHostArchitecture();
288
  }
289 290
}

291
bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
292 293 294
  const std::string& name) const
{
  std::string genName;
295 296 297 298 299 300 301 302 303 304 305 306
  switch (this->Version) {
    case cmGlobalVisualStudioGenerator::VS9:
    case cmGlobalVisualStudioGenerator::VS10:
    case cmGlobalVisualStudioGenerator::VS11:
    case cmGlobalVisualStudioGenerator::VS12:
    case cmGlobalVisualStudioGenerator::VS14:
      break;
    case cmGlobalVisualStudioGenerator::VS15:
      if (cmVS15GenName(name, genName)) {
        return genName == this->GetName();
      }
      break;
307 308 309 310 311
    case cmGlobalVisualStudioGenerator::VS16:
      if (cmVS16GenName(name, genName)) {
        return genName == this->GetName();
      }
      break;
312 313 314 315
  }
  return false;
}

316
bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
317 318 319 320 321 322 323 324 325 326 327 328
  std::string const& i, cmMakefile* mf)
{
  if (!i.empty()) {
    if (!this->vsSetupAPIHelper.SetVSInstance(i)) {
      std::ostringstream e;
      /* clang-format off */
      e <<
        "Generator\n"
        "  " << this->GetName() << "\n"
        "could not find specified instance of Visual Studio:\n"
        "  " << i;
      /* clang-format on */
329
      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
330 331 332 333 334 335 336 337 338 339 340 341 342
      return false;
    }
  }

  std::string vsInstance;
  if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
    std::ostringstream e;
    /* clang-format off */
    e <<
      "Generator\n"
      "  " << this->GetName() << "\n"
      "could not find any instance of Visual Studio.\n";
    /* clang-format on */
343
    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
344 345 346 347 348 349 350 351 352 353 354 355 356 357
    return false;
  }

  // Save the selected instance persistently.
  std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
  if (vsInstance != genInstance) {
    this->CMakeInstance->AddCacheEntry(
      "CMAKE_GENERATOR_INSTANCE", vsInstance.c_str(),
      "Generator instance identifier.", cmStateEnums::INTERNAL);
  }

  return true;
}

358 359
bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
  std::string& dir) const
360 361 362 363
{
  return vsSetupAPIHelper.GetVSInstanceInfo(dir);
}

364
bool cmGlobalVisualStudioVersionedGenerator::IsDefaultToolset(
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
  const std::string& version) const
{
  if (version.empty()) {
    return true;
  }

  std::string vcToolsetVersion;
  if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {

    cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9]+");
    if (regex.find(version) && regex.find(vcToolsetVersion)) {
      const auto majorMinorEnd = vcToolsetVersion.find('.', 3);
      const auto majorMinor = vcToolsetVersion.substr(0, majorMinorEnd);
      return version == majorMinor;
    }
  }

  return false;
}

385
std::string cmGlobalVisualStudioVersionedGenerator::GetAuxiliaryToolset() const
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
{
  const char* version = this->GetPlatformToolsetVersion();
  if (version) {
    std::string instancePath;
    GetVSInstance(instancePath);
    std::stringstream path;
    path << instancePath;
    path << "/VC/Auxiliary/Build/";
    path << version;
    path << "/Microsoft.VCToolsVersion." << version << ".props";

    std::string toolsetPath = path.str();
    cmSystemTools::ConvertToUnixSlashes(toolsetPath);
    return toolsetPath;
  }
  return {};
}

404
bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
405 406 407 408
{
  // If the Win 8.1 SDK is installed then we can select a SDK matching
  // the target Windows version.
  if (this->IsWin81SDKInstalled()) {
409 410 411 412 413 414
    // VS 2019 does not default to 8.1 so specify it explicitly when needed.
    if (this->Version >= cmGlobalVisualStudioGenerator::VS16 &&
        !cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
      this->SetWindowsTargetPlatformVersion("8.1", mf);
      return true;
    }
415 416 417 418 419 420 421
    return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
  }
  // Otherwise we must choose a Win 10 SDK even if we are not targeting
  // Windows 10.
  return this->SelectWindows10SDK(mf, false);
}

422
bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
423 424 425 426 427
  std::string& toolset) const
{
  if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
    if (this->IsWindowsStoreToolsetInstalled() &&
        this->IsWindowsDesktopToolsetInstalled()) {
428
      toolset = VSVersionToToolset(this->Version);
429 430 431 432 433 434 435 436 437
      return true;
    } else {
      return false;
    }
  }
  return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
    toolset);
}

438 439
bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
  const
440
{
441
  return vsSetupAPIHelper.IsVSInstalled();
442 443
}

444 445
bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
  const
446
{
447 448 449
  return vsSetupAPIHelper.IsWin10SDKInstalled();
}

450
bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
{
  // Does the VS installer tool know about one?
  if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
    return true;
  }

  // Does the registry know about one (e.g. from VS 2015)?
  std::string win81Root;
  if (cmSystemTools::ReadRegistryValue(
        "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"
        "Windows Kits\\Installed Roots;KitsRoot81",
        win81Root, cmSystemTools::KeyWOW64_32) ||
      cmSystemTools::ReadRegistryValue(
        "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
        "Windows Kits\\Installed Roots;KitsRoot81",
        win81Root, cmSystemTools::KeyWOW64_32)) {
467 468
    return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
                                     true);
469 470 471 472
  }
  return false;
}

473 474
std::string cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersion()
  const
475 476 477 478
{
  return std::string();
}

479
std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
480 481 482 483 484 485
{
  std::string msbuild;

  // Ask Visual Studio Installer tool.
  std::string vs;
  if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
486 487 488 489
    msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
    if (cmSystemTools::FileExists(msbuild)) {
      return msbuild;
    }
490 491 492 493 494 495 496 497 498 499
    msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
    if (cmSystemTools::FileExists(msbuild)) {
      return msbuild;
    }
  }

  msbuild = "MSBuild.exe";
  return msbuild;
}

500
std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
501 502 503 504 505 506 507 508 509 510 511
{
  std::string devenv;

  // Ask Visual Studio Installer tool.
  std::string vs;
  if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
    devenv = vs + "/Common7/IDE/devenv.com";
    if (cmSystemTools::FileExists(devenv)) {
      return devenv;
    }
  }
512

513 514
  devenv = "devenv.com";
  return devenv;
515
}