cmGlobalVisualStudioGenerator.cxx 33.5 KB
Newer Older
1

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

6
#include "cmsys/Encoding.hxx"
7
#include <future>
8
#include <iostream>
9 10
#include <objbase.h>
#include <shellapi.h>
11
#include <windows.h>
12

13
#include "cmAlgorithms.h"
14
#include "cmCallVisualStudioMacro.h"
15
#include "cmCustomCommand.h"
16
#include "cmGeneratedFileStream.h"
17 18
#include "cmGeneratorTarget.h"
#include "cmLocalVisualStudioGenerator.h"
19
#include "cmMakefile.h"
20
#include "cmSourceFile.h"
21
#include "cmState.h"
22
#include "cmTarget.h"
23
#include "cmake.h"
24

25 26
cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(
  cmake* cm, std::string const& platformInGeneratorName)
27
  : cmGlobalGenerator(cm)
28
{
29
  cm->GetState()->SetIsGeneratorMultiConfig(true);
30 31
  cm->GetState()->SetWindowsShell(true);
  cm->GetState()->SetWindowsVSIDE(true);
32 33 34 35 36

  if (platformInGeneratorName.empty()) {
    this->DefaultPlatformName = "Win32";
  } else {
    this->DefaultPlatformName = platformInGeneratorName;
37
    this->PlatformInGeneratorName = true;
38
  }
39 40 41 42 43
}

cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator()
{
}
44

45 46 47 48 49 50 51 52 53 54 55
cmGlobalVisualStudioGenerator::VSVersion
cmGlobalVisualStudioGenerator::GetVersion() const
{
  return this->Version;
}

void cmGlobalVisualStudioGenerator::SetVersion(VSVersion v)
{
  this->Version = v;
}

56 57 58 59
void cmGlobalVisualStudioGenerator::EnableLanguage(
  std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
{
  mf->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT",
60
                    this->DefaultPlatformName);
61 62 63
  this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
}

64 65 66 67 68 69 70 71
bool cmGlobalVisualStudioGenerator::SetGeneratorPlatform(std::string const& p,
                                                         cmMakefile* mf)
{
  if (this->GetPlatformName() == "x64") {
    mf->AddDefinition("CMAKE_FORCE_WIN64", "TRUE");
  } else if (this->GetPlatformName() == "Itanium") {
    mf->AddDefinition("CMAKE_FORCE_IA64", "TRUE");
  }
72
  mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName());
73 74 75 76 77 78 79 80 81 82 83
  return this->cmGlobalGenerator::SetGeneratorPlatform(p, mf);
}

std::string const& cmGlobalVisualStudioGenerator::GetPlatformName() const
{
  if (!this->GeneratorPlatform.empty()) {
    return this->GeneratorPlatform;
  }
  return this->DefaultPlatformName;
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const
{
  switch (this->Version) {
    case cmGlobalVisualStudioGenerator::VS9:
      return "9.0";
    case cmGlobalVisualStudioGenerator::VS10:
      return "10.0";
    case cmGlobalVisualStudioGenerator::VS11:
      return "11.0";
    case cmGlobalVisualStudioGenerator::VS12:
      return "12.0";
    case cmGlobalVisualStudioGenerator::VS14:
      return "14.0";
    case cmGlobalVisualStudioGenerator::VS15:
      return "15.0";
99 100
    case cmGlobalVisualStudioGenerator::VS16:
      return "16.0";
101 102 103 104
  }
  return "";
}

105 106
void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout)
{
107 108 109
  char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) };
  fout.write(utf8bom, 3);

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
  switch (this->Version) {
    case cmGlobalVisualStudioGenerator::VS9:
      fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n";
      fout << "# Visual Studio 2008\n";
      break;
    case cmGlobalVisualStudioGenerator::VS10:
      fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n";
      if (this->ExpressEdition) {
        fout << "# Visual C++ Express 2010\n";
      } else {
        fout << "# Visual Studio 2010\n";
      }
      break;
    case cmGlobalVisualStudioGenerator::VS11:
      fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
      if (this->ExpressEdition) {
        fout << "# Visual Studio Express 2012 for Windows Desktop\n";
      } else {
        fout << "# Visual Studio 2012\n";
      }
      break;
    case cmGlobalVisualStudioGenerator::VS12:
      fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
      if (this->ExpressEdition) {
        fout << "# Visual Studio Express 2013 for Windows Desktop\n";
      } else {
        fout << "# Visual Studio 2013\n";
      }
      break;
    case cmGlobalVisualStudioGenerator::VS14:
      // Visual Studio 14 writes .sln format 12.00
      fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
      if (this->ExpressEdition) {
        fout << "# Visual Studio Express 14 for Windows Desktop\n";
      } else {
        fout << "# Visual Studio 14\n";
      }
      break;
    case cmGlobalVisualStudioGenerator::VS15:
      // Visual Studio 15 writes .sln format 12.00
      fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
      if (this->ExpressEdition) {
        fout << "# Visual Studio Express 15 for Windows Desktop\n";
      } else {
        fout << "# Visual Studio 15\n";
      }
      break;
157 158 159 160 161 162 163 164 165
    case cmGlobalVisualStudioGenerator::VS16:
      // Visual Studio 16 writes .sln format 12.00
      fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
      if (this->ExpressEdition) {
        fout << "# Visual Studio Express 16 for Windows Desktop\n";
      } else {
        fout << "# Visual Studio 16\n";
      }
      break;
166 167 168
  }
}

