cmGlobalVisualStudioGenerator.cxx 31.8 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 7
#include <cmsys/Encoding.hxx>
#include <iostream>
8
#include <windows.h>
9

10
#include "cmAlgorithms.h"
11
#include "cmCallVisualStudioMacro.h"
12
#include "cmGeneratedFileStream.h"
13 14
#include "cmGeneratorTarget.h"
#include "cmLocalVisualStudioGenerator.h"
15
#include "cmMakefile.h"
16
#include "cmSourceFile.h"
17
#include "cmState.h"
18 19
#include "cmTarget.h"

20 21
cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(cmake* cm)
  : cmGlobalGenerator(cm)
22
{
23
  cm->GetState()->SetIsGeneratorMultiConfig(true);
24 25
  cm->GetState()->SetWindowsShell(true);
  cm->GetState()->SetWindowsVSIDE(true);
26 27 28 29 30
}

cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator()
{
}
31

32 33 34 35 36 37 38 39 40 41 42
cmGlobalVisualStudioGenerator::VSVersion
cmGlobalVisualStudioGenerator::GetVersion() const
{
  return this->Version;
}

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

43
std::string cmGlobalVisualStudioGenerator::GetRegistryBase()
44
{
45
  return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
46 47
}

48
std::string cmGlobalVisualStudioGenerator::GetRegistryBase(const char* version)
49 50
{
  std::string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\";
51
  return key + version;
52 53
}

54
void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
55 56 57 58 59 60
{
  // 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;
61
  std::map<std::string, std::vector<cmLocalGenerator*> >::iterator it;
62
  for (it = this->ProjectMap.begin(); it != this->ProjectMap.end(); ++it) {
63 64
    std::vector<cmLocalGenerator*>& gen = it->second;
    // add the ALL_BUILD to the first local generator of each project
65
    if (!gen.empty()) {
66 67
      // Use no actual command lines so that the target itself is not
      // considered always out of date.
68 69 70
      cmTarget* allBuild = gen[0]->GetMakefile()->AddUtilityCommand(
        "ALL_BUILD", true, no_working_dir, no_depends, no_commands, false,
        "Build all projects");
71

72
      cmGeneratorTarget* gt = new cmGeneratorTarget(allBuild, gen[0]);
73
      gen[0]->AddGeneratorTarget(gt);
74 75 76 77

      //
      // Organize in the "predefined targets" folder:
      //
78
      if (this->UseFolderProperty()) {
79
        allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
80
      }
81

82
      // Now make all targets depend on the ALL_BUILD target
83 84 85 86 87
      for (std::vector<cmLocalGenerator*>::iterator i = gen.begin();
           i != gen.end(); ++i) {
        std::vector<cmGeneratorTarget*> targets = (*i)->GetGeneratorTargets();
        for (std::vector<cmGeneratorTarget*>::iterator t = targets.begin();
             t != targets.end(); ++t) {
88
          cmGeneratorTarget* tgt = *t;
89 90
          if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
              tgt->IsImported()) {
91
            continue;
92 93
          }
          if (!this->IsExcluded(gen[0], tgt)) {
94
            allBuild->AddUtility(tgt->GetName());
95 96
          }
        }
97 98
      }
    }
99
  }
100

101 102 103 104
  // Configure CMake Visual Studio macros, for this user on this version
  // of Visual Studio.
  this->ConfigureCMakeVisualStudioMacros();

105
  // Add CMakeLists.txt with custom command to rerun CMake.
106 107 108
  for (std::vector<cmLocalGenerator*>::const_iterator lgi =
         this->LocalGenerators.begin();
       lgi != this->LocalGenerators.end(); ++lgi) {
109 110 111
    cmLocalVisualStudioGenerator* lg =
      static_cast<cmLocalVisualStudioGenerator*>(*lgi);
    lg->AddCMakeListsRules();
112
  }
113 114
}

