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"
10 #include <system_error>
13 #include <cm/iterator>
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"
32 #include "cmStateTypes.h"
33 #include "cmStringAlgorithms.h"
34 #include "cmSystemTools.h"
38 cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(
39 cmake* cm, std::string const& platformInGeneratorName)
40 : cmGlobalGenerator(cm)
42 cm->GetState()->SetIsGeneratorMultiConfig(true);
43 cm->GetState()->SetWindowsShell(true);
44 cm->GetState()->SetWindowsVSIDE(true);
46 if (platformInGeneratorName.empty()) {
47 this->DefaultPlatformName = "Win32";
49 this->DefaultPlatformName = platformInGeneratorName;
50 this->PlatformInGeneratorName = true;
54 cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator()
58 cmGlobalVisualStudioGenerator::VSVersion
59 cmGlobalVisualStudioGenerator::GetVersion() const
64 void cmGlobalVisualStudioGenerator::SetVersion(VSVersion v)
69 void cmGlobalVisualStudioGenerator::EnableLanguage(
70 std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
72 mf->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT",
73 this->DefaultPlatformName);
74 this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
77 bool cmGlobalVisualStudioGenerator::SetGeneratorPlatform(std::string const& p,
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");
85 mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName());
86 return this->cmGlobalGenerator::SetGeneratorPlatform(p, mf);
89 std::string const& cmGlobalVisualStudioGenerator::GetPlatformName() const
91 if (!this->GeneratorPlatform.empty()) {
92 return this->GeneratorPlatform;
94 return this->DefaultPlatformName;
97 const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const
99 switch (this->Version) {
100 case cmGlobalVisualStudioGenerator::VSVersion::VS9:
102 case cmGlobalVisualStudioGenerator::VSVersion::VS11:
104 case cmGlobalVisualStudioGenerator::VSVersion::VS12:
106 case cmGlobalVisualStudioGenerator::VSVersion::VS14:
108 case cmGlobalVisualStudioGenerator::VSVersion::VS15:
110 case cmGlobalVisualStudioGenerator::VSVersion::VS16:
112 case cmGlobalVisualStudioGenerator::VSVersion::VS17:
118 void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout)
120 char utf8bom[] = { char(0xEF), char(0xBB), char(0xBF) };
121 fout.write(utf8bom, 3);
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";
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";
134 fout << "# Visual Studio 2012\n";
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";
142 fout << "# Visual Studio 2013\n";
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";
151 fout << "# Visual Studio 14\n";
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";
160 fout << "# Visual Studio 15\n";
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";
169 fout << "# Visual Studio Version 16\n";
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";
178 fout << "# Visual Studio Version 17\n";
184 std::string cmGlobalVisualStudioGenerator::GetRegistryBase()
186 return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
189 std::string cmGlobalVisualStudioGenerator::GetRegistryBase(const char* version)
191 std::string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\";
192 return key + version;
195 void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
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
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");
210 gen[0]->AddUtilityCommand("ALL_BUILD", true, std::move(cc));
212 gen[0]->AddGeneratorTarget(
213 cm::make_unique<cmGeneratorTarget>(allBuild, gen[0]));
216 // Organize in the "predefined targets" folder:
218 if (this->UseFolderProperty()) {
219 allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
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 ||
229 if (!this->IsExcluded(gen[0], tgt.get())) {
230 allBuild->AddUtility(tgt->GetName(), false);
237 // Configure CMake Visual Studio macros, for this user on this version
239 this->ConfigureCMakeVisualStudioMacros();
242 void cmGlobalVisualStudioGenerator::ComputeTargetObjectDirectory(
243 cmGeneratorTarget* gt) const
246 cmStrCat(gt->LocalGenerator->GetCurrentBinaryDirectory(), '/');
247 std::string tgtDir = gt->LocalGenerator->GetTargetDirectory(gt);
248 if (!tgtDir.empty()) {
252 const char* cd = this->GetCMakeCFGIntDir();
257 gt->ObjectDirectory = dir;
260 bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
261 const std::string& regKeyBase,
262 std::string& nextAvailableSubKeyName);
264 void RegisterVisualStudioMacros(const std::string& macrosFile,
265 const std::string& regKeyBase);
267 #define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
269 #define CMAKE_VSMACROS_RELOAD_MACRONAME \
270 "Macros.CMakeVSMacros2.Macros.ReloadProjects"
272 #define CMAKE_VSMACROS_STOP_MACRONAME "Macros.CMakeVSMacros2.Macros.StopBuild"
274 void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
276 std::string dir = this->GetUserMacrosDirectory();
279 std::string src = cmStrCat(cmSystemTools::GetCMakeRoot(),
280 "/Templates/" CMAKE_VSMACROS_FILENAME);
282 std::string dst = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
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.
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");
299 RegisterVisualStudioMacros(dst, this->GetUserMacrosRegKeyBase());
303 void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
304 MacroName m, const std::string& vsSolutionFile)
306 // If any solution or project files changed during the generation,
307 // tell Visual Studio to reload them...
308 std::string dir = this->GetUserMacrosDirectory();
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
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());
331 } else if (m == MacroStop) {
332 cmCallVisualStudioMacro::CallMacro(
333 vsSolutionFile, CMAKE_VSMACROS_STOP_MACRONAME, "",
334 this->GetCMakeInstance()->GetDebugOutput());
340 std::string cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
345 std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
350 void cmGlobalVisualStudioGenerator::FillLinkClosure(
351 const cmGeneratorTarget* target, TargetSet& linked)
353 if (linked.insert(target).second) {
354 TargetDependSet const& depends = this->GetTargetDirectDepends(target);
355 for (cmTargetDepend const& di : depends) {
357 this->FillLinkClosure(di, linked);
363 cmGlobalVisualStudioGenerator::TargetSet const&
364 cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmGeneratorTarget* target)
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);
375 void cmGlobalVisualStudioGenerator::FollowLinkDepends(
376 const cmGeneratorTarget* target, std::set<const cmGeneratorTarget*>& linked)
378 if (!target->IsInBuildSystem()) {
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) {
388 this->FollowLinkDepends(di, linked);
394 bool cmGlobalVisualStudioGenerator::ComputeTargetDepends()
396 if (!this->cmGlobalGenerator::ComputeTargetDepends()) {
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());
409 static bool VSLinkable(cmGeneratorTarget const* t)
411 return t->IsLinkable() || t->GetType() == cmStateEnums::OBJECT_LIBRARY;
414 void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(
415 cmGeneratorTarget* target)
417 if (this->VSTargetDepends.find(target) != this->VSTargetDepends.end()) {
420 VSDependSet& vsTargetDepend = this->VSTargetDepends[target];
421 // VS <= 7.1 has two behaviors that affect solution dependencies.
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.)
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.
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);
445 TargetDependSet const& depends = this->GetTargetDirectDepends(target);
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) {
454 this->FollowLinkDepends(di, linkDepends);
459 // Collect explicit util dependencies (add_dependencies).
460 std::set<cmGeneratorTarget const*> utilDepends;
461 for (cmTargetDepend const& di : depends) {
463 this->FollowLinkDepends(di, utilDepends);
467 // Collect all targets linked by this target so we can avoid
468 // intermediate targets below.
470 if (target->GetType() != cmStateEnums::STATIC_LIBRARY) {
471 linked = this->GetTargetLinkClosure(target);
474 // Emit link dependencies.
475 for (cmGeneratorTarget const* dep : linkDepends) {
476 vsTargetDepend.insert(dep->GetName());
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());
485 // Direct dependency on linkable target not allowed.
486 // Use an intermediate utility target.
487 vsTargetDepend.insert(this->GetUtilityDepend(dgt));
492 bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf)
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());
503 std::string cmGlobalVisualStudioGenerator::GetUtilityDepend(
504 cmGeneratorTarget const* target)
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;
515 std::string cmGlobalVisualStudioGenerator::GetStartupProjectName(
516 cmLocalGenerator const* root) const
518 cmValue n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
520 std::string startup = *n;
521 if (this->FindTarget(startup)) {
524 root->GetMakefile()->IssueMessage(
525 MessageType::AUTHOR_WARNING,
526 "Directory property VS_STARTUP_PROJECT specifies target "
528 startup + "' that does not exist. Ignoring.");
532 // default, if not specified
533 return this->GetAllTargetName();
536 bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
537 const std::string& regKeyBase,
538 std::string& nextAvailableSubKeyName)
540 bool macrosRegistered = false;
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);
552 LONG result = ERROR_SUCCESS;
555 keyname = regKeyBase + "\\OtherProjects7";
558 RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
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;
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:
575 result = RegOpenKeyExW(hkey, subkeyname, 0, KEY_READ, &hsubkey);
576 if (ERROR_SUCCESS == result) {
577 DWORD valueType = REG_SZ;
579 DWORD cch_data1 = sizeof(data1);
580 RegQueryValueExW(hsubkey, L"Path", 0, &valueType, (LPBYTE)data1,
584 DWORD cch_data2 = sizeof(data2);
585 RegQueryValueExW(hsubkey, L"Security", 0, &valueType, (LPBYTE)&data2,
589 DWORD cch_data3 = sizeof(data3);
590 RegQueryValueExW(hsubkey, L"StorageFormat", 0, &valueType,
591 (LPBYTE)&data3, &cch_data3);
593 s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
594 cmSystemTools::ConvertToUnixSlashes(s2);
596 macrosRegistered = true;
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);
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;
621 RegCloseKey(hsubkey);
623 std::cout << "error opening subkey: "
624 << cmsys::Encoding::ToNarrow(subkeyname) << std::endl;
625 std::cout << std::endl;
629 cch_subkeyname = cm::size(subkeyname);
630 cch_keyclass = cm::size(keyclass);
631 lastWriteTime.dwHighDateTime = 0;
632 lastWriteTime.dwLowDateTime = 0;
637 std::cout << "error opening key: " << keyname << std::endl;
638 std::cout << std::endl;
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);
647 keyname = regKeyBase + "\\RecordingProject7";
650 RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
652 if (ERROR_SUCCESS == result) {
653 DWORD valueType = REG_SZ;
655 DWORD cch_data1 = sizeof(data1);
656 RegQueryValueExW(hkey, L"Path", 0, &valueType, (LPBYTE)data1, &cch_data1);
659 DWORD cch_data2 = sizeof(data2);
660 RegQueryValueExW(hkey, L"Security", 0, &valueType, (LPBYTE)&data2,
664 DWORD cch_data3 = sizeof(data3);
665 RegQueryValueExW(hkey, L"StorageFormat", 0, &valueType, (LPBYTE)&data3,
668 s2 = cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1));
669 cmSystemTools::ConvertToUnixSlashes(s2);
671 macrosRegistered = true;
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;
682 std::cout << "error opening key: " << keyname << std::endl;
683 std::cout << std::endl;
686 return macrosRegistered;
689 void WriteVSMacrosFileRegistryEntry(const std::string& nextAvailableSubKeyName,
690 const std::string& macrosFile,
691 const std::string& regKeyBase)
693 std::string keyname = regKeyBase + "\\OtherProjects7";
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:
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) {
708 std::string s(macrosFile);
709 std::replace(s.begin(), s.end(), '/', '\\');
710 std::wstring ws = cmsys::Encoding::ToWide(s);
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;
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)
723 result = RegSetValueExW(hsubkey, L"Security", 0, REG_DWORD, (LPBYTE)&dw,
725 if (ERROR_SUCCESS != result) {
726 std::cout << "error result 2: " << result << std::endl;
727 std::cout << std::endl;
730 // StorageFormat value is always "0" for sample macros files
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;
739 RegCloseKey(hsubkey);
741 std::cout << "error creating subkey: " << nextAvailableSubKeyName
743 std::cout << std::endl;
747 std::cout << "error opening key: " << keyname << std::endl;
748 std::cout << std::endl;
752 void RegisterVisualStudioMacros(const std::string& macrosFile,
753 const std::string& regKeyBase)
755 bool macrosRegistered;
756 std::string nextAvailableSubKeyName;
758 macrosRegistered = IsVisualStudioMacrosFileRegistered(
759 macrosFile, regKeyBase, nextAvailableSubKeyName);
761 if (!macrosRegistered) {
763 cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
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
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
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");
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...
789 count = cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
792 // Also re-get the nextAvailableSubKeyName in case Visual Studio
793 // wrote out new registered macros information as it was exiting:
796 IsVisualStudioMacrosFileRegistered(macrosFile, regKeyBase,
797 nextAvailableSubKeyName);
801 // Do another if check - 'count' may have changed inside the above if:
804 WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName, macrosFile,
809 bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
810 cmGeneratorTarget const* gt)
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
820 cmValue linkLang = gt->GetProperty("LINKER_LANGUAGE");
821 if (cmNonempty(linkLang)) {
822 languages.insert(*linkLang);
825 // Intel Fortran .vfproj files do support the resource compiler.
826 languages.erase("RC");
828 return languages.size() == 1 && *languages.begin() == "Fortran";
831 bool cmGlobalVisualStudioGenerator::IsInSolution(
832 const cmGeneratorTarget* gt) const
834 return gt->IsInBuildSystem();
837 bool cmGlobalVisualStudioGenerator::IsDepInSolution(
838 const std::string& targetName) const
840 return !targetName.empty();
843 bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
844 cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
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) {
852 if (l->GetName() == this->First) {
855 return l->GetName() < r->GetName();
858 cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
859 TargetDependSet const& targets, std::string const& first)
860 : derived(TargetCompare(first))
862 this->insert(targets.begin(), targets.end());
865 cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
866 TargetSet const& targets, std::string const& first)
867 : derived(TargetCompare(first))
869 for (cmGeneratorTarget const* it : targets) {
874 std::string cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
875 const std::string& str, const std::string& config) const
877 std::string replace = GetCMakeCFGIntDir();
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);
888 void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
889 cmGeneratorTarget* gt, std::vector<cmCustomCommand>& commands,
890 std::string const& configName)
892 cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
893 gt->GetModuleDefinitionInfo(configName);
894 if (!mdi || !mdi->DefFileGenerated) {
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) {
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(),
913 cmSystemTools::MakeDirectory(obj_dir_expanded);
914 std::string const objs_file = obj_dir_expanded + "/objects.txt";
915 cmGeneratedFileStream fout(objs_file.c_str());
917 cmSystemTools::Error("could not open " + objs_file);
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];
928 std::string objFile = obj_dir + v;
929 objs.push_back(objFile);
931 std::vector<cmSourceFile const*> externalObjectSources;
932 gt->GetExternalObjects(externalObjectSources, configName);
933 for (cmSourceFile const* it : externalObjectSources) {
934 objs.push_back(it->GetFullPath());
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(),
942 if (cmHasLiteralSuffix(objFile, ".obj")) {
943 fout << objFile << "\n";
948 for (cmSourceFile const* i : mdi->Sources) {
949 fout << i->GetFullPath() << "\n";
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));
964 static bool OpenSolution(std::string sln)
966 HRESULT comInitialized =
967 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
968 if (FAILED(comInitialized)) {
973 ShellExecuteA(NULL, "open", sln.c_str(), NULL, NULL, SW_SHOWNORMAL);
977 return reinterpret_cast<intptr_t>(hi) > 32;
980 bool cmGlobalVisualStudioGenerator::Open(const std::string& bindir,
981 const std::string& projectName,
984 std::string sln = bindir + "/" + projectName + ".sln";
987 return cmSystemTools::FileExists(sln, true);
990 sln = cmSystemTools::ConvertToOutputPath(sln);
992 return std::async(std::launch::async, OpenSolution, sln).get();