169
std::string cmGlobalVisualStudioGenerator::GetRegistryBase()
170
{
171
  return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
172 173
}

174
std::string cmGlobalVisualStudioGenerator::GetRegistryBase(const char* version)
175 176
{
  std::string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\";
177
  return key + version;
178 179
}

180
void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
181 182 183 184 185 186
{
  // Add a special target that depends on ALL projects for easy build
  // of one configuration only.
  const char* no_working_dir = 0;
  std::vector<std::string> no_depends;
  cmCustomCommandLines no_commands;
187 188
  for (auto const& it : this->ProjectMap) {
    std::vector<cmLocalGenerator*> const& gen = it.second;
189
    // add the ALL_BUILD to the first local generator of each project
190
    if (!gen.empty()) {
191 192
      // Use no actual command lines so that the target itself is not
      // considered always out of date.
193
      cmTarget* allBuild = gen[0]->GetMakefile()->AddUtilityCommand(
194 195
        "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, no_working_dir,
        no_depends, no_commands, false, "Build all projects");
196

197
      cmGeneratorTarget* gt = new cmGeneratorTarget(allBuild, gen[0]);
198
      gen[0]->AddGeneratorTarget(gt);
199 200 201 202

      //
      // Organize in the "predefined targets" folder:
      //
203
      if (this->UseFolderProperty()) {
204
        allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
205
      }
206

207
      // Now make all targets depend on the ALL_BUILD target
208
      for (cmLocalGenerator const* i : gen) {
209
        for (cmGeneratorTarget* tgt : i->GetGeneratorTargets()) {
210 211
          if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
              tgt->IsImported()) {
212
            continue;
213
          }
214
          if (!this->IsExcluded(tgt)) {
215
            allBuild->AddUtility(tgt->GetName());
216 217
          }
        }
218 219
      }
    }
220
  }
221

222 223 224
  // Configure CMake Visual Studio macros, for this user on this version
  // of Visual Studio.
  this->ConfigureCMakeVisualStudioMacros();
225 226
}

227 228
void cmGlobalVisualStudioGenerator::ComputeTargetObjectDirectory(
  cmGeneratorTarget* gt) const
229
{
230
  std::string dir = gt->LocalGenerator->GetCurrentBinaryDirectory();
231
  dir += "/";
232
  std::string tgtDir = gt->LocalGenerator->GetTargetDirectory(gt);
233
  if (!tgtDir.empty()) {
234 235
    dir += tgtDir;
    dir += "/";
236
  }
237
  const char* cd = this->GetCMakeCFGIntDir();
238
  if (cd && *cd) {
239 240
    dir += cd;
    dir += "/";
241
  }
242
  gt->ObjectDirectory = dir;
243 244
}

245
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
246 247
                                        const std::string& regKeyBase,
                                        std::string& nextAvailableSubKeyName);
248

249
void RegisterVisualStudioMacros(const std::string& macrosFile,
250
                                const std::string& regKeyBase);
251

252
#define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
253

254
#define CMAKE_VSMACROS_RELOAD_MACRONAME                                       \
255
  "Macros.CMakeVSMacros2.Macros.ReloadProjects"
256

257
#define CMAKE_VSMACROS_STOP_MACRONAME "Macros.CMakeVSMacros2.Macros.StopBuild"
258