115 116
void cmGlobalVisualStudioGenerator::ComputeTargetObjectDirectory(
  cmGeneratorTarget* gt) const
117
{
118
  std::string dir = gt->LocalGenerator->GetCurrentBinaryDirectory();
119
  dir += "/";
120
  std::string tgtDir = gt->LocalGenerator->GetTargetDirectory(gt);
121
  if (!tgtDir.empty()) {
122 123
    dir += tgtDir;
    dir += "/";
124
  }
125
  const char* cd = this->GetCMakeCFGIntDir();
126
  if (cd && *cd) {
127 128
    dir += cd;
    dir += "/";
129
  }
130
  gt->ObjectDirectory = dir;
131 132
}

133
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
134 135
                                        const std::string& regKeyBase,
                                        std::string& nextAvailableSubKeyName);
136

137
void RegisterVisualStudioMacros(const std::string& macrosFile,
138
                                const std::string& regKeyBase);
139

140
#define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
141

142
#define CMAKE_VSMACROS_RELOAD_MACRONAME                                       \
143
  "Macros.CMakeVSMacros2.Macros.ReloadProjects"
144

145
#define CMAKE_VSMACROS_STOP_MACRONAME "Macros.CMakeVSMacros2.Macros.StopBuild"
146

147 148 149 150
void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
{
  std::string dir = this->GetUserMacrosDirectory();

151
  if (dir != "") {
152
    std::string src = cmSystemTools::GetCMakeRoot();
153 154 155 156
    src += "/Templates/" CMAKE_VSMACROS_FILENAME;

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

157 158 159 160 161 162
    // 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;
163 164 165
    if (!cmSystemTools::FileTimeCompare(src.c_str(), dst.c_str(), &res) ||
        res > 0) {
      if (!cmSystemTools::CopyFileAlways(src.c_str(), dst.c_str())) {
166 167 168 169 170
        std::ostringstream oss;
        oss << "Could not copy from: " << src << std::endl;
        oss << "                 to: " << dst << std::endl;
        cmSystemTools::Message(oss.str().c_str(), "Warning");
      }
171
    }
172

173
    RegisterVisualStudioMacros(dst, this->GetUserMacrosRegKeyBase());
174
  }
175 176
}

177 178
void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
  MacroName m, const char* vsSolutionFile)
179 180 181 182 183 184
{
  // 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();

185 186 187 188 189 190
  // 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
  //
191
  if (dir != "") {
192 193 194
    std::string macrosFile = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
    std::string nextSubkeyName;
    if (cmSystemTools::FileExists(macrosFile.c_str()) &&
195 196
        IsVisualStudioMacrosFileRegistered(
          macrosFile, this->GetUserMacrosRegKeyBase(), nextSubkeyName)) {
197
      std::string topLevelSlnName;
198
      if (vsSolutionFile) {
199
        topLevelSlnName = vsSolutionFile;
200
      } else {
201
        topLevelSlnName = mf->GetCurrentBinaryDirectory();
202
        topLevelSlnName += "/";
203
        topLevelSlnName += this->LocalGenerators[0]->GetProjectName();
204
        topLevelSlnName += ".sln";
205
      }
206

207
      if (m == MacroReload) {
208 209
        std::vector<std::string> filenames;
        this->GetFilesReplacedDuringGenerate(filenames);
210
        if (!filenames.empty()) {
211 212 213
          // Convert vector to semi-colon delimited string of filenames:
          std::string projects;
          std::vector<std::string>::iterator it = filenames.begin();
214
          if (it != filenames.end()) {
215 216
            projects = *it;
            ++it;
217 218
          }
          for (; it != filenames.end(); ++it) {
219 220 221
            projects += ";";
            projects += *it;
          }
222 223 224
          cmCallVisualStudioMacro::CallMacro(
            topLevelSlnName, CMAKE_VSMACROS_RELOAD_MACRONAME, projects,
            this->GetCMakeInstance()->GetDebugOutput());
225
        }
226 227 228
      } else if (m == MacroStop) {
        cmCallVisualStudioMacro::CallMacro(
          topLevelSlnName, CMAKE_VSMACROS_STOP_MACRONAME, "",
229
          this->GetCMakeInstance()->GetDebugOutput());
230 231
      }
    }
232
  }
233 234 235 236 237 238 239
}

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

