cmGlobalVisualStudioVersionedGenerator.cxx 14.1 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
24
#else
#  include "cmsys/SystemInformation.hxx"
#endif

25
26
27
28
29
30
31
32
33
34
35
36
37
38
static std::string VSHostPlatformName()
{
#ifdef HOST_PLATFORM_NAME
  return HOST_PLATFORM_NAME;
#else
  cmsys::SystemInformation info;
  if (info.Is64Bits()) {
    return "x64";
  } else {
    return "Win32";
  }
#endif
}

39
40
41
42
43
44
45
46
47
48
49
50
51
52
static std::string VSHostArchitecture()
{
#ifdef HOST_TOOLS_ARCH
  return HOST_TOOLS_ARCH;
#else
  cmsys::SystemInformation info;
  if (info.Is64Bits()) {
    return "x64";
  } else {
    return "x86";
  }
#endif
}

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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;
69
70
    case cmGlobalVisualStudioGenerator::VS16:
      return 16;
71
72
73
74
  }
  return 0;
}

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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:
92
      return "v142";
93
94
95
96
  }
  return "";
}

97
static const char vs15generatorName[] = "Visual Studio 15 2017";
98
99
100
101
102

// 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,
103
              sizeof(vs15generatorName) - 6) != 0) {
104
105
    return 0;
  }
106
107
108
109
  const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
  if (cmHasLiteralPrefix(p, " 2017")) {
    p += 5;
  }
110
111
112
113
  genName = std::string(vs15generatorName) + p;
  return p;
}

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

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

151
  std::vector<std::string> GetGeneratorNames() const override
152
  {
153
    std::vector<std::string> names;
154
    names.push_back(vs15generatorName);
155
156
157
158
159
160
    return names;
  }

  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  {
    std::vector<std::string> names;
161
162
    names.push_back(vs15generatorName + std::string(" ARM"));
    names.push_back(vs15generatorName + std::string(" Win64"));
163
    return names;
164
165
  }

166
167
  bool SupportsToolset() const override { return true; }
  bool SupportsPlatform() const override { return true; }
168
169
170
171
172
173
174
175
176
177

  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;
  }
178
179

  std::string GetDefaultPlatformName() const override { return "Win32"; }
180
181
};

182
183
cmGlobalGeneratorFactory*
cmGlobalVisualStudioVersionedGenerator::NewFactory15()
184
{
185
  return new Factory15;
186
187
}

188
189
190
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
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.";
  }

231
  std::vector<std::string> GetGeneratorNames() const override
232
  {
233
    std::vector<std::string> names;
234
    names.push_back(vs16generatorName);
235
236
237
238
239
240
    return names;
  }

  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
  {
    return std::vector<std::string>();
241
242
243
244
  }

  bool SupportsToolset() const override { return true; }
  bool SupportsPlatform() const override { return true; }
245
246
247
248
249
250
251
252
253
254

  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;
  }
255
256
257
258
259

  std::string GetDefaultPlatformName() const override
  {
    return VSHostPlatformName();
  }
260
261
262
263
264
265
266
267
};

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

268
cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
269
  VSVersion version, cmake* cm, const std::string& name,
270
271
  std::string const& platformInGeneratorName)
  : cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
272
  , vsSetupAPIHelper(VSVersionToMajor(version))
273
{
274
  this->Version = version;
275
276
277
278
279
280
  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) {
281
    this->DefaultPlatformName = VSHostPlatformName();
282
    this->DefaultPlatformToolsetHostArchitecture = VSHostArchitecture();
283
  }
284
285
}

286
bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
287
288
289
  const std::string& name) const
{
  std::string genName;
290
291
292
293
294
295
296
297
298
299
300
301
  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;
302
303
304
305
306
    case cmGlobalVisualStudioGenerator::VS16:
      if (cmVS16GenName(name, genName)) {
        return genName == this->GetName();
      }
      break;
307
308
309
310
  }
  return false;
}

311
bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
312
313
314
315
316
317
318
319
320
321
322
323
  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 */
324
      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
325
326
327
328
329
330
331
332
333
334
335
336
337
      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 */
338
    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    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;
}

353
354
bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
  std::string& dir) const
355
356
357
358
{
  return vsSetupAPIHelper.GetVSInstanceInfo(dir);
}

359
bool cmGlobalVisualStudioVersionedGenerator::IsDefaultToolset(
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  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;
}

380
std::string cmGlobalVisualStudioVersionedGenerator::GetAuxiliaryToolset() const
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
{
  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 {};
}

399
bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
400
401
402
403
{
  // If the Win 8.1 SDK is installed then we can select a SDK matching
  // the target Windows version.
  if (this->IsWin81SDKInstalled()) {
404
405
406
407
408
409
    // 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;
    }
410
411
412
413
414
415
416
    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);
}

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

433
434
bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
  const
435
{
436
  return vsSetupAPIHelper.IsVSInstalled();
437
438
}

439
440
bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
  const
441
{
442
443
444
  return vsSetupAPIHelper.IsWin10SDKInstalled();
}

445
bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
{
  // 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)) {
462
463
    return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
                                     true);
464
465
466
467
  }
  return false;
}

468
469
std::string cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersion()
  const
470
471
472
473
{
  return std::string();
}

474
std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
475
476
477
478
479
480
{
  std::string msbuild;

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

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

495
std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
496
497
498
499
500
501
502
503
504
505
506
{
  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;
    }
  }
507

508
509
  devenv = "devenv.com";
  return devenv;
510
}