259 260 261 262
void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
{
  std::string dir = this->GetUserMacrosDirectory();

263
  if (!dir.empty()) {
264
    std::string src = cmSystemTools::GetCMakeRoot();
265 266 267 268
    src += "/Templates/" CMAKE_VSMACROS_FILENAME;

    std::string dst = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;

269 270 271 272 273 274
    // Copy the macros file to the user directory only if the
    // destination does not exist or the source location is newer.
    // This will allow the user to edit the macros for development
    // purposes but newer versions distributed with CMake will replace
    // older versions in user directories.
    int res;
275 276
    if (!cmSystemTools::FileTimeCompare(src, dst, &res) || res > 0) {
      if (!cmSystemTools::CopyFileAlways(src, dst)) {
277 278 279
        std::ostringstream oss;
        oss << "Could not copy from: " << src << std::endl;
        oss << "                 to: " << dst << std::endl;
280
        cmSystemTools::Message(oss.str(), "Warning");
281
      }
282
    }
283

284
    RegisterVisualStudioMacros(dst, this->GetUserMacrosRegKeyBase());
285
  }
286 287
}

288 289
void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
  MacroName m, const char* vsSolutionFile)
290 291 292 293 294 295
{
  // If any solution or project files changed during the generation,
  // tell Visual Studio to reload them...
  cmMakefile* mf = this->LocalGenerators[0]->GetMakefile();
  std::string dir = this->GetUserMacrosDirectory();

296 297 298 299 300 301
  // Only really try to call the macro if:
  //  - there is a UserMacrosDirectory
  //  - the CMake vsmacros file exists
  //  - the CMake vsmacros file is registered
  //  - there were .sln/.vcproj files changed during generation
  //
302
  if (!dir.empty()) {
303 304 305
    std::string macrosFile = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
    std::string nextSubkeyName;
    if (cmSystemTools::FileExists(macrosFile.c_str()) &&
306 307
        IsVisualStudioMacrosFileRegistered(
          macrosFile, this->GetUserMacrosRegKeyBase(), nextSubkeyName)) {
308
      std::string topLevelSlnName;
309
      if (vsSolutionFile) {
310
        topLevelSlnName = vsSolutionFile;
311
      } else {
312
        topLevelSlnName = mf->GetCurrentBinaryDirectory();
313
        topLevelSlnName += "/";
314
        topLevelSlnName += this->LocalGenerators[0]->GetProjectName();
315
        topLevelSlnName += ".sln";
316
      }
317

318
      if (m == MacroReload) {
319 320
        std::vector<std::string> filenames;
        this->GetFilesReplacedDuringGenerate(filenames);
321
        if (!filenames.empty()) {
322
          std::string projects = cmJoin(filenames, ";");
323 324 325
          cmCallVisualStudioMacro::CallMacro(
            topLevelSlnName, CMAKE_VSMACROS_RELOAD_MACRONAME, projects,
            this->GetCMakeInstance()->GetDebugOutput());
326
        }
327 328 329
      } else if (m == MacroStop) {
        cmCallVisualStudioMacro::CallMacro(
          topLevelSlnName, CMAKE_VSMACROS_STOP_MACRONAME, "",
330
          this->GetCMakeInstance()->GetDebugOutput());
331 332
      }
    }
333
  }
334 335 336 337 338 339 340
}

std::string cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
{
  return "";
}

341 342 343 344 345
std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
{
  return "";
}

346
void cmGlobalVisualStudioGenerator::FillLinkClosure(
347
  const cmGeneratorTarget* target, TargetSet& linked)
348
{
349 350
  if (linked.insert(target).second) {
    TargetDependSet const& depends = this->GetTargetDirectDepends(target);
351 352 353
    for (cmTargetDepend const& di : depends) {
      if (di.IsLink()) {
        this->FillLinkClosure(di, linked);
354 355
      }
    }
356
  }
357 358 359
}

cmGlobalVisualStudioGenerator::TargetSet const&
360
cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmGeneratorTarget* target)
361
{
362
  auto i = this->TargetLinkClosure.find(target);
363
  if (i == this->TargetLinkClosure.end()) {
364
    TargetSetMap::value_type entry(target, TargetSet());
365 366
    i = this->TargetLinkClosure.insert(entry).first;
    this->FillLinkClosure(target, i->second);
367
  }
368 369 370 371
  return i->second;
}

void cmGlobalVisualStudioGenerator::FollowLinkDepends(
372
  const cmGeneratorTarget* target, std::set<const cmGeneratorTarget*>& linked)