240 241 242 243 244
std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
{
  return "";
}

245
void cmGlobalVisualStudioGenerator::FillLinkClosure(
246
  const cmGeneratorTarget* target, TargetSet& linked)
247
{
248 249 250 251 252
  if (linked.insert(target).second) {
    TargetDependSet const& depends = this->GetTargetDirectDepends(target);
    for (TargetDependSet::const_iterator di = depends.begin();
         di != depends.end(); ++di) {
      if (di->IsLink()) {
253
        this->FillLinkClosure(*di, linked);
254 255
      }
    }
256
  }
257 258 259
}

cmGlobalVisualStudioGenerator::TargetSet const&
260
cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmGeneratorTarget* target)
261
{
262
  TargetSetMap::iterator i = this->TargetLinkClosure.find(target);
263
  if (i == this->TargetLinkClosure.end()) {
264
    TargetSetMap::value_type entry(target, TargetSet());
265 266
    i = this->TargetLinkClosure.insert(entry).first;
    this->FillLinkClosure(target, i->second);
267
  }
268 269 270 271
  return i->second;
}

void cmGlobalVisualStudioGenerator::FollowLinkDepends(
272
  const cmGeneratorTarget* target, std::set<const cmGeneratorTarget*>& linked)
273
{
274
  if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
275
    return;
276 277
  }
  if (linked.insert(target).second &&
278
      target->GetType() == cmStateEnums::STATIC_LIBRARY) {
279 280
    // Static library targets do not list their link dependencies so
    // we must follow them transitively now.
281
    TargetDependSet const& depends = this->GetTargetDirectDepends(target);
282 283 284
    for (TargetDependSet::const_iterator di = depends.begin();
         di != depends.end(); ++di) {
      if (di->IsLink()) {
285
        this->FollowLinkDepends(*di, linked);
286 287
      }
    }
288
  }
289 290
}

291 292
bool cmGlobalVisualStudioGenerator::ComputeTargetDepends()
{
293
  if (!this->cmGlobalGenerator::ComputeTargetDepends()) {
294
    return false;
295
  }
296
  std::map<std::string, std::vector<cmLocalGenerator*> >::iterator it;
297
  for (it = this->ProjectMap.begin(); it != this->ProjectMap.end(); ++it) {
298
    std::vector<cmLocalGenerator*>& gen = it->second;
299 300
    for (std::vector<cmLocalGenerator*>::iterator i = gen.begin();
         i != gen.end(); ++i) {
Stephen Kelly's avatar
Stephen Kelly committed
301
      std::vector<cmGeneratorTarget*> targets = (*i)->GetGeneratorTargets();
302 303
      for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();
           ti != targets.end(); ++ti) {
304
        this->ComputeVSTargetDepends(*ti);
305 306
      }
    }
307
  }
308 309 310
  return true;
}

311
static bool VSLinkable(cmGeneratorTarget const* t)
Brad King's avatar
Brad King committed
312
{
313
  return t->IsLinkable() || t->GetType() == cmStateEnums::OBJECT_LIBRARY;
Brad King's avatar
Brad King committed
314 315
}

316
void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(
317
  cmGeneratorTarget* target)
