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