373
{
374
  if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
375
    return;
376 377
  }
  if (linked.insert(target).second &&
378
      target->GetType() == cmStateEnums::STATIC_LIBRARY) {
379 380
    // Static library targets do not list their link dependencies so
    // we must follow them transitively now.
381
    TargetDependSet const& depends = this->GetTargetDirectDepends(target);
382 383 384
    for (cmTargetDepend const& di : depends) {
      if (di.IsLink()) {
        this->FollowLinkDepends(di, linked);
385 386
      }
    }
387
  }
388 389
}

390 391
bool cmGlobalVisualStudioGenerator::ComputeTargetDepends()
{
392
  if (!this->cmGlobalGenerator::ComputeTargetDepends()) {
393
    return false;
394
  }
395
  for (auto const& it : this->ProjectMap) {
396 397
    for (const cmLocalGenerator* i : it.second) {
      for (cmGeneratorTarget* ti : i->GetGeneratorTargets()) {
398
        this->ComputeVSTargetDepends(ti);
399 400
      }
    }
401
  }
402 403 404
  return true;
}

405
static bool VSLinkable(cmGeneratorTarget const* t)
Brad King's avatar
Brad King committed
406
{
407
  return t->IsLinkable() || t->GetType() == cmStateEnums::OBJECT_LIBRARY;
Brad King's avatar
Brad King committed
408 409
}

410
void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(
411
  cmGeneratorTarget* target)
412
{
413
  if (this->VSTargetDepends.find(target) != this->VSTargetDepends.end()) {
414
    return;
415
  }
416
  VSDependSet& vsTargetDepend = this->VSTargetDepends[target];
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
  // VS <= 7.1 has two behaviors that affect solution dependencies.
  //
  // (1) Solution-level dependencies between a linkable target and a
  // library cause that library to be linked.  We use an intermedite
  // empty utility target to express the dependency.  (VS 8 and above
  // provide a project file "LinkLibraryDependencies" setting to
  // choose whether to activate this behavior.  We disable it except
  // when linking external project files.)
  //
  // (2) We cannot let static libraries depend directly on targets to
  // which they "link" because the librarian tool will copy the
  // targets into the static library.  While the work-around for
  // behavior (1) would also avoid this, it would create a large
  // number of extra utility targets for little gain.  Instead, use
  // the above work-around only for dependencies explicitly added by
  // the add_dependencies() command.  Approximate link dependencies by
  // leaving them out for the static library itself but following them
  // transitively for other targets.

436 437 438 439
  bool allowLinkable = (target->GetType() != cmStateEnums::STATIC_LIBRARY &&
                        target->GetType() != cmStateEnums::SHARED_LIBRARY &&
                        target->GetType() != cmStateEnums::MODULE_LIBRARY &&
                        target->GetType() != cmStateEnums::EXECUTABLE);
440

441
  TargetDependSet const& depends = this->GetTargetDirectDepends(target);
442 443 444 445

  // Collect implicit link dependencies (target_link_libraries).
  // Static libraries cannot depend on their link implementation
  // due to behavior (2), but they do not really need to.
446
  std::set<cmGeneratorTarget const*> linkDepends;
447
  if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
448
    for (cmTargetDepend const& di : depends) {
449
      if (di.IsLink()) {
450
        this->FollowLinkDepends(di, linkDepends);
451 452
      }
    }
453
  }
454

455
  // Collect explicit util dependencies (add_dependencies).
456
  std::set<cmGeneratorTarget const*> utilDepends;
457
  for (cmTargetDepend const& di : depends) {
458
    if (di.IsUtil()) {
459
      this->FollowLinkDepends(di, utilDepends);
460
    }
461
  }
462

463 464 465
  // Collect all targets linked by this target so we can avoid
  // intermediate targets below.
  TargetSet linked;
466
  if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
467
    linked = this->GetTargetLinkClosure(target);
468
  }
469 470

  // Emit link dependencies.
471
  for (cmGeneratorTarget const* dep : linkDepends) {
472
    vsTargetDepend.insert(dep->GetName());
473
  }
474 475

  // Emit util dependencies.  Possibly use intermediate targets.
476
  for (cmGeneratorTarget const* dgt : utilDepends) {
477
    if (allowLinkable || !VSLinkable(dgt) || linked.count(dgt)) {
478
      // Direct dependency allowed.
479
      vsTargetDepend.insert(dgt->GetName());
480
    } else {
481 482
      // Direct dependency on linkable target not allowed.
      // Use an intermediate utility target.
483
      vsTargetDepend.insert(this->GetUtilityDepend(dgt));
484
    }
485
  }