318
{
319
  if (this->VSTargetDepends.find(target) != this->VSTargetDepends.end()) {
320
    return;
321
  }
322
  VSDependSet& vsTargetDepend = this->VSTargetDepends[target];
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
  // 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.

342 343 344 345
  bool allowLinkable = (target->GetType() != cmStateEnums::STATIC_LIBRARY &&
                        target->GetType() != cmStateEnums::SHARED_LIBRARY &&
                        target->GetType() != cmStateEnums::MODULE_LIBRARY &&
                        target->GetType() != cmStateEnums::EXECUTABLE);
346

347
  TargetDependSet const& depends = this->GetTargetDirectDepends(target);
348 349 350 351

  // 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.
352
  std::set<cmGeneratorTarget const*> linkDepends;
353
  if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
354 355
    for (TargetDependSet::const_iterator di = depends.begin();
         di != depends.end(); ++di) {
356
      cmTargetDepend dep = *di;
357
      if (dep.IsLink()) {
358
        this->FollowLinkDepends(*di, linkDepends);
359 360
      }
    }
361
  }
362

363
  // Collect explicit util dependencies (add_dependencies).
364
  std::set<cmGeneratorTarget const*> utilDepends;
365 366
  for (TargetDependSet::const_iterator di = depends.begin();
       di != depends.end(); ++di) {
367
    cmTargetDepend dep = *di;
368
    if (dep.IsUtil()) {
369
      this->FollowLinkDepends(*di, utilDepends);
370
    }
371
  }
372

373 374 375
  // Collect all targets linked by this target so we can avoid
  // intermediate targets below.
  TargetSet linked;
376
  if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
377
    linked = this->GetTargetLinkClosure(target);
378
  }
379 380

  // Emit link dependencies.
381 382
  for (std::set<cmGeneratorTarget const*>::iterator di = linkDepends.begin();
       di != linkDepends.end(); ++di) {
383
    cmGeneratorTarget const* dep = *di;
384
    vsTargetDepend.insert(dep->GetName());
385
  }
386 387

  // Emit util dependencies.  Possibly use intermediate targets.
388 389
  for (std::set<cmGeneratorTarget const*>::iterator di = utilDepends.begin();
       di != utilDepends.end(); ++di) {
390
    cmGeneratorTarget const* dgt = *di;
391
    if (allowLinkable || !VSLinkable(dgt) || linked.count(dgt)) {
392
      // Direct dependency allowed.
393
      vsTargetDepend.insert(dgt->GetName());
394
    } else {
395 396
      // Direct dependency on linkable target not allowed.
      // Use an intermediate utility target.
397
      vsTargetDepend.insert(this->GetUtilityDepend(dgt));
398
    }
399
  }
400 401
}

402
bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf)
403 404 405 406
{
  // 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.
407 408 409
  if (cmSystemTools::IsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
    mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram().c_str());
  }
410
  return true;
411 412
}

413 414
std::string cmGlobalVisualStudioGenerator::GetUtilityDepend(
  cmGeneratorTarget const* target)
415 416
{
  UtilityDependsMap::iterator i = this->UtilityDepends.find(target);
417
  if (i == this->UtilityDepends.end()) {
418
    std::string name = this->WriteUtilityDepend(target);
419 420
    UtilityDependsMap::value_type entry(target, name);
    i = this->UtilityDepends.insert(entry).first;
421
  }
422 423 424
  return i->second;
}

