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
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
404
405
406
407
408
409
410
{
  // If the Win 8.1 SDK is installed then we can select a SDK matching
  // the target Windows version.
  if (this->IsWin81SDKInstalled()) {
    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);
}

411
bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
412
413
414
415
416
  std::string& toolset) const
{
  if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
    if (this->IsWindowsStoreToolsetInstalled() &&
        this->IsWindowsDesktopToolsetInstalled()) {
417
      toolset = VSVersionToToolset(this->Version);
418
419
420
421
422
423
424
425
426
      return true;
    } else {
      return false;
    }
  }
  return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
    toolset);
}

427
428
bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
  const
429
{
430
  return vsSetupAPIHelper.IsVSInstalled();
431
432
}

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

439
bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
{
  // 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)) {
456
    return cmSystemTools::FileExists(win81Root + "/um/windows.h", true);
457
458
459
460
  }
  return false;
}

461
462
std::string cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersion()
  const
463
464
465
466
{
  return std::string();
}

467
std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
468
469
470
471
472
473
{
  std::string msbuild;

  // Ask Visual Studio Installer tool.
  std::string vs;
  if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
474
475
476
477
478
479
480
481
482
483
484
485
    std::string const& hostArch =
      this->GetPlatformToolsetHostArchitectureString();
    if (hostArch == "x64") {
      msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
      if (cmSystemTools::FileExists(msbuild)) {
        return msbuild;
      }
      msbuild = vs + "/MSBuild/15.0/Bin/amd64/MSBuild.exe";
      if (cmSystemTools::FileExists(msbuild)) {
        return msbuild;
      }
    }
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
}