cmGlobalVisualStudioGenerator.cxx 33.7 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 60 61 62 63
void cmGlobalVisualStudioGenerator::EnableLanguage(
  std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
{
  mf->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT",
                    this->DefaultPlatformName.c_str());
  this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
}

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
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");
  }
  mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName().c_str());
  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 206 207 208
      for (cmLocalGenerator const* i : gen) {
        std::vector<cmGeneratorTarget*> const& targets =
          i->GetGeneratorTargets();
        for (cmGeneratorTarget* tgt : targets) {
209 210
          if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
              tgt->IsImported()) {
211
            continue;
212
          }
213
          if (!this->IsExcluded(tgt)) {
214
            allBuild->AddUtility(tgt->GetName());
215 216
          }
        }
217 218
      }
    }
219
  }
220

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

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

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

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

251
#define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
252

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  // 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.
448
  std::set<cmGeneratorTarget const*> linkDepends;
449
  if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
450 451
    for (cmTargetDepend const& di : depends) {
      cmTargetDepend dep = di;
452
      if (dep.IsLink()) {
453
        this->FollowLinkDepends(di, linkDepends);
454 455
      }
    }
456
  }
457

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

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

  // Emit link dependencies.
475
  for (cmGeneratorTarget const* dep : linkDepends) {
476
    vsTargetDepend.insert(dep->GetName());
477
  }
478 479

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

492
bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf)
493 494 495 496
{
  // 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.
497 498 499
  if (cmSystemTools::IsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram().c_str());
  }
500
  return true;
501 502
}

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

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

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

536
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
537 538
                                        const std::string& regKeyBase,
                                        std::string& nextAvailableSubKeyName)
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
{
  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;

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

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

        DWORD data2 = 0;
        DWORD cch_data2 = sizeof(data2);
585 586
        RegQueryValueExW(hsubkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                         &cch_data2);
587 588 589

        DWORD data3 = 0;
        DWORD cch_data3 = sizeof(data3);
590
        RegQueryValueExW(hsubkey, L"StorageFormat", 0, &valueType,
591
                         (LPBYTE)&data3, &cch_data3);
592

593
        s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
594
        cmSystemTools::ConvertToUnixSlashes(s2);
595
        if (s2 == s1) {
596
          macrosRegistered = true;
597
        }
598

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

611 612 613 614 615 616 617 618 619
        // 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;
620 621

        RegCloseKey(hsubkey);
622
      } else {
623 624
        std::cout << "error opening subkey: " << subkeyname << std::endl;
        std::cout << std::endl;
625
      }
626 627

      ++index;
628 629
      cch_subkeyname = sizeof(subkeyname) * sizeof(subkeyname[0]);
      cch_keyclass = sizeof(keyclass) * sizeof(keyclass[0]);
630 631
      lastWriteTime.dwHighDateTime = 0;
      lastWriteTime.dwLowDateTime = 0;
632
    }
633 634

    RegCloseKey(hkey);
635
  } else {
636 637
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
638
  }
639 640 641 642 643 644 645 646 647

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

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

    DWORD data2 = 0;
    DWORD cch_data2 = sizeof(data2);
662 663
    RegQueryValueExW(hkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                     &cch_data2);
664 665 666

    DWORD data3 = 0;
    DWORD cch_data3 = sizeof(data3);
667 668
    RegQueryValueExW(hkey, L"StorageFormat", 0, &valueType, (LPBYTE)&data3,
                     &cch_data3);
669

670
    s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
671
    cmSystemTools::ConvertToUnixSlashes(s2);
672
    if (s2 == s1) {
673
      macrosRegistered = true;
674
    }
675

676 677 678 679 680
    // 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;
681 682

    RegCloseKey(hkey);
683
  } else {
684 685
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
686
  }
687 688 689 690

  return macrosRegistered;
}

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

      std::string s(macrosFile);
711
      std::replace(s.begin(), s.end(), '/', '\\');
712
      std::wstring ws = cmsys::Encoding::ToWide(s);
713

714 715 716 717
      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) {
718 719
        std::cout << "error result 1: " << result << std::endl;
        std::cout << std::endl;
720
      }
721 722 723 724

      // 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;
725 726 727
      result = RegSetValueExW(hsubkey, L"Security", 0, REG_DWORD, (LPBYTE)&dw,
                              sizeof(DWORD));
      if (ERROR_SUCCESS != result) {
728 729
        std::cout << "error result 2: " << result << std::endl;
        std::cout << std::endl;
730
      }
731 732 733

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

      RegCloseKey(hsubkey);
742 743 744
    } else {
      std::cout << "error creating subkey: " << nextAvailableSubKeyName
                << std::endl;
745 746
      std::cout << std::endl;
    }
747 748
    RegCloseKey(hkey);
  } else {
749 750
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
751
  }
752 753
}

754
void RegisterVisualStudioMacros(const std::string& macrosFile,
755
                                const std::string& regKeyBase)
756 757 758 759
{
  bool macrosRegistered;
  std::string nextAvailableSubKeyName;

760 761
  macrosRegistered = IsVisualStudioMacrosFileRegistered(
    macrosFile, regKeyBase, nextAvailableSubKeyName);
762

763 764 765
  if (!macrosRegistered) {
    int count =
      cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
766 767 768 769 770

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

      // 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...
      //
791 792
      count = cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
        "ALL");
793 794 795 796

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

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

823 824
  // If there's only one source language, Fortran has to be used
  // in order for the sources to compile.
825
  std::set<std::string> languages;
826
  gt->GetLanguages(languages, "");
827 828 829 830 831 832 833 834 835
  // 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);
  }
836 837 838 839

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

840
  return languages.size() == 1 && *languages.begin() == "Fortran";
841 842
}

843 844
bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
  cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
845
{
Brad King's avatar
Brad King committed
846 847 848
  // 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.
849
  if (r->GetName() == this->First) {
850
    return false;
851 852
  }
  if (l->GetName() == this->First) {
853
    return true;
854
  }
Brad King's avatar
Brad King committed
855
  return l->GetName() < r->GetName();
856 857
}

858 859 860
cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  TargetDependSet const& targets, std::string const& first)
  : derived(TargetCompare(first))
861
{
862
  this->insert(targets.begin(), targets.end());
863
}
864

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

std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
875
  const std::string& str, const std::string& config) const
876 877 878 879
{
  std::string replace = GetCMakeCFGIntDir();

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

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

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

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

944 945
    for (std::string const& it : objs) {
      std::string objFile = it;
946 947 948 949 950 951
      // replace $(ConfigurationName) in the object names
      cmSystemTools::ReplaceString(objFile, this->GetCMakeCFGIntDir(),
                                   configName.c_str());
      if (cmHasLiteralSuffix(objFile, ".obj")) {
        fout << objFile << "\n";
      }
952
    }
953
  }
954