425
std::string cmGlobalVisualStudioGenerator::GetStartupProjectName(
426 427 428
  cmLocalGenerator const* root) const
{
  const char* n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
429
  if (n && *n) {
430
    std::string startup = n;
431
    if (this->FindTarget(startup)) {
432
      return startup;
433
    } else {
434 435 436
      root->GetMakefile()->IssueMessage(
        cmake::AUTHOR_WARNING,
        "Directory property VS_STARTUP_PROJECT specifies target "
437 438
        "'" +
          startup + "' that does not exist.  Ignoring.");
439
    }
440
  }
441 442 443 444 445

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

446
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
447 448
                                        const std::string& regKeyBase,
                                        std::string& nextAvailableSubKeyName)
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
{
  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;

465
  keyname = regKeyBase + "\\OtherProjects7";
466
  hkey = NULL;
467 468 469 470
  result =
    RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
                  0, KEY_READ, &hkey);
  if (ERROR_SUCCESS == result) {
471
    // Iterate the subkeys and look for the values of interest in each subkey:
472
    wchar_t subkeyname[256];
473
    DWORD cch_subkeyname = sizeof(subkeyname) * sizeof(subkeyname[0]);
474
    wchar_t keyclass[256];
475
    DWORD cch_keyclass = sizeof(keyclass) * sizeof(keyclass[0]);
476 477 478 479
    FILETIME lastWriteTime;
    lastWriteTime.dwHighDateTime = 0;
    lastWriteTime.dwLowDateTime = 0;

480
    while (ERROR_SUCCESS == RegEnumKeyExW(hkey, index, subkeyname,
481 482
                                          &cch_subkeyname, 0, keyclass,
                                          &cch_keyclass, &lastWriteTime)) {
483 484
      // Open the subkey and query the values of interest:
      HKEY hsubkey = NULL;
485
      result = RegOpenKeyExW(hkey, subkeyname, 0, KEY_READ, &hsubkey);
486
      if (ERROR_SUCCESS == result) {
487
        DWORD valueType = REG_SZ;
488
        wchar_t data1[256];
489 490 491
        DWORD cch_data1 = sizeof(data1) * sizeof(data1[0]);
        RegQueryValueExW(hsubkey, L"Path", 0, &valueType, (LPBYTE)&data1[0],
                         &cch_data1);
492 493 494

        DWORD data2 = 0;
        DWORD cch_data2 = sizeof(data2);
495 496
        RegQueryValueExW(hsubkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                         &cch_data2);
497 498 499

        DWORD data3 = 0;
        DWORD cch_data3 = sizeof(data3);
500
        RegQueryValueExW(hsubkey, L"StorageFormat", 0, &valueType,
501
                         (LPBYTE)&data3, &cch_data3);
502

503
        s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
504
        cmSystemTools::ConvertToUnixSlashes(s2);
505
        if (s2 == s1) {
506
          macrosRegistered = true;
507
        }
508

509
        std::string fullname = cmsys::Encoding::ToNarrow(data1);
510 511 512 513
        std::string filename;
        std::string filepath;
        std::string filepathname;
        std::string filepathpath;
514
        if (cmSystemTools::FileExists(fullname.c_str())) {
515 516 517 518
          filename = cmSystemTools::GetFilenameName(fullname);
          filepath = cmSystemTools::GetFilenamePath(fullname);
          filepathname = cmSystemTools::GetFilenameName(filepath);
          filepathpath = cmSystemTools::GetFilenamePath(filepath);
519
        }
520

521 522 523 524 525 526 527 528 529
        // 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;
530 531

        RegCloseKey(hsubkey);
532
      } else {
533 534
        std::cout << "error opening subkey: " << subkeyname << std::endl;
        std::cout << std::endl;
535
      }
536 537

      ++index;
538 539
      cch_subkeyname = sizeof(subkeyname) * sizeof(subkeyname[0]);
      cch_keyclass = sizeof(keyclass) * sizeof(keyclass[0]);
540 541
      lastWriteTime.dwHighDateTime = 0;
      lastWriteTime.dwLowDateTime = 0;
542
    }
543 544

    RegCloseKey(hkey);
545
  } else {
546 547
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
548
  }
549 550 551 552 553 554 555 556 557

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

558
  keyname = regKeyBase + "\\RecordingProject7";
559
  hkey = NULL;
