cmGlobalVisualStudioGenerator.cxx 33.4 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 107 108 109 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
void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout)
{
  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;
154 155 156 157 158 159 160 161 162
    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;
163 164 165
  }
}

166
std::string cmGlobalVisualStudioGenerator::GetRegistryBase()
167
{
168
  return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
169 170
}

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

177
void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
178 179 180 181 182 183
{
  // 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;
184 185
  for (auto const& it : this->ProjectMap) {
    std::vector<cmLocalGenerator*> const& gen = it.second;
186
    // add the ALL_BUILD to the first local generator of each project
187
    if (!gen.empty()) {
188 189
      // Use no actual command lines so that the target itself is not
      // considered always out of date.
190
      cmTarget* allBuild = gen[0]->GetMakefile()->AddUtilityCommand(
191 192
        "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, no_working_dir,
        no_depends, no_commands, false, "Build all projects");
193

194
      cmGeneratorTarget* gt = new cmGeneratorTarget(allBuild, gen[0]);
195
      gen[0]->AddGeneratorTarget(gt);
196 197 198 199

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

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

219 220 221
  // Configure CMake Visual Studio macros, for this user on this version
  // of Visual Studio.
  this->ConfigureCMakeVisualStudioMacros();
222 223
}

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

242
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
243 244
                                        const std::string& regKeyBase,
                                        std::string& nextAvailableSubKeyName);
245

246
void RegisterVisualStudioMacros(const std::string& macrosFile,
247
                                const std::string& regKeyBase);
248

249
#define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
250

251
#define CMAKE_VSMACROS_RELOAD_MACRONAME                                       \
252
  "Macros.CMakeVSMacros2.Macros.ReloadProjects"
253

254
#define CMAKE_VSMACROS_STOP_MACRONAME "Macros.CMakeVSMacros2.Macros.StopBuild"
255

256 257 258 259
void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
{
  std::string dir = this->GetUserMacrosDirectory();

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

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

266 267 268 269 270 271
    // 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;
272 273
    if (!cmSystemTools::FileTimeCompare(src, dst, &res) || res > 0) {
      if (!cmSystemTools::CopyFileAlways(src, dst)) {
274 275 276
        std::ostringstream oss;
        oss << "Could not copy from: " << src << std::endl;
        oss << "                 to: " << dst << std::endl;
277
        cmSystemTools::Message(oss.str(), "Warning");
278
      }
279
    }
280

281
    RegisterVisualStudioMacros(dst, this->GetUserMacrosRegKeyBase());
282
  }
283 284
}

285 286
void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
  MacroName m, const char* vsSolutionFile)
287 288 289 290 291 292
{
  // 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();

293 294 295 296 297 298
  // 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
  //
299
  if (!dir.empty()) {
300 301 302
    std::string macrosFile = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
    std::string nextSubkeyName;
    if (cmSystemTools::FileExists(macrosFile.c_str()) &&
303 304
        IsVisualStudioMacrosFileRegistered(
          macrosFile, this->GetUserMacrosRegKeyBase(), nextSubkeyName)) {
305
      std::string topLevelSlnName;
306
      if (vsSolutionFile) {
307
        topLevelSlnName = vsSolutionFile;
308
      } else {
309
        topLevelSlnName = mf->GetCurrentBinaryDirectory();
310
        topLevelSlnName += "/";
311
        topLevelSlnName += this->LocalGenerators[0]->GetProjectName();
312
        topLevelSlnName += ".sln";
313
      }
314

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

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

338 339 340 341 342
std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
{
  return "";
}

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

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

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

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

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

407
void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(
408
  cmGeneratorTarget* target)
409
{
410
  if (this->VSTargetDepends.find(target) != this->VSTargetDepends.end()) {
411
    return;
412
  }
413
  VSDependSet& vsTargetDepend = this->VSTargetDepends[target];
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
  // 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.

433 434 435 436
  bool allowLinkable = (target->GetType() != cmStateEnums::STATIC_LIBRARY &&
                        target->GetType() != cmStateEnums::SHARED_LIBRARY &&
                        target->GetType() != cmStateEnums::MODULE_LIBRARY &&
                        target->GetType() != cmStateEnums::EXECUTABLE);
437

438
  TargetDependSet const& depends = this->GetTargetDirectDepends(target);
439 440 441 442

  // 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.
443
  std::set<cmGeneratorTarget const*> linkDepends;
444
  if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
445
    for (cmTargetDepend const& di : depends) {
446
      if (di.IsLink()) {
447
        this->FollowLinkDepends(di, linkDepends);
448 449
      }
    }
450
  }
451

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

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

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

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

485
bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf)
486 487 488 489
{
  // 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.
490
  if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
491
    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram());
492
  }
493
  return true;
494 495
}

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

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

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