486 487
}

488
bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf)
489 490 491 492
{
  // Visual Studio generators know how to lookup their build tool
  // directly instead of needing a helper module to do it, so we
  // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
493
  if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
494
    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram());
495
  }
496
  return true;
497 498
}

499 500
std::string cmGlobalVisualStudioGenerator::GetUtilityDepend(
  cmGeneratorTarget const* target)
501
{
502
  auto i = this->UtilityDepends.find(target);
503
  if (i == this->UtilityDepends.end()) {
504
    std::string name = this->WriteUtilityDepend(target);
505 506
    UtilityDependsMap::value_type entry(target, name);
    i = this->UtilityDepends.insert(entry).first;
507
  }
508 509 510
  return i->second;
}

511
std::string cmGlobalVisualStudioGenerator::GetStartupProjectName(
512 513 514
  cmLocalGenerator const* root) const
{
  const char* n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
515
  if (n && *n) {
516
    std::string startup = n;
517
    if (this->FindTarget(startup)) {
518
      return startup;
519
    } else {
520
      root->GetMakefile()->IssueMessage(
521
        MessageType::AUTHOR_WARNING,
522
        "Directory property VS_STARTUP_PROJECT specifies target "
523 524
        "'" +
          startup + "' that does not exist.  Ignoring.");
525
    }
526
  }
527 528 529 530 531

  // default, if not specified
  return this->GetAllTargetName();
}

532
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
533 534
                                        const std::string& regKeyBase,
                                        std::string& nextAvailableSubKeyName)
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
{
  bool macrosRegistered = false;

  std::string s1;
  std::string s2;

  // Make lowercase local copies, convert to Unix slashes, and
  // see if the resulting strings are the same:
  s1 = cmSystemTools::LowerCase(macrosFile);
  cmSystemTools::ConvertToUnixSlashes(s1);

  std::string keyname;
  HKEY hkey = NULL;
  LONG result = ERROR_SUCCESS;
  DWORD index = 0;

551
  keyname = regKeyBase + "\\OtherProjects7";
552
  hkey = NULL;
553 554 555 556
  result =
    RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
                  0, KEY_READ, &hkey);
  if (ERROR_SUCCESS == result) {
557
    // Iterate the subkeys and look for the values of interest in each subkey:
558
    wchar_t subkeyname[256];
559
    DWORD cch_subkeyname = sizeof(subkeyname) * sizeof(subkeyname[0]);
560
    wchar_t keyclass[256];
561
    DWORD cch_keyclass = sizeof(keyclass) * sizeof(keyclass[0]);
562 563 564 565
    FILETIME lastWriteTime;
    lastWriteTime.dwHighDateTime = 0;
    lastWriteTime.dwLowDateTime = 0;

566 567 568
    while (ERROR_SUCCESS ==
           RegEnumKeyExW(hkey, index, subkeyname, &cch_subkeyname, 0, keyclass,
                         &cch_keyclass, &lastWriteTime)) {
569 570
      // Open the subkey and query the values of interest:
      HKEY hsubkey = NULL;
571
      result = RegOpenKeyExW(hkey, subkeyname, 0, KEY_READ, &hsubkey);
572
      if (ERROR_SUCCESS == result) {
573
        DWORD valueType = REG_SZ;
574
        wchar_t data1[256];
575 576 577
        DWORD cch_data1 = sizeof(data1) * sizeof(data1[0]);
        RegQueryValueExW(hsubkey, L"Path", 0, &valueType, (LPBYTE)&data1[0],
                         &cch_data1);
578 579 580

        DWORD data2 = 0;
        DWORD cch_data2 = sizeof(data2);
581 582
        RegQueryValueExW(hsubkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                         &cch_data2);
583 584 585

        DWORD data3 = 0;
        DWORD cch_data3 = sizeof(data3);
586
        RegQueryValueExW(hsubkey, L"StorageFormat", 0, &valueType,
587
                         (LPBYTE)&data3, &cch_data3);
588

589
        s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
590
        cmSystemTools::ConvertToUnixSlashes(s2);
591
        if (s2 == s1) {
592
          macrosRegistered = true;
593
        }
594

595
        std::string fullname = cmsys::Encoding::ToNarrow(data1);
596 597 598 599
        std::string filename;
        std::string filepath;
        std::string filepathname;
        std::string filepathpath;
600
        if (cmSystemTools::FileExists(fullname.c_str())) {
601 602 603 604
          filename = cmSystemTools::GetFilenameName(fullname);
          filepath = cmSystemTools::GetFilenamePath(fullname);
          filepathname = cmSystemTools::GetFilenameName(filepath);
          filepathpath = cmSystemTools::GetFilenamePath(filepath);
605
        }
606

607 608 609 610 611 612 613 614 615
        // std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
        // std::cout << "  Path: " << data1 << std::endl;
        // std::cout << "  Security: " << data2 << std::endl;
        // std::cout << "  StorageFormat: " << data3 << std::endl;
        // std::cout << "  filename: " << filename << std::endl;
        // std::cout << "  filepath: " << filepath << std::endl;
        // std::cout << "  filepathname: " << filepathname << std::endl;
        // std::cout << "  filepathpath: " << filepathpath << std::endl;
        // std::cout << std::endl;
616 617

        RegCloseKey(hsubkey);
618
      } else {
619 620
        std::cout << "error opening subkey: " << subkeyname << std::endl;
        std::cout << std::endl;
621
      }
622 623

      ++index;
624 625
      cch_subkeyname = sizeof(subkeyname) * sizeof(subkeyname[0]);
      cch_keyclass = sizeof(keyclass) * sizeof(keyclass[0]);
626 627
      lastWriteTime.dwHighDateTime = 0;
      lastWriteTime.dwLowDateTime = 0;
628
    }
629 630

    RegCloseKey(hkey);
631
  } else {
632 633
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
634
  }