560 561 562 563
  result =
    RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
                  0, KEY_READ, &hkey);
  if (ERROR_SUCCESS == result) {
564
    DWORD valueType = REG_SZ;
565
    wchar_t data1[256];
566 567 568
    DWORD cch_data1 = sizeof(data1) * sizeof(data1[0]);
    RegQueryValueExW(hkey, L"Path", 0, &valueType, (LPBYTE)&data1[0],
                     &cch_data1);
569 570 571

    DWORD data2 = 0;
    DWORD cch_data2 = sizeof(data2);
572 573
    RegQueryValueExW(hkey, L"Security", 0, &valueType, (LPBYTE)&data2,
                     &cch_data2);
574 575 576

    DWORD data3 = 0;
    DWORD cch_data3 = sizeof(data3);
577 578
    RegQueryValueExW(hkey, L"StorageFormat", 0, &valueType, (LPBYTE)&data3,
                     &cch_data3);
579

580
    s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
581
    cmSystemTools::ConvertToUnixSlashes(s2);
582
    if (s2 == s1) {
583
      macrosRegistered = true;
584
    }
585

586 587 588 589 590
    // 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;
591 592

    RegCloseKey(hkey);
593
  } else {
594 595
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
596
  }
597 598 599 600

  return macrosRegistered;
}

601 602 603
void WriteVSMacrosFileRegistryEntry(const std::string& nextAvailableSubKeyName,
                                    const std::string& macrosFile,
                                    const std::string& regKeyBase)
604
{
605
  std::string keyname = regKeyBase + "\\OtherProjects7";
606
  HKEY hkey = NULL;
607 608 609 610
  LONG result =
    RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
                  0, KEY_READ | KEY_WRITE, &hkey);
  if (ERROR_SUCCESS == result) {
611 612
    // Create the subkey and set the values of interest:
    HKEY hsubkey = NULL;
613
    wchar_t lpClass[] = L"";
614 615 616 617
    result = RegCreateKeyExW(
      hkey, cmsys::Encoding::ToWide(nextAvailableSubKeyName).c_str(), 0,
      lpClass, 0, KEY_READ | KEY_WRITE, 0, &hsubkey, 0);
    if (ERROR_SUCCESS == result) {
618 619 620
      DWORD dw = 0;

      std::string s(macrosFile);
621
      std::replace(s.begin(), s.end(), '/', '\\');
622
      std::wstring ws = cmsys::Encoding::ToWide(s);
623

624 625 626 627
      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) {
628 629
        std::cout << "error result 1: " << result << std::endl;
        std::cout << std::endl;
630
      }
631 632 633 634

      // 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;
635 636 637
      result = RegSetValueExW(hsubkey, L"Security", 0, REG_DWORD, (LPBYTE)&dw,
                              sizeof(DWORD));
      if (ERROR_SUCCESS != result) {
638 639
        std::cout << "error result 2: " << result << std::endl;
        std::cout << std::endl;
640
      }
641 642 643

      // StorageFormat value is always "0" for sample macros files
      dw = 0;
644 645 646
      result = RegSetValueExW(hsubkey, L"StorageFormat", 0, REG_DWORD,
                              (LPBYTE)&dw, sizeof(DWORD));
      if (ERROR_SUCCESS != result) {
647 648
        std::cout << "error result 3: " << result << std::endl;
        std::cout << std::endl;
649
      }
650 651

      RegCloseKey(hsubkey);
652 653 654
    } else {
      std::cout << "error creating subkey: " << nextAvailableSubKeyName
                << std::endl;
655 656
      std::cout << std::endl;
    }
657 658
    RegCloseKey(hkey);
  } else {
659 660
    std::cout << "error opening key: " << keyname << std::endl;
    std::cout << std::endl;
661
  }
662 663
}

664
void RegisterVisualStudioMacros(const std::string& macrosFile,
665
                                const std::string& regKeyBase)
