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
}