529
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
530 531
                                        const std::string& regKeyBase,
                                        std::string& nextAvailableSubKeyName)
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
{
  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;

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

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

        DWORD data2 = 0;
        DWORD cch_data2 = sizeof(data2);
578 579
        RegQueryValueExW(hsubkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                         &cch_data2);
580 581 582

        DWORD data3 = 0;
        DWORD cch_data3 = sizeof(data3);
583
        RegQueryValueExW(hsubkey, L"StorageFormat", 0, &valueType,
584
                         (LPBYTE)&data3, &cch_data3);
585

586
        s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
587
        cmSystemTools::ConvertToUnixSlashes(s2);
588
        if (s2 == s1) {
589
          macrosRegistered = true;
590
        }
591

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

604 605 606 607 608 609 610 611 612
        // 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;
613 614

        RegCloseKey(hsubkey);
615
      } else {
616 617
        std::cout << "error opening subkey: " << subkeyname << std::endl;
        std::cout << std::endl;
618
      }
619 620

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

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

  // 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();

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

    DWORD data2 = 0;
    DWORD cch_data2 = sizeof(data2);
655 656
    RegQueryValueExW(hkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                     &cch_data2);
657 658 659

    DWORD data3 = 0;
    DWORD cch_data3 = sizeof(data3);
660 661
    RegQueryValueExW(hkey, L"StorageFormat", 0, &valueType, (LPBYTE)&data3,
                     &cch_data3);
662

663
    s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
664
    cmSystemTools::ConvertToUnixSlashes(s2);
665
    if (s2 == s1) {
666
      macrosRegistered = true;
667
    }
668

669 670 671 672 673
    // 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;
674 675

    RegCloseKey(hkey);
676
  } else {
677 678
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
679
  }
680 681 682 683

  return macrosRegistered;
}

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

      std::string s(macrosFile);
704
      std::replace(s.begin(), s.end(), '/', '\\');
705
      std::wstring ws = cmsys::Encoding::ToWide(s);
706

707 708 709 710
      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) {
711 712
        std::cout << "error result 1: " << result << std::endl;
        std::cout << std::endl;
713
      }
714 715 716 717

      // 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;
718 719 720
      result = RegSetValueExW(hsubkey, L"Security", 0, REG_DWORD, (LPBYTE)&dw,
                              sizeof(DWORD));
      if (ERROR_SUCCESS != result) {
721 722
        std::cout << "error result 2: " << result << std::endl;
        std::cout << std::endl;
723
      }
724 725 726

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

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

747
void RegisterVisualStudioMacros(const std::string& macrosFile,
748
                                const std::string& regKeyBase)
749 750 751 752
{
  bool macrosRegistered;
  std::string nextAvailableSubKeyName;

753 754
  macrosRegistered = IsVisualStudioMacrosFileRegistered(
    macrosFile, regKeyBase, nextAvailableSubKeyName);
755

756 757 758
  if (!macrosRegistered) {
    int count =
      cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
759 760 761 762 763

    // 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,
764 765
    // emit a warning asking the user to exit all running Visual Studio
    // instances...
766
    //
767
    if (0 != count) {
768
      std::ostringstream oss;
769
      oss << "Could not register CMake's Visual Studio macros file '"
770 771 772 773 774 775 776
          << 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;
777
      cmSystemTools::Message(oss.str(), "Warning");
778 779 780 781 782 783

      // 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...
      //
784 785
      count = cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
        "ALL");
786 787 788 789

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

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

816 817
  // If there's only one source language, Fortran has to be used
  // in order for the sources to compile.
818
  std::set<std::string> languages;
819
  gt->GetLanguages(languages, "");
820 821 822 823 824 825 826 827 828
  // 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);
  }
829 830 831 832

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

833
  return languages.size() == 1 && *languages.begin() == "Fortran";
834 835
}

836 837
bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
  cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
838
{
Brad King's avatar
Brad King committed
839 840 841
  // 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.
842
  if (r->GetName() == this->First) {
843
    return false;
844 845
  }
  if (l->GetName() == this->First) {
846
    return true;
847
  }
Brad King's avatar
Brad King committed
848
  return l->GetName() < r->GetName();
849 850
}

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

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

std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
868
  const std::string& str, const std::string& config) const
869 870 871 872
{
  std::string replace = GetCMakeCFGIntDir();

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

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

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

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