666 667 668 669
{
  bool macrosRegistered;
  std::string nextAvailableSubKeyName;

670 671
  macrosRegistered = IsVisualStudioMacrosFileRegistered(
    macrosFile, regKeyBase, nextAvailableSubKeyName);
672

673 674 675
  if (!macrosRegistered) {
    int count =
      cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
676 677 678 679 680

    // 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,
681 682
    // emit a warning asking the user to exit all running Visual Studio
    // instances...
683
    //
684
    if (0 != count) {
685
      std::ostringstream oss;
686
      oss << "Could not register CMake's Visual Studio macros file '"
687 688 689 690 691 692 693
          << 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;
694
      cmSystemTools::Message(oss.str().c_str(), "Warning");
695 696 697 698 699 700

      // 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...
      //
701 702
      count = cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
        "ALL");
703 704 705 706

      // Also re-get the nextAvailableSubKeyName in case Visual Studio
      // wrote out new registered macros information as it was exiting:
      //
707
      if (0 == count) {
708
        IsVisualStudioMacrosFileRegistered(macrosFile, regKeyBase,
709
                                           nextAvailableSubKeyName);
710
      }
711
    }
712 713 714

    // Do another if check - 'count' may have changed inside the above if:
    //
715
    if (0 == count) {
716
      WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName, macrosFile,
717
                                     regKeyBase);
718
    }
719
  }
720
}
721 722
bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
  cmGeneratorTarget const* gt)
723 724
{
  // check to see if this is a fortran build
725
  std::set<std::string> languages;
726
  {
727 728 729 730
    // Issue diagnostic if the source files depend on the config.
    std::vector<cmSourceFile*> sources;
    if (!gt->GetConfigCommonSourceFiles(sources)) {
      return false;
731 732
    }
  }
733 734 735 736
  // If there's only one source language, Fortran has to be used
  // in order for the sources to compile.
  // Note: Via linker propagation, LINKER_LANGUAGE could become CXX in
  // this situation and mismatch from the actual language of the linker.
737
  gt->GetLanguages(languages, "");
738 739
  if (languages.size() == 1) {
    if (*languages.begin() == "Fortran") {
740
      return true;
741 742
    }
  }
743 744 745 746 747 748 749 750 751 752

  // In the case of mixed object files or sources mixed with objects,
  // decide the language based on the value of LINKER_LANGUAGE.
  // This will not make it possible to mix source files of different
  // languages, but object libraries will be linked together in the
  // same fashion as other generators do.
  if (gt->GetLinkerLanguage("") == "Fortran") {
    return true;
  }

753 754 755 756 757 758 759 760 761 762 763 764 765 766
  return false;
}

bool cmGlobalVisualStudioGenerator::TargetIsCSharpOnly(
  cmGeneratorTarget const* gt)
{
  // check to see if this is a C# build
  std::set<std::string> languages;
  {
    // Issue diagnostic if the source files depend on the config.
    std::vector<cmSourceFile*> sources;
    if (!gt->GetConfigCommonSourceFiles(sources)) {
      return false;
    }
767 768 769 770
    // Only "real" targets are allowed to be C# targets.
    if (gt->Target->GetType() > cmStateEnums::OBJECT_LIBRARY) {
      return false;
    }
771 772 773 774 775
  }
  gt->GetLanguages(languages, "");
  if (languages.size() == 1) {
    if (*languages.begin() == "CSharp") {
      return true;
776
    }
777
  }
778 779
  return false;
}
780

781 782
bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
  cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
783
{
Brad King's avatar
Brad King committed
784 785 786
  // 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.
787
  if (r->GetName() == this->First) {
788
    return false;
789 790
  }
  if (l->GetName() == this->First) {
791
    return true;
792
  }
Brad King's avatar
Brad King committed
793
  return l->GetName() < r->GetName();
794 795
}

796 797 798
cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  TargetDependSet const& targets, std::string const& first)
  : derived(TargetCompare(first))
799
{
800
  this->insert(targets.begin(), targets.end());
801
}
802

803 804 805
cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  TargetSet const& targets, std::string const& first)
  : derived(TargetCompare(first))
806
{
807 808
  for (TargetSet::const_iterator it = targets.begin(); it != targets.end();
       ++it) {
809
    this->insert(*it);
810
  }
811
}
812 813