635 636 637 638 639 640 641 642 643

  // Pass back next available sub key name, assuming sub keys always
  // follow the expected naming scheme. Expected naming scheme is that
  // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
  // as the name of the next subkey.
  std::ostringstream ossNext;
  ossNext << index;
  nextAvailableSubKeyName = ossNext.str();

644
  keyname = regKeyBase + "\\RecordingProject7";
645
  hkey = NULL;
646 647 648 649
  result =
    RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
                  0, KEY_READ, &hkey);
  if (ERROR_SUCCESS == result) {
650
    DWORD valueType = REG_SZ;
651
    wchar_t data1[256];
652 653 654
    DWORD cch_data1 = sizeof(data1) * sizeof(data1[0]);
    RegQueryValueExW(hkey, L"Path", 0, &valueType, (LPBYTE)&data1[0],
                     &cch_data1);
655 656 657

    DWORD data2 = 0;
    DWORD cch_data2 = sizeof(data2);
658 659
    RegQueryValueExW(hkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                     &cch_data2);
660 661 662

    DWORD data3 = 0;
    DWORD cch_data3 = sizeof(data3);
663 664
    RegQueryValueExW(hkey, L"StorageFormat", 0, &valueType, (LPBYTE)&data3,
                     &cch_data3);
665

666
    s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
667
    cmSystemTools::ConvertToUnixSlashes(s2);
668
    if (s2 == s1) {
669
      macrosRegistered = true;
670
    }
671

672 673 674 675 676
    // std::cout << keyname << ":" << std::endl;
    // std::cout << "  Path: " << data1 << std::endl;
    // std::cout << "  Security: " << data2 << std::endl;
    // std::cout << "  StorageFormat: " << data3 << std::endl;
    // std::cout << std::endl;
677 678

    RegCloseKey(hkey);
679
  } else {
680 681
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
682
  }
683 684 685 686

  return macrosRegistered;
}

687 688 689
void WriteVSMacrosFileRegistryEntry(const std::string& nextAvailableSubKeyName,
                                    const std::string& macrosFile,
                                    const std::string& regKeyBase)
