resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmGlobalVisualStudioGenerator.cxx
1
2 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
3    file Copyright.txt or https://cmake.org/licensing for details.  */
4 #include "cmGlobalVisualStudioGenerator.h"
5
6 #include <cassert>
7 #include <future>
8 #include <iostream>
9 #include <sstream>
10 #include <system_error>
11 #include <utility>
12
13 #include <cm/iterator>
14 #include <cm/memory>
15
16 #include <windows.h>
17
18 #include <objbase.h>
19 #include <shellapi.h>
20
21 #include "cmCallVisualStudioMacro.h"
22 #include "cmCustomCommand.h"
23 #include "cmCustomCommandLines.h"
24 #include "cmGeneratedFileStream.h"
25 #include "cmGeneratorTarget.h"
26 #include "cmLocalGenerator.h"
27 #include "cmMakefile.h"
28 #include "cmMessageType.h"
29 #include "cmPolicies.h"
30 #include "cmSourceFile.h"
31 #include "cmState.h"
32 #include "cmStateTypes.h"
33 #include "cmStringAlgorithms.h"
34 #include "cmSystemTools.h"
35 #include "cmTarget.h"
36 #include "cmake.h"
37
38 cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(
39   cmake* cm, std::string const& platformInGeneratorName)
40   : cmGlobalGenerator(cm)
41 {
42   cm->GetState()->SetIsGeneratorMultiConfig(true);
43   cm->GetState()->SetWindowsShell(true);
44   cm->GetState()->SetWindowsVSIDE(true);
45
46   if (platformInGeneratorName.empty()) {
47     this->DefaultPlatformName = "Win32";
48   } else {
49     this->DefaultPlatformName = platformInGeneratorName;
50     this->PlatformInGeneratorName = true;
51   }
52 }
53
54 cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator()
55 {
56 }
57
58 cmGlobalVisualStudioGenerator::VSVersion
59 cmGlobalVisualStudioGenerator::GetVersion() const
60 {
61   return this->Version;
62 }
63
64 void cmGlobalVisualStudioGenerator::SetVersion(VSVersion v)
65 {
66   this->Version = v;
67 }
68
69 void cmGlobalVisualStudioGenerator::EnableLanguage(
70   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
71 {
72   mf->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT",
73                     this->DefaultPlatformName);
74   this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
75 }
76
77 bool cmGlobalVisualStudioGenerator::SetGeneratorPlatform(std::string const& p,
78                                                          cmMakefile* mf)
79 {
80   if (this->GetPlatformName() == "x64") {
81     mf->AddDefinition("CMAKE_FORCE_WIN64", "TRUE");
82   } else if (this->GetPlatformName() == "Itanium") {
83     mf->AddDefinition("CMAKE_FORCE_IA64", "TRUE");
84   }
85   mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName());
86   return this->cmGlobalGenerator::SetGeneratorPlatform(p, mf);
87 }
88
89 std::string const& cmGlobalVisualStudioGenerator::GetPlatformName() const
90 {
91   if (!this->GeneratorPlatform.empty()) {
92     return this->GeneratorPlatform;
93   }
94   return this->DefaultPlatformName;
95 }
96
97 const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const
98 {
99   switch (this->Version) {
100     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
101       return "9.0";
102     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
103       return "11.0";
104     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
105       return "12.0";
106     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
107       return "14.0";
108     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
109       return "15.0";
110     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
111       return "16.0";
112     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
113       return "17.0";
114   }
115   return "";
116 }
117
118 void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout)
119 {
120   char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) };
121   fout.write(utf8bom, 3);
122   fout << '\n';
123
124   switch (this->Version) {
125     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
126       fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n";
127       fout << "# Visual Studio 2008\n";
128       break;
129     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
130       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
131       if (this->ExpressEdition) {
132         fout << "# Visual Studio Express 2012 for Windows Desktop\n";
133       } else {
134         fout << "# Visual Studio 2012\n";
135       }
136       break;
137     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
138       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
139       if (this->ExpressEdition) {
140         fout << "# Visual Studio Express 2013 for Windows Desktop\n";
141       } else {
142         fout << "# Visual Studio 2013\n";
143       }
144       break;
145     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
146       // Visual Studio 14 writes .sln format 12.00
147       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
148       if (this->ExpressEdition) {
149         fout << "# Visual Studio Express 14 for Windows Desktop\n";
150       } else {
151         fout << "# Visual Studio 14\n";
152       }
153       break;
154     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
155       // Visual Studio 15 writes .sln format 12.00
156       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
157       if (this->ExpressEdition) {
158         fout << "# Visual Studio Express 15 for Windows Desktop\n";
159       } else {
160         fout << "# Visual Studio 15\n";
161       }
162       break;
163     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
164       // Visual Studio 16 writes .sln format 12.00
165       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
166       if (this->ExpressEdition) {
167         fout << "# Visual Studio Express 16 for Windows Desktop\n";
168       } else {
169         fout << "# Visual Studio Version 16\n";
170       }
171       break;
172     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
173       // Visual Studio 17 writes .sln format 12.00
174       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
175       if (this->ExpressEdition) {
176         fout << "# Visual Studio Express 17 for Windows Desktop\n";
177       } else {
178         fout << "# Visual Studio Version 17\n";
179       }
180       break;
181   }
182 }
183
184 std::string cmGlobalVisualStudioGenerator::GetRegistryBase()
185 {
186   return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
187 }
188
189 std::string cmGlobalVisualStudioGenerator::GetRegistryBase(const char* version)
190 {
191   std::string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\";
192   return key + version;
193 }
194
195 void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
196 {
197   // Add a special target that depends on ALL projects for easy build
198   // of one configuration only.
199   for (auto const& it : this->ProjectMap) {
200     std::vector<cmLocalGenerator*> const& gen = it.second;
201     // add the ALL_BUILD to the first local generator of each project
202     if (!gen.empty()) {
203       // Use no actual command lines so that the target itself is not
204       // considered always out of date.
205       auto cc = cm::make_unique<cmCustomCommand>();
206       cc->SetCMP0116Status(cmPolicies::NEW);
207       cc->SetEscapeOldStyle(false);
208       cc->SetComment("Build all projects");
209       cmTarget* allBuild =
210         gen[0]->AddUtilityCommand("ALL_BUILD", true, std::move(cc));
211
212       gen[0]->AddGeneratorTarget(
213         cm::make_unique<cmGeneratorTarget>(allBuild, gen[0]));
214
215       //
216       // Organize in the "predefined targets" folder:
217       //
218       if (this->UseFolderProperty()) {
219         allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
220       }
221
222       // Now make all targets depend on the ALL_BUILD target
223       for (cmLocalGenerator const* i : gen) {
224         for (const auto& tgt : i->GetGeneratorTargets()) {
225           if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
226               tgt->IsImported()) {
227             continue;
228           }
229           if (!this->IsExcluded(gen[0], tgt.get())) {
230             allBuild->AddUtility(tgt->GetName(), false);
231           }
232         }
233       }
234     }
235   }
236
237   // Configure CMake Visual Studio macros, for this user on this version
238   // of Visual Studio.
239   this->ConfigureCMakeVisualStudioMacros();
240 }
241
242 void cmGlobalVisualStudioGenerator::ComputeTargetObjectDirectory(
243   cmGeneratorTarget* gt) const
244 {
245   std::string dir =
246     cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/');
247   std::string tgtDir = gt->LocalGenerator->GetTargetDirectory(gt);
248   if (!tgtDir.empty()) {
249     dir += tgtDir;
250     dir += "/";
251   }
252   const char* cd = this->GetCMakeCFGIntDir();
253   if (cd && *cd) {
254     dir += cd;
255     dir += "/";
256   }
257   gt->ObjectDirectory = dir;
258 }
259
260 bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
261                                         const std::string& regKeyBase,
262                                         std::string& nextAvailableSubKeyName);
263
264 void RegisterVisualStudioMacros(const std::string& macrosFile,
265                                 const std::string& regKeyBase);
266
267 #define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
268
269 #define CMAKE_VSMACROS_RELOAD_MACRONAME                                       \
270   "Macros.CMakeVSMacros2.Macros.ReloadProjects"
271
272 #define CMAKE_VSMACROS_STOP_MACRONAME "Macros.CMakeVSMacros2.Macros.StopBuild"
273
274 void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
275 {
276   std::string dir = this->GetUserMacrosDirectory();
277
278   if (!dir.empty()) {
279     std::string src = cmStrCat(cmSystemTools::GetCMakeRoot(),
280                                "/Templates/" CMAKE_VSMACROS_FILENAME);
281
282     std::string dst = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
283
284     // Copy the macros file to the user directory only if the
285     // destination does not exist or the source location is newer.
286     // This will allow the user to edit the macros for development
287     // purposes but newer versions distributed with CMake will replace
288     // older versions in user directories.
289     int res;
290     if (!cmSystemTools::FileTimeCompare(src, dst, &res) || res > 0) {
291       if (!cmSystemTools::CopyFileAlways(src, dst)) {
292         std::ostringstream oss;
293         oss << "Could not copy from: " << src << std::endl;
294         oss << "                 to: " << dst << std::endl;
295         cmSystemTools::Message(oss.str(), "Warning");
296       }
297     }
298
299     RegisterVisualStudioMacros(dst, this->GetUserMacrosRegKeyBase());
300   }
301 }
302
303 void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
304   MacroName m, const std::string& vsSolutionFile)
305 {
306   // If any solution or project files changed during the generation,
307   // tell Visual Studio to reload them...
308   std::string dir = this->GetUserMacrosDirectory();
309
310   // Only really try to call the macro if:
311   //  - there is a UserMacrosDirectory
312   //  - the CMake vsmacros file exists
313   //  - the CMake vsmacros file is registered
314   //  - there were .sln/.vcproj files changed during generation
315   //
316   if (!dir.empty()) {
317     std::string macrosFile = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
318     std::string nextSubkeyName;
319     if (cmSystemTools::FileExists(macrosFile) &&
320         IsVisualStudioMacrosFileRegistered(
321           macrosFile, this->GetUserMacrosRegKeyBase(), nextSubkeyName)) {
322       if (m == MacroReload) {
323         std::vector<std::string> filenames;
324         this->GetFilesReplacedDuringGenerate(filenames);
325         if (!filenames.empty()) {
326           std::string projects = cmJoin(filenames, ";");
327           cmCallVisualStudioMacro::CallMacro(
328             vsSolutionFile, CMAKE_VSMACROS_RELOAD_MACRONAME, projects,
329             this->GetCMakeInstance()->GetDebugOutput());
330         }
331       } else if (m == MacroStop) {
332         cmCallVisualStudioMacro::CallMacro(
333           vsSolutionFile, CMAKE_VSMACROS_STOP_MACRONAME, "",
334           this->GetCMakeInstance()->GetDebugOutput());
335       }
336     }
337   }
338 }
339
340 std::string cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
341 {
342   return "";
343 }
344
345 std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
346 {
347   return "";
348 }
349
350 void cmGlobalVisualStudioGenerator::FillLinkClosure(
351   const cmGeneratorTarget* target, TargetSet& linked)
352 {
353   if (linked.insert(target).second) {
354     TargetDependSet const& depends = this->GetTargetDirectDepends(target);
355     for (cmTargetDepend const& di : depends) {
356       if (di.IsLink()) {
357         this->FillLinkClosure(di, linked);
358       }
359     }
360   }
361 }
362
363 cmGlobalVisualStudioGenerator::TargetSet const&
364 cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmGeneratorTarget* target)
365 {
366   auto i = this->TargetLinkClosure.find(target);
367   if (i == this->TargetLinkClosure.end()) {
368     TargetSetMap::value_type entry(target, TargetSet());
369     i = this->TargetLinkClosure.insert(entry).first;
370     this->FillLinkClosure(target, i->second);
371   }
372   return i->second;
373 }
374
375 void cmGlobalVisualStudioGenerator::FollowLinkDepends(
376   const cmGeneratorTarget* target, std::set<const cmGeneratorTarget*>& linked)
377 {
378   if (!target->IsInBuildSystem()) {
379     return;
380   }
381   if (linked.insert(target).second &&
382       target->GetType() == cmStateEnums::STATIC_LIBRARY) {
383     // Static library targets do not list their link dependencies so
384     // we must follow them transitively now.
385     TargetDependSet const& depends = this->GetTargetDirectDepends(target);
386     for (cmTargetDepend const& di : depends) {
387       if (di.IsLink()) {
388         this->FollowLinkDepends(di, linked);
389       }
390     }
391   }
392 }
393
394 bool cmGlobalVisualStudioGenerator::ComputeTargetDepends()
395 {
396   if (!this->cmGlobalGenerator::ComputeTargetDepends()) {
397     return false;
398   }
399   for (auto const& it : this->ProjectMap) {
400     for (const cmLocalGenerator* i : it.second) {
401       for (const auto& ti : i->GetGeneratorTargets()) {
402         this->ComputeVSTargetDepends(ti.get());
403       }
404     }
405   }
406   return true;
407 }
408
409 static bool VSLinkable(cmGeneratorTarget const* t)
410 {
411   return t->IsLinkable() || t->GetType() == cmStateEnums::OBJECT_LIBRARY;
412 }
413
414 void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(
415   cmGeneratorTarget* target)
416 {
417   if (this->VSTargetDepends.find(target) != this->VSTargetDepends.end()) {
418     return;
419   }
420   VSDependSet& vsTargetDepend = this->VSTargetDepends[target];
421   // VS <= 7.1 has two behaviors that affect solution dependencies.
422   //
423   // (1) Solution-level dependencies between a linkable target and a
424   // library cause that library to be linked.  We use an intermedite
425   // empty utility target to express the dependency.  (VS 8 and above
426   // provide a project file "LinkLibraryDependencies" setting to
427   // choose whether to activate this behavior.  We disable it except
428   // when linking external project files.)
429   //
430   // (2) We cannot let static libraries depend directly on targets to
431   // which they "link" because the librarian tool will copy the
432   // targets into the static library.  While the work-around for
433   // behavior (1) would also avoid this, it would create a large
434   // number of extra utility targets for little gain.  Instead, use
435   // the above work-around only for dependencies explicitly added by
436   // the add_dependencies() command.  Approximate link dependencies by
437   // leaving them out for the static library itself but following them
438   // transitively for other targets.
439
440   bool allowLinkable = (target->GetType() != cmStateEnums::STATIC_LIBRARY &&
441                         target->GetType() != cmStateEnums::SHARED_LIBRARY &&
442                         target->GetType() != cmStateEnums::MODULE_LIBRARY &&
443                         target->GetType() != cmStateEnums::EXECUTABLE);
444
445   TargetDependSet const& depends = this->GetTargetDirectDepends(target);
446
447   // Collect implicit link dependencies (target_link_libraries).
448   // Static libraries cannot depend on their link implementation
449   // due to behavior (2), but they do not really need to.
450   std::set<cmGeneratorTarget const*> linkDepends;
451   if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
452     for (cmTargetDepend const& di : depends) {
453       if (di.IsLink()) {
454         this->FollowLinkDepends(di, linkDepends);
455       }
456     }
457   }
458
459   // Collect explicit util dependencies (add_dependencies).
460   std::set<cmGeneratorTarget const*> utilDepends;
461   for (cmTargetDepend const& di : depends) {
462     if (di.IsUtil()) {
463       this->FollowLinkDepends(di, utilDepends);
464     }
465   }
466
467   // Collect all targets linked by this target so we can avoid
468   // intermediate targets below.
469   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       // Direct dependency on linkable target not allowed.
486       // Use an intermediate utility target.
487       vsTargetDepend.insert(this->GetUtilityDepend(dgt));
488     }
489   }
490 }
491
492 bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf)
493 {
494   // Visual Studio generators know how to lookup their build tool
495   // directly instead of needing a helper module to do it, so we
496   // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
497   if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
498     mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram());
499   }
500   return true;
501 }
502
503 std::string cmGlobalVisualStudioGenerator::GetUtilityDepend(
504   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     UtilityDependsMap::value_type entry(target, name);
510     i = this->UtilityDepends.insert(entry).first;
511   }
512   return i->second;
513 }
514
515 std::string cmGlobalVisualStudioGenerator::GetStartupProjectName(
516   cmLocalGenerator const* root) const
517 {
518   cmValue n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
519   if (cmNonempty(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   // default, if not specified
533   return this->GetAllTargetName();
534 }
535
536 bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
537                                         const std::string& regKeyBase,
538                                         std::string& nextAvailableSubKeyName)
539 {
540   bool macrosRegistered = false;
541
542   std::string s1;
543   std::string s2;
544
545   // Make lowercase local copies, convert to Unix slashes, and
546   // see if the resulting strings are the same:
547   s1 = cmSystemTools::LowerCase(macrosFile);
548   cmSystemTools::ConvertToUnixSlashes(s1);
549
550   std::string keyname;
551   HKEY hkey = NULL;
552   LONG result = ERROR_SUCCESS;
553   DWORD index = 0;
554
555   keyname = regKeyBase + "\\OtherProjects7";
556   hkey = NULL;
557   result =
558     RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
559                   0, KEY_READ, &hkey);
560   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 = cm::size(subkeyname);
564     wchar_t keyclass[256];
565     DWORD cch_keyclass = cm::size(keyclass);
566     FILETIME lastWriteTime;
567     lastWriteTime.dwHighDateTime = 0;
568     lastWriteTime.dwLowDateTime = 0;
569
570     while (ERROR_SUCCESS ==
571            RegEnumKeyExW(hkey, index, subkeyname, &cch_subkeyname, 0, keyclass,
572                          &cch_keyclass, &lastWriteTime)) {
573       // Open the subkey and query the values of interest:
574       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         DWORD cch_data1 = sizeof(data1);
580         RegQueryValueExW(hsubkey, L"Path", 0, &valueType, (LPBYTE)data1,
581                          &cch_data1);
582
583         DWORD data2 = 0;
584         DWORD cch_data2 = sizeof(data2);
585         RegQueryValueExW(hsubkey, L"Security", 0, &valueType, (LPBYTE)&data2,
586                          &cch_data2);
587
588         DWORD data3 = 0;
589         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         std::string filename;
601         std::string filepath;
602         std::string filepathname;
603         std::string filepathpath;
604         if (cmSystemTools::FileExists(fullname)) {
605           filename = cmSystemTools::GetFilenameName(fullname);
606           filepath = cmSystemTools::GetFilenamePath(fullname);
607           filepathname = cmSystemTools::GetFilenameName(filepath);
608           filepathpath = cmSystemTools::GetFilenamePath(filepath);
609         }
610
611         // std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
612         // std::cout << "  Path: " << data1 << std::endl;
613         // std::cout << "  Security: " << data2 << std::endl;
614         // std::cout << "  StorageFormat: " << data3 << std::endl;
615         // std::cout << "  filename: " << filename << std::endl;
616         // std::cout << "  filepath: " << filepath << std::endl;
617         // std::cout << "  filepathname: " << filepathname << std::endl;
618         // std::cout << "  filepathpath: " << filepathpath << std::endl;
619         // std::cout << std::endl;
620
621         RegCloseKey(hsubkey);
622       } else {
623         std::cout << "error opening subkey: "
624                   << cmsys::Encoding::ToNarrow(subkeyname) << std::endl;
625         std::cout << std::endl;
626       }
627
628       ++index;
629       cch_subkeyname = cm::size(subkeyname);
630       cch_keyclass = cm::size(keyclass);
631       lastWriteTime.dwHighDateTime = 0;
632       lastWriteTime.dwLowDateTime = 0;
633     }
634
635     RegCloseKey(hkey);
636   } else {
637     std::cout << "error opening key: " << keyname << std::endl;
638     std::cout << std::endl;
639   }
640
641   // Pass back next available sub key name, assuming sub keys always
642   // follow the expected naming scheme. Expected naming scheme is that
643   // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
644   // as the name of the next subkey.
645   nextAvailableSubKeyName = std::to_string(index);
646
647   keyname = regKeyBase + "\\RecordingProject7";
648   hkey = NULL;
649   result =
650     RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
651                   0, KEY_READ, &hkey);
652   if (ERROR_SUCCESS == result) {
653     DWORD valueType = REG_SZ;
654     wchar_t data1[256];
655     DWORD cch_data1 = sizeof(data1);
656     RegQueryValueExW(hkey, L"Path", 0, &valueType, (LPBYTE)data1, &cch_data1);
657
658     DWORD data2 = 0;
659     DWORD cch_data2 = sizeof(data2);
660     RegQueryValueExW(hkey, L"Security", 0, &valueType, (LPBYTE)&data2,
661                      &cch_data2);
662
663     DWORD data3 = 0;
664     DWORD cch_data3 = sizeof(data3);
665     RegQueryValueExW(hkey, L"StorageFormat", 0, &valueType, (LPBYTE)&data3,
666                      &cch_data3);
667
668     s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
669     cmSystemTools::ConvertToUnixSlashes(s2);
670     if (s2 == s1) {
671       macrosRegistered = true;
672     }
673
674     // std::cout << keyname << ":" << std::endl;
675     // std::cout << "  Path: " << data1 << std::endl;
676     // std::cout << "  Security: " << data2 << std::endl;
677     // std::cout << "  StorageFormat: " << data3 << std::endl;
678     // std::cout << std::endl;
679
680     RegCloseKey(hkey);
681   } else {
682     std::cout << "error opening key: " << keyname << std::endl;
683     std::cout << std::endl;
684   }
685
686   return macrosRegistered;
687 }
688
689 void WriteVSMacrosFileRegistryEntry(const std::string& nextAvailableSubKeyName,
690                                     const std::string& macrosFile,
691                                     const std::string& regKeyBase)
692 {
693   std::string keyname = regKeyBase + "\\OtherProjects7";
694   HKEY hkey = NULL;
695   LONG result =
696     RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
697                   0, KEY_READ | KEY_WRITE, &hkey);
698   if (ERROR_SUCCESS == result) {
699     // Create the subkey and set the values of interest:
700     HKEY hsubkey = NULL;
701     wchar_t lpClass[] = L"";
702     result = RegCreateKeyExW(
703       hkey, cmsys::Encoding::ToWide(nextAvailableSubKeyName).c_str(), 0,
704       lpClass, 0, KEY_READ | KEY_WRITE, 0, &hsubkey, 0);
705     if (ERROR_SUCCESS == result) {
706       DWORD dw = 0;
707
708       std::string s(macrosFile);
709       std::replace(s.begin(), s.end(), '/', '\\');
710       std::wstring ws = cmsys::Encoding::ToWide(s);
711
712       result =
713         RegSetValueExW(hsubkey, L"Path", 0, REG_SZ, (LPBYTE)ws.c_str(),
714                        static_cast<DWORD>(ws.size() + 1) * sizeof(wchar_t));
715       if (ERROR_SUCCESS != result) {
716         std::cout << "error result 1: " << result << std::endl;
717         std::cout << std::endl;
718       }
719
720       // Security value is always "1" for sample macros files (seems to be "2"
721       // if you put the file somewhere outside the standard VSMacros folder)
722       dw = 1;
723       result = RegSetValueExW(hsubkey, L"Security", 0, REG_DWORD, (LPBYTE)&dw,
724                               sizeof(DWORD));
725       if (ERROR_SUCCESS != result) {
726         std::cout << "error result 2: " << result << std::endl;
727         std::cout << std::endl;
728       }
729
730       // StorageFormat value is always "0" for sample macros files
731       dw = 0;
732       result = RegSetValueExW(hsubkey, L"StorageFormat", 0, REG_DWORD,
733                               (LPBYTE)&dw, sizeof(DWORD));
734       if (ERROR_SUCCESS != result) {
735         std::cout << "error result 3: " << result << std::endl;
736         std::cout << std::endl;
737       }
738
739       RegCloseKey(hsubkey);
740     } else {
741       std::cout << "error creating subkey: " << nextAvailableSubKeyName
742                 << std::endl;
743       std::cout << std::endl;
744     }
745     RegCloseKey(hkey);
746   } else {
747     std::cout << "error opening key: " << keyname << std::endl;
748     std::cout << std::endl;
749   }
750 }
751
752 void RegisterVisualStudioMacros(const std::string& macrosFile,
753                                 const std::string& regKeyBase)
754 {
755   bool macrosRegistered;
756   std::string nextAvailableSubKeyName;
757
758   macrosRegistered = IsVisualStudioMacrosFileRegistered(
759     macrosFile, regKeyBase, nextAvailableSubKeyName);
760
761   if (!macrosRegistered) {
762     int count =
763       cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
764
765     // Only register the macros file if there are *no* instances of Visual
766     // Studio running. If we register it while one is running, first, it has
767     // no effect on the running instance; second, and worse, Visual Studio
768     // removes our newly added registration entry when it quits. Instead,
769     // emit a warning asking the user to exit all running Visual Studio
770     // instances...
771     //
772     if (0 != count) {
773       std::ostringstream oss;
774       oss << "Could not register CMake's Visual Studio macros file '"
775           << CMAKE_VSMACROS_FILENAME "' while Visual Studio is running."
776           << " Please exit all running instances of Visual Studio before"
777           << " continuing." << std::endl
778           << std::endl
779           << "CMake needs to register Visual Studio macros when its macros"
780           << " file is updated or when it detects that its current macros file"
781           << " is no longer registered with Visual Studio." << std::endl;
782       cmSystemTools::Message(oss.str(), "Warning");
783
784       // Count them again now that the warning is over. In the case of a GUI
785       // warning, the user may have gone to close Visual Studio and then come
786       // back to the CMake GUI and clicked ok on the above warning. If so,
787       // then register the macros *now* if the count is *now* 0...
788       //
789       count = cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
790         "ALL");
791
792       // Also re-get the nextAvailableSubKeyName in case Visual Studio
793       // wrote out new registered macros information as it was exiting:
794       //
795       if (0 == count) {
796         IsVisualStudioMacrosFileRegistered(macrosFile, regKeyBase,
797                                            nextAvailableSubKeyName);
798       }
799     }
800
801     // Do another if check - 'count' may have changed inside the above if:
802     //
803     if (0 == count) {
804       WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName, macrosFile,
805                                      regKeyBase);
806     }
807   }
808 }
809 bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
810   cmGeneratorTarget const* gt)
811 {
812   // If there's only one source language, Fortran has to be used
813   // in order for the sources to compile.
814   std::set<std::string> languages = gt->GetAllConfigCompileLanguages();
815   // Consider an explicit linker language property, but *not* the
816   // computed linker language that may depend on linked targets.
817   // This allows the project to control the language choice in
818   // a target with none of its own sources, e.g. when also using
819   // object libraries.
820   cmValue linkLang = gt->GetProperty("LINKER_LANGUAGE");
821   if (cmNonempty(linkLang)) {
822     languages.insert(*linkLang);
823   }
824
825   // Intel Fortran .vfproj files do support the resource compiler.
826   languages.erase("RC");
827
828   return languages.size() == 1 && *languages.begin() == "Fortran";
829 }
830
831 bool cmGlobalVisualStudioGenerator::IsInSolution(
832   const cmGeneratorTarget* gt) const
833 {
834   return gt->IsInBuildSystem();
835 }
836
837 bool cmGlobalVisualStudioGenerator::IsDepInSolution(
838   const std::string& targetName) const
839 {
840   return !targetName.empty();
841 }
842
843 bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
844   cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
845 {
846   // Make sure a given named target is ordered first,
847   // e.g. to set ALL_BUILD as the default active project.
848   // 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   }
855   return l->GetName() < r->GetName();
856 }
857
858 cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
859   TargetDependSet const& targets, std::string const& first)
860   : derived(TargetCompare(first))
861 {
862   this->insert(targets.begin(), targets.end());
863 }
864
865 cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
866   TargetSet const& targets, std::string const& first)
867   : derived(TargetCompare(first))
868 {
869   for (cmGeneratorTarget const* it : targets) {
870     this->insert(it);
871   }
872 }
873
874 std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
875   const std::string& str, const std::string& config) const
876 {
877   std::string replace = GetCMakeCFGIntDir();
878
879   std::string tmp = str;
880   for (std::string::size_type i = tmp.find(replace); i != std::string::npos;
881        i = tmp.find(replace, i)) {
882     tmp.replace(i, replace.size(), config);
883     i += config.size();
884   }
885   return tmp;
886 }
887
888 void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
889   cmGeneratorTarget* gt, std::vector<cmCustomCommand>& commands,
890   std::string const& configName)
891 {
892   cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
893     gt->GetModuleDefinitionInfo(configName);
894   if (!mdi || !mdi->DefFileGenerated) {
895     return;
896   }
897
898   std::vector<std::string> outputs;
899   outputs.push_back(mdi->DefFile);
900   std::vector<std::string> empty;
901   std::vector<cmSourceFile const*> objectSources;
902   gt->GetObjectSources(objectSources, configName);
903   std::map<cmSourceFile const*, std::string> mapping;
904   for (cmSourceFile const* it : objectSources) {
905     mapping[it];
906   }
907   gt->LocalGenerator->ComputeObjectFilenames(mapping, gt);
908   std::string obj_dir = gt->ObjectDirectory;
909   std::string cmakeCommand = cmSystemTools::GetCMakeCommand();
910   std::string obj_dir_expanded = obj_dir;
911   cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(),
912                                configName.c_str());
913   cmSystemTools::MakeDirectory(obj_dir_expanded);
914   std::string const objs_file = obj_dir_expanded + "/objects.txt";
915   cmGeneratedFileStream fout(objs_file.c_str());
916   if (!fout) {
917     cmSystemTools::Error("could not open " + objs_file);
918     return;
919   }
920
921   if (mdi->WindowsExportAllSymbols) {
922     std::vector<std::string> objs;
923     for (cmSourceFile const* it : objectSources) {
924       // Find the object file name corresponding to this source file.
925       // It must exist because we populated the mapping just above.
926       const auto& v = mapping[it];
927       assert(!v.empty());
928       std::string objFile = obj_dir + v;
929       objs.push_back(objFile);
930     }
931     std::vector<cmSourceFile const*> externalObjectSources;
932     gt->GetExternalObjects(externalObjectSources, configName);
933     for (cmSourceFile const* it : externalObjectSources) {
934       objs.push_back(it->GetFullPath());
935     }
936
937     for (std::string const& it : objs) {
938       std::string objFile = it;
939       // replace $(ConfigurationName) in the object names
940       cmSystemTools::ReplaceString(objFile, this->GetCMakeCFGIntDir(),
941                                    configName);
942       if (cmHasLiteralSuffix(objFile, ".obj")) {
943         fout << objFile << "\n";
944       }
945     }
946   }
947
948   for (cmSourceFile const* i : mdi->Sources) {
949     fout << i->GetFullPath() << "\n";
950   }
951
952   cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
953     { cmakeCommand, "-E", "__create_def", mdi->DefFile, objs_file });
954   cmCustomCommand command;
955   command.SetOutputs(outputs);
956   command.SetCommandLines(commandLines);
957   command.SetComment("Auto build dll exports");
958   command.SetBacktrace(gt->Target->GetMakefile()->GetBacktrace());
959   command.SetWorkingDirectory(".");
960   command.SetStdPipesUTF8(true);
961   commands.push_back(std::move(command));
962 }
963
964 static bool OpenSolution(std::string sln)
965 {
966   HRESULT comInitialized =
967     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
968   if (FAILED(comInitialized)) {
969     return false;
970   }
971
972   HINSTANCE hi =
973     ShellExecuteA(NULL, "open", sln.c_str(), NULL, NULL, SW_SHOWNORMAL);
974
975   CoUninitialize();
976
977   return reinterpret_cast<intptr_t>(hi) > 32;
978 }
979
980 bool cmGlobalVisualStudioGenerator::Open(const std::string& bindir,
981                                          const std::string& projectName,
982                                          bool dryRun)
983 {
984   std::string sln = bindir + "/" + projectName + ".sln";
985
986   if (dryRun) {
987     return cmSystemTools::FileExists(sln, true);
988   }
989
990   sln = cmSystemTools::ConvertToOutputPath(sln);
991
992   return std::async(std::launch::async, OpenSolution, sln).get();
993 }