std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
814
  const std::string& str, const std::string& config) const
815 816 817 818
{
  std::string replace = GetCMakeCFGIntDir();

  std::string tmp = str;
819 820
  for (std::string::size_type i = tmp.find(replace); i != std::string::npos;
       i = tmp.find(replace, i)) {
821 822
    tmp.replace(i, replace.size(), config);
    i += config.size();
823
  }
824 825
  return tmp;
}
826 827 828 829 830

void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
  cmGeneratorTarget* gt, std::vector<cmCustomCommand>& commands,
  std::string const& configName)
{
831 832
  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
    gt->GetModuleDefinitionInfo(configName);
833
  if (!mdi || !mdi->DefFileGenerated) {
834 835 836
    return;
  }

837
  std::vector<std::string> outputs;
838
  outputs.push_back(mdi->DefFile);
839 840 841 842
  std::vector<std::string> empty;
  std::vector<cmSourceFile const*> objectSources;
  gt->GetObjectSources(objectSources, configName);
  std::map<cmSourceFile const*, std::string> mapping;
843 844 845
  for (std::vector<cmSourceFile const*>::const_iterator it =
         objectSources.begin();
       it != objectSources.end(); ++it) {
846
    mapping[*it];
847 848
  }
  gt->LocalGenerator->ComputeObjectFilenames(mapping, gt);
849 850 851 852 853 854 855
  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");
856
  cmdl.push_back(mdi->DefFile);
857
  std::string obj_dir_expanded = obj_dir;
858
  cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(),
859
                               configName.c_str());
860 861
  cmSystemTools::MakeDirectory(obj_dir_expanded);
  std::string const objs_file = obj_dir_expanded + "/objects.txt";
862 863
  cmdl.push_back(objs_file);
  cmGeneratedFileStream fout(objs_file.c_str());
864
  if (!fout) {
865 866
    cmSystemTools::Error("could not open ", objs_file.c_str());
    return;
867
  }
868

869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
  if (mdi->WindowsExportAllSymbols) {
    std::vector<std::string> objs;
    for (std::vector<cmSourceFile const*>::const_iterator it =
           objectSources.begin();
         it != objectSources.end(); ++it) {
      // Find the object file name corresponding to this source file.
      std::map<cmSourceFile const*, std::string>::const_iterator map_it =
        mapping.find(*it);
      // It must exist because we populated the mapping just above.
      assert(!map_it->second.empty());
      std::string objFile = obj_dir + map_it->second;
      objs.push_back(objFile);
    }
    std::vector<cmSourceFile const*> externalObjectSources;
    gt->GetExternalObjects(externalObjectSources, configName);
    for (std::vector<cmSourceFile const*>::const_iterator it =
           externalObjectSources.begin();
         it != externalObjectSources.end(); ++it) {
      objs.push_back((*it)->GetFullPath());
    }

    gt->UseObjectLibraries(objs, configName);
    for (std::vector<std::string>::iterator it = objs.begin();
         it != objs.end(); ++it) {
      std::string objFile = *it;
      // replace $(ConfigurationName) in the object names
      cmSystemTools::ReplaceString(objFile, this->GetCMakeCFGIntDir(),
                                   configName.c_str());
      if (cmHasLiteralSuffix(objFile, ".obj")) {
        fout << objFile << "\n";
      }
900
    }
901
  }
902 903 904 905 906 907 908

  for (std::vector<cmSourceFile const*>::const_iterator i =
         mdi->Sources.begin();
       i != mdi->Sources.end(); ++i) {
    fout << (*i)->GetFullPath() << "\n";
  }

909 910
  cmCustomCommandLines commandLines;
  commandLines.push_back(cmdl);
911 912
  cmCustomCommand command(gt->Target->GetMakefile(), outputs, empty, empty,
                          commandLines, "Auto build dll exports", ".");
913 914
  commands.push_back(command);
}