690
{
691
  std::string keyname = regKeyBase + "\\OtherProjects7";
692
  HKEY hkey = NULL;
693 694 695 696
  LONG result =
    RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
                  0, KEY_READ | KEY_WRITE, &hkey);
  if (ERROR_SUCCESS == result) {
697 698
    // Create the subkey and set the values of interest:
    HKEY hsubkey = NULL;
699
    wchar_t lpClass[] = L"";
700 701 702 703
    result = RegCreateKeyExW(
      hkey, cmsys::Encoding::ToWide(nextAvailableSubKeyName).c_str(), 0,
      lpClass, 0, KEY_READ | KEY_WRITE, 0, &hsubkey, 0);
    if (ERROR_SUCCESS == result) {
704 705 706
      DWORD dw = 0;

      std::string s(macrosFile);
707
      std::replace(s.begin(), s.end(), '/', '\\');
708
      std::wstring ws = cmsys::Encoding::ToWide(s);
709

710 711 712 713
      result =
        RegSetValueExW(hsubkey, L"Path", 0, REG_SZ, (LPBYTE)ws.c_str(),
                       static_cast<DWORD>(ws.size() + 1) * sizeof(wchar_t));
      if (ERROR_SUCCESS != result) {
714 715
        std::cout << "error result 1: " << result << std::endl;
        std::cout << std::endl;
716
      }
717 718 719 720

      // Security value is always "1" for sample macros files (seems to be "2"
      // if you put the file somewhere outside the standard VSMacros folder)
      dw = 1;
721 722 723
      result = RegSetValueExW(hsubkey, L"Security", 0, REG_DWORD, (LPBYTE)&dw,
                              sizeof(DWORD));
      if (ERROR_SUCCESS != result) {
724 725
        std::cout << "error result 2: " << result << std::endl;
        std::cout << std::endl;
726
      }
727 728 729

      // StorageFormat value is always "0" for sample macros files
      dw = 0;
730 731 732
      result = RegSetValueExW(hsubkey, L"StorageFormat", 0, REG_DWORD,
                              (LPBYTE)&dw, sizeof(DWORD));
      if (ERROR_SUCCESS != result) {
733 734
        std::cout << "error result 3: " << result << std::endl;
        std::cout << std::endl;
735
      }
736 737

      RegCloseKey(hsubkey);
738 739 740
    } else {
      std::cout << "error creating subkey: " << nextAvailableSubKeyName
                << std::endl;
741 742
      std::cout << std::endl;
    }
743 744
    RegCloseKey(hkey);
  } else {
745 746
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
747
  }
748 749
}

750
void RegisterVisualStudioMacros(const std::string& macrosFile,
751
                                const std::string& regKeyBase)
752 753 754 755
{
  bool macrosRegistered;
  std::string nextAvailableSubKeyName;

756 757
  macrosRegistered = IsVisualStudioMacrosFileRegistered(
    macrosFile, regKeyBase, nextAvailableSubKeyName);
758

759 760 761
  if (!macrosRegistered) {
    int count =
      cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
762 763 764 765 766

    // Only register the macros file if there are *no* instances of Visual
    // Studio running. If we register it while one is running, first, it has
    // no effect on the running instance; second, and worse, Visual Studio
    // removes our newly added registration entry when it quits. Instead,
767 768
    // emit a warning asking the user to exit all running Visual Studio
    // instances...
769
    //
770
    if (0 != count) {
771
      std::ostringstream oss;
772
      oss << "Could not register CMake's Visual Studio macros file '"
773 774 775 776 777 778 779
          << CMAKE_VSMACROS_FILENAME "' while Visual Studio is running."
          << " Please exit all running instances of Visual Studio before"
          << " continuing." << std::endl
          << std::endl
          << "CMake needs to register Visual Studio macros when its macros"
          << " file is updated or when it detects that its current macros file"
          << " is no longer registered with Visual Studio." << std::endl;
780
      cmSystemTools::Message(oss.str(), "Warning");
781 782 783 784 785 786

      // Count them again now that the warning is over. In the case of a GUI
      // warning, the user may have gone to close Visual Studio and then come
      // back to the CMake GUI and clicked ok on the above warning. If so,
      // then register the macros *now* if the count is *now* 0...
      //
787 788
      count = cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
        "ALL");
789 790 791 792

      // Also re-get the nextAvailableSubKeyName in case Visual Studio
      // wrote out new registered macros information as it was exiting:
      //
793
      if (0 == count) {
794
        IsVisualStudioMacrosFileRegistered(macrosFile, regKeyBase,
795
                                           nextAvailableSubKeyName);
796
      }
797
    }
798 799 800

    // Do another if check - 'count' may have changed inside the above if:
    //
801
    if (0 == count) {
802
      WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName, macrosFile,
803
                                     regKeyBase);
804
    }
805
  }
806
}
807 808
bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
  cmGeneratorTarget const* gt)
809 810
{
  // check to see if this is a fortran build
811
  {
812 813 814 815
    // Issue diagnostic if the source files depend on the config.
    std::vector<cmSourceFile*> sources;
    if (!gt->GetConfigCommonSourceFiles(sources)) {
      return false;
816 817
    }
  }
818

819 820
  // If there's only one source language, Fortran has to be used
  // in order for the sources to compile.
821
  std::set<std::string> languages;
822
  gt->GetLanguages(languages, "");
823 824 825 826 827 828 829 830 831
  // Consider an explicit linker language property, but *not* the
  // computed linker language that may depend on linked targets.
  // This allows the project to control the language choice in
  // a target with none of its own sources, e.g. when also using
  // object libraries.
  const char* linkLang = gt->GetProperty("LINKER_LANGUAGE");
  if (linkLang && *linkLang) {
    languages.insert(linkLang);
  }
832 833 834 835

  // Intel Fortran .vfproj files do support the resource compiler.
  languages.erase("RC");

836
  return languages.size() == 1 && *languages.begin() == "Fortran";
837 838
}

839 840
bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
  cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
841
{
842 843 844
  // Make sure a given named target is ordered first,
  // e.g. to set ALL_BUILD as the default active project.
  // When the empty string is named this is a no-op.
845
  if (r->GetName() == this->First) {
846
    return false;
847 848
  }
  if (l->GetName() == this->First) {
849
    return true;
850
  }
851
  return l->GetName() < r->GetName();
852 853
}

854 855 856
cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  TargetDependSet const& targets, std::string const& first)
  : derived(TargetCompare(first))
857
{
858
  this->insert(targets.begin(), targets.end());
859
}
860

861 862 863
cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  TargetSet const& targets, std::string const& first)
  : derived(TargetCompare(first))
864
{
865 866
  for (cmGeneratorTarget const* it : targets) {
    this->insert(it);
867
  }
868
}
869 870

std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
871
  const std::string& str, const std::string& config) const
872 873 874 875
{
  std::string replace = GetCMakeCFGIntDir();

  std::string tmp = str;
876 877
  for (std::string::size_type i = tmp.find(replace); i != std::string::npos;
       i = tmp.find(replace, i)) {
878 879
    tmp.replace(i, replace.size(), config);
    i += config.size();
880
  }
881 882
  return tmp;
}
883 884 885 886 887

void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
  cmGeneratorTarget* gt, std::vector<cmCustomCommand>& commands,
  std::string const& configName)
{
888 889
  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
    gt->GetModuleDefinitionInfo(configName);
890
  if (!mdi || !mdi->DefFileGenerated) {
891 892 893
    return;
  }

894
  std::vector<std::string> outputs;
895
  outputs.push_back(mdi->DefFile);
896 897 898 899
  std::vector<std::string> empty;
  std::vector<cmSourceFile const*> objectSources;
  gt->GetObjectSources(objectSources, configName);
  std::map<cmSourceFile const*, std::string> mapping;
900 901
  for (cmSourceFile const* it : objectSources) {
    mapping[it];
902 903
  }
  gt->LocalGenerator->ComputeObjectFilenames(mapping, gt);
904 905 906 907 908 909 910
  std::string obj_dir = gt->ObjectDirectory;
  std::string cmakeCommand = cmSystemTools::GetCMakeCommand();
  cmSystemTools::ConvertToWindowsExtendedPath(cmakeCommand);
  cmCustomCommandLine cmdl;
  cmdl.push_back(cmakeCommand);
  cmdl.push_back("-E");
  cmdl.push_back("__create_def");
911
  cmdl.push_back(mdi->DefFile);
912
  std::string obj_dir_expanded = obj_dir;
913
  cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(),
914
                               configName.c_str());
915 916
  cmSystemTools::MakeDirectory(obj_dir_expanded);
  std::string const objs_file = obj_dir_expanded + "/objects.txt";
917 918
  cmdl.push_back(objs_file);
  cmGeneratedFileStream fout(objs_file.c_str());
919
  if (!fout) {
920
    cmSystemTools::Error("could not open " + objs_file);
921
    return;
922
  }
923

924 925
  if (mdi->WindowsExportAllSymbols) {
    std::vector<std::string> objs;
926
    for (cmSourceFile const* it : objectSources) {
927 928
      // Find the object file name corresponding to this source file.
      // It must exist because we populated the mapping just above.
929 930 931
      const auto& v = mapping[it];
      assert(!v.empty());
      std::string objFile = obj_dir + v;
932 933 934 935
      objs.push_back(objFile);
    }
    std::vector<cmSourceFile const*> externalObjectSources;
    gt->GetExternalObjects(externalObjectSources, configName);
936 937
    for (cmSourceFile const* it : externalObjectSources) {
      objs.push_back(it->GetFullPath());