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>
15 #include <cmext/string_view>
22 #include "cmCallVisualStudioMacro.h"
23 #include "cmCustomCommand.h"
24 #include "cmCustomCommandLines.h"
25 #include "cmGeneratedFileStream.h"
26 #include "cmGeneratorTarget.h"
27 #include "cmLocalGenerator.h"
28 #include "cmMakefile.h"
29 #include "cmMessageType.h"
30 #include "cmPolicies.h"
31 #include "cmSourceFile.h"
33 #include "cmStateTypes.h"
34 #include "cmStringAlgorithms.h"
35 #include "cmSystemTools.h"
39 cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator(
40 cmake
* cm
, std::string
const& platformInGeneratorName
)
41 : cmGlobalGenerator(cm
)
43 cm
->GetState()->SetIsGeneratorMultiConfig(true);
44 cm
->GetState()->SetWindowsShell(true);
45 cm
->GetState()->SetWindowsVSIDE(true);
47 if (platformInGeneratorName
.empty()) {
48 this->DefaultPlatformName
= "Win32";
50 this->DefaultPlatformName
= platformInGeneratorName
;
51 this->PlatformInGeneratorName
= true;
55 cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator() = default;
57 cmGlobalVisualStudioGenerator::VSVersion
58 cmGlobalVisualStudioGenerator::GetVersion() const
63 void cmGlobalVisualStudioGenerator::SetVersion(VSVersion v
)
68 void cmGlobalVisualStudioGenerator::EnableLanguage(
69 std::vector
<std::string
> const& lang
, cmMakefile
* mf
, bool optional
)
71 mf
->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT",
72 this->DefaultPlatformName
);
73 this->cmGlobalGenerator::EnableLanguage(lang
, mf
, optional
);
76 bool cmGlobalVisualStudioGenerator::SetGeneratorPlatform(std::string
const& p
,
79 if (!this->InitializePlatform(mf
)) {
82 if (this->GetPlatformName() == "x64"_s
) {
83 mf
->AddDefinition("CMAKE_FORCE_WIN64", "TRUE");
84 } else if (this->GetPlatformName() == "Itanium"_s
) {
85 mf
->AddDefinition("CMAKE_FORCE_IA64", "TRUE");
87 mf
->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName());
88 return this->cmGlobalGenerator::SetGeneratorPlatform(p
, mf
);
91 bool cmGlobalVisualStudioGenerator::InitializePlatform(cmMakefile
*)
96 std::string
const& cmGlobalVisualStudioGenerator::GetPlatformName() const
98 if (!this->GeneratorPlatform
.empty()) {
99 return this->GeneratorPlatform
;
101 return this->DefaultPlatformName
;
104 const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const
106 switch (this->Version
) {
107 case cmGlobalVisualStudioGenerator::VSVersion::VS14
:
109 case cmGlobalVisualStudioGenerator::VSVersion::VS15
:
111 case cmGlobalVisualStudioGenerator::VSVersion::VS16
:
113 case cmGlobalVisualStudioGenerator::VSVersion::VS17
:
119 void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream
& fout
)
121 char utf8bom
[] = { char(0xEF), char(0xBB), char(0xBF) };
122 fout
.write(utf8bom
, 3);
125 switch (this->Version
) {
126 case cmGlobalVisualStudioGenerator::VSVersion::VS14
:
127 // Visual Studio 14 writes .sln format 12.00
128 fout
<< "Microsoft Visual Studio Solution File, Format Version 12.00\n";
129 if (this->ExpressEdition
) {
130 fout
<< "# Visual Studio Express 14 for Windows Desktop\n";
132 fout
<< "# Visual Studio 14\n";
135 case cmGlobalVisualStudioGenerator::VSVersion::VS15
:
136 // Visual Studio 15 writes .sln format 12.00
137 fout
<< "Microsoft Visual Studio Solution File, Format Version 12.00\n";
138 if (this->ExpressEdition
) {
139 fout
<< "# Visual Studio Express 15 for Windows Desktop\n";
141 fout
<< "# Visual Studio 15\n";
144 case cmGlobalVisualStudioGenerator::VSVersion::VS16
:
145 // Visual Studio 16 writes .sln format 12.00
146 fout
<< "Microsoft Visual Studio Solution File, Format Version 12.00\n";
147 if (this->ExpressEdition
) {
148 fout
<< "# Visual Studio Express 16 for Windows Desktop\n";
150 fout
<< "# Visual Studio Version 16\n";
153 case cmGlobalVisualStudioGenerator::VSVersion::VS17
:
154 // Visual Studio 17 writes .sln format 12.00
155 fout
<< "Microsoft Visual Studio Solution File, Format Version 12.00\n";
156 if (this->ExpressEdition
) {
157 fout
<< "# Visual Studio Express 17 for Windows Desktop\n";
159 fout
<< "# Visual Studio Version 17\n";
165 std::string
cmGlobalVisualStudioGenerator::GetRegistryBase()
167 return cmGlobalVisualStudioGenerator::GetRegistryBase(this->GetIDEVersion());
170 std::string
cmGlobalVisualStudioGenerator::GetRegistryBase(const char* version
)
172 return cmStrCat(R
"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\)",
176 void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
178 // Add a special target that depends on ALL projects for easy build
179 // of one configuration only.
180 for (auto const& it
: this->ProjectMap
) {
181 std::vector
<cmLocalGenerator
*> const& gen
= it
.second
;
182 // add the ALL_BUILD to the first local generator of each project
184 // Use no actual command lines so that the target itself is not
185 // considered always out of date.
186 auto cc
= cm::make_unique
<cmCustomCommand
>();
187 cc
->SetEscapeOldStyle(false);
188 cc
->SetComment("Build all projects");
190 gen
[0]->AddUtilityCommand("ALL_BUILD", true, std::move(cc
));
192 gen
[0]->AddGeneratorTarget(
193 cm::make_unique
<cmGeneratorTarget
>(allBuild
, gen
[0]));
196 // Organize in the "predefined targets" folder:
198 if (this->UseFolderProperty()) {
199 allBuild
->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
202 // Now make all targets depend on the ALL_BUILD target
203 for (cmLocalGenerator
const* i
: gen
) {
204 for (const auto& tgt
: i
->GetGeneratorTargets()) {
205 if (tgt
->GetType() == cmStateEnums::GLOBAL_TARGET
||
209 if (!this->IsExcluded(gen
[0], tgt
.get())) {
210 allBuild
->AddUtility(tgt
->GetName(), false);
217 // Configure CMake Visual Studio macros, for this user on this version
219 this->ConfigureCMakeVisualStudioMacros();
222 void cmGlobalVisualStudioGenerator::ComputeTargetObjectDirectory(
223 cmGeneratorTarget
* gt
) const
226 cmStrCat(gt
->LocalGenerator
->GetCurrentBinaryDirectory(), '/');
227 std::string tgtDir
= gt
->LocalGenerator
->GetTargetDirectory(gt
);
228 if (!tgtDir
.empty()) {
232 const char* cd
= this->GetCMakeCFGIntDir();
237 gt
->ObjectDirectory
= dir
;
240 bool IsVisualStudioMacrosFileRegistered(const std::string
& macrosFile
,
241 const std::string
& regKeyBase
,
242 std::string
& nextAvailableSubKeyName
);
244 void RegisterVisualStudioMacros(const std::string
& macrosFile
,
245 const std::string
& regKeyBase
);
247 #define CMAKE_VSMACROS_FILENAME "CMakeVSMacros2.vsmacros"
249 #define CMAKE_VSMACROS_RELOAD_MACRONAME \
250 "Macros.CMakeVSMacros2.Macros.ReloadProjects"
252 #define CMAKE_VSMACROS_STOP_MACRONAME "Macros.CMakeVSMacros2.Macros.StopBuild"
254 void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
256 std::string dir
= this->GetUserMacrosDirectory();
259 std::string src
= cmStrCat(cmSystemTools::GetCMakeRoot(),
260 "/Templates/" CMAKE_VSMACROS_FILENAME
);
262 std::string dst
= cmStrCat(dir
, "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
);
264 // Copy the macros file to the user directory only if the
265 // destination does not exist or the source location is newer.
266 // This will allow the user to edit the macros for development
267 // purposes but newer versions distributed with CMake will replace
268 // older versions in user directories.
270 if (!cmSystemTools::FileTimeCompare(src
, dst
, &res
) || res
> 0) {
271 if (!cmSystemTools::CopyFileAlways(src
, dst
)) {
272 std::ostringstream oss
;
273 oss
<< "Could not copy from: " << src
<< std::endl
274 << " to: " << dst
<< std::endl
;
275 cmSystemTools::Message(oss
.str(), "Warning");
279 RegisterVisualStudioMacros(dst
, this->GetUserMacrosRegKeyBase());
283 void cmGlobalVisualStudioGenerator::CallVisualStudioMacro(
284 MacroName m
, const std::string
& vsSolutionFile
)
286 // If any solution or project files changed during the generation,
287 // tell Visual Studio to reload them...
288 std::string dir
= this->GetUserMacrosDirectory();
290 // Only really try to call the macro if:
291 // - there is a UserMacrosDirectory
292 // - the CMake vsmacros file exists
293 // - the CMake vsmacros file is registered
294 // - there were .sln/.vcproj files changed during generation
297 std::string macrosFile
=
298 cmStrCat(dir
, "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
);
299 std::string nextSubkeyName
;
300 if (cmSystemTools::FileExists(macrosFile
) &&
301 IsVisualStudioMacrosFileRegistered(
302 macrosFile
, this->GetUserMacrosRegKeyBase(), nextSubkeyName
)) {
303 if (m
== MacroReload
) {
304 std::vector
<std::string
> filenames
;
305 this->GetFilesReplacedDuringGenerate(filenames
);
306 if (!filenames
.empty()) {
307 std::string projects
= cmJoin(filenames
, ";");
308 cmCallVisualStudioMacro::CallMacro(
309 vsSolutionFile
, CMAKE_VSMACROS_RELOAD_MACRONAME
, projects
,
310 this->GetCMakeInstance()->GetDebugOutput());
312 } else if (m
== MacroStop
) {
313 cmCallVisualStudioMacro::CallMacro(
314 vsSolutionFile
, CMAKE_VSMACROS_STOP_MACRONAME
, "",
315 this->GetCMakeInstance()->GetDebugOutput());
321 std::string
cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
326 std::string
cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
331 void cmGlobalVisualStudioGenerator::FillLinkClosure(
332 const cmGeneratorTarget
* target
, TargetSet
& linked
)
334 if (linked
.insert(target
).second
) {
335 TargetDependSet
const& depends
= this->GetTargetDirectDepends(target
);
336 for (cmTargetDepend
const& di
: depends
) {
338 this->FillLinkClosure(di
, linked
);
344 cmGlobalVisualStudioGenerator::TargetSet
const&
345 cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmGeneratorTarget
* target
)
347 auto i
= this->TargetLinkClosure
.find(target
);
348 if (i
== this->TargetLinkClosure
.end()) {
349 TargetSetMap::value_type
entry(target
, TargetSet());
350 i
= this->TargetLinkClosure
.insert(entry
).first
;
351 this->FillLinkClosure(target
, i
->second
);
356 void cmGlobalVisualStudioGenerator::FollowLinkDepends(
357 const cmGeneratorTarget
* target
, std::set
<const cmGeneratorTarget
*>& linked
)
359 if (!target
->IsInBuildSystem()) {
362 if (linked
.insert(target
).second
&&
363 target
->GetType() == cmStateEnums::STATIC_LIBRARY
) {
364 // Static library targets do not list their link dependencies so
365 // we must follow them transitively now.
366 TargetDependSet
const& depends
= this->GetTargetDirectDepends(target
);
367 for (cmTargetDepend
const& di
: depends
) {
369 this->FollowLinkDepends(di
, linked
);
375 bool cmGlobalVisualStudioGenerator::ComputeTargetDepends()
377 if (!this->cmGlobalGenerator::ComputeTargetDepends()) {
380 for (auto const& it
: this->ProjectMap
) {
381 for (const cmLocalGenerator
* i
: it
.second
) {
382 for (const auto& ti
: i
->GetGeneratorTargets()) {
383 this->ComputeVSTargetDepends(ti
.get());
390 static bool VSLinkable(cmGeneratorTarget
const* t
)
392 return t
->IsLinkable() || t
->GetType() == cmStateEnums::OBJECT_LIBRARY
;
395 void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(
396 cmGeneratorTarget
* target
)
398 if (this->VSTargetDepends
.find(target
) != this->VSTargetDepends
.end()) {
401 VSDependSet
& vsTargetDepend
= this->VSTargetDepends
[target
];
402 // VS <= 7.1 has two behaviors that affect solution dependencies.
404 // (1) Solution-level dependencies between a linkable target and a
405 // library cause that library to be linked. We use an intermedite
406 // empty utility target to express the dependency. (VS 8 and above
407 // provide a project file "LinkLibraryDependencies" setting to
408 // choose whether to activate this behavior. We disable it except
409 // when linking external project files.)
411 // (2) We cannot let static libraries depend directly on targets to
412 // which they "link" because the librarian tool will copy the
413 // targets into the static library. While the work-around for
414 // behavior (1) would also avoid this, it would create a large
415 // number of extra utility targets for little gain. Instead, use
416 // the above work-around only for dependencies explicitly added by
417 // the add_dependencies() command. Approximate link dependencies by
418 // leaving them out for the static library itself but following them
419 // transitively for other targets.
421 bool allowLinkable
= (target
->GetType() != cmStateEnums::STATIC_LIBRARY
&&
422 target
->GetType() != cmStateEnums::SHARED_LIBRARY
&&
423 target
->GetType() != cmStateEnums::MODULE_LIBRARY
&&
424 target
->GetType() != cmStateEnums::EXECUTABLE
);
426 TargetDependSet
const& depends
= this->GetTargetDirectDepends(target
);
428 // Collect implicit link dependencies (target_link_libraries).
429 // Static libraries cannot depend on their link implementation
430 // due to behavior (2), but they do not really need to.
431 std::set
<cmGeneratorTarget
const*> linkDepends
;
432 if (target
->GetType() != cmStateEnums::STATIC_LIBRARY
) {
433 for (cmTargetDepend
const& di
: depends
) {
435 this->FollowLinkDepends(di
, linkDepends
);
440 // Collect explicit util dependencies (add_dependencies).
441 std::set
<cmGeneratorTarget
const*> utilDepends
;
442 for (cmTargetDepend
const& di
: depends
) {
444 this->FollowLinkDepends(di
, utilDepends
);
448 // Collect all targets linked by this target so we can avoid
449 // intermediate targets below.
451 if (target
->GetType() != cmStateEnums::STATIC_LIBRARY
) {
452 linked
= this->GetTargetLinkClosure(target
);
455 // Emit link dependencies.
456 for (cmGeneratorTarget
const* dep
: linkDepends
) {
457 vsTargetDepend
.insert(dep
->GetName());
460 // Emit util dependencies. Possibly use intermediate targets.
461 for (cmGeneratorTarget
const* dgt
: utilDepends
) {
462 if (allowLinkable
|| !VSLinkable(dgt
) || linked
.count(dgt
)) {
463 // Direct dependency allowed.
464 vsTargetDepend
.insert(dgt
->GetName());
466 // Direct dependency on linkable target not allowed.
467 // Use an intermediate utility target.
468 vsTargetDepend
.insert(this->GetUtilityDepend(dgt
));
473 bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile
* mf
)
475 // Visual Studio generators know how to lookup their build tool
476 // directly instead of needing a helper module to do it, so we
477 // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
478 if (mf
->GetDefinition("CMAKE_MAKE_PROGRAM").IsOff()) {
479 mf
->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetVSMakeProgram());
484 std::string
cmGlobalVisualStudioGenerator::GetUtilityDepend(
485 cmGeneratorTarget
const* target
)
487 auto i
= this->UtilityDepends
.find(target
);
488 if (i
== this->UtilityDepends
.end()) {
489 std::string name
= this->WriteUtilityDepend(target
);
490 UtilityDependsMap::value_type
entry(target
, name
);
491 i
= this->UtilityDepends
.insert(entry
).first
;
496 std::string
cmGlobalVisualStudioGenerator::GetStartupProjectName(
497 cmLocalGenerator
const* root
) const
499 cmValue n
= root
->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
501 std::string startup
= *n
;
502 if (this->FindTarget(startup
)) {
505 root
->GetMakefile()->IssueMessage(
506 MessageType::AUTHOR_WARNING
,
507 cmStrCat("Directory property VS_STARTUP_PROJECT specifies target "
509 startup
, "' that does not exist. Ignoring."));
512 // default, if not specified
513 return this->GetAllTargetName();
516 bool IsVisualStudioMacrosFileRegistered(const std::string
& macrosFile
,
517 const std::string
& regKeyBase
,
518 std::string
& nextAvailableSubKeyName
)
520 bool macrosRegistered
= false;
525 // Make lowercase local copies, convert to Unix slashes, and
526 // see if the resulting strings are the same:
527 s1
= cmSystemTools::LowerCase(macrosFile
);
528 cmSystemTools::ConvertToUnixSlashes(s1
);
532 LONG result
= ERROR_SUCCESS
;
535 keyname
= cmStrCat(regKeyBase
, "\\OtherProjects7");
538 RegOpenKeyExW(HKEY_CURRENT_USER
, cmsys::Encoding::ToWide(keyname
).c_str(),
540 if (ERROR_SUCCESS
== result
) {
541 // Iterate the subkeys and look for the values of interest in each subkey:
542 wchar_t subkeyname
[256];
543 DWORD cch_subkeyname
= cm::size(subkeyname
);
544 wchar_t keyclass
[256];
545 DWORD cch_keyclass
= cm::size(keyclass
);
546 FILETIME lastWriteTime
;
547 lastWriteTime
.dwHighDateTime
= 0;
548 lastWriteTime
.dwLowDateTime
= 0;
550 while (ERROR_SUCCESS
==
551 RegEnumKeyExW(hkey
, index
, subkeyname
, &cch_subkeyname
, 0, keyclass
,
552 &cch_keyclass
, &lastWriteTime
)) {
553 // Open the subkey and query the values of interest:
554 HKEY hsubkey
= nullptr;
555 result
= RegOpenKeyExW(hkey
, subkeyname
, 0, KEY_READ
, &hsubkey
);
556 if (ERROR_SUCCESS
== result
) {
557 DWORD valueType
= REG_SZ
;
559 DWORD cch_data1
= sizeof(data1
);
560 RegQueryValueExW(hsubkey
, L
"Path", 0, &valueType
, (LPBYTE
)data1
,
564 DWORD cch_data2
= sizeof(data2
);
565 RegQueryValueExW(hsubkey
, L
"Security", 0, &valueType
, (LPBYTE
)&data2
,
569 DWORD cch_data3
= sizeof(data3
);
570 RegQueryValueExW(hsubkey
, L
"StorageFormat", 0, &valueType
,
571 (LPBYTE
)&data3
, &cch_data3
);
573 s2
= cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1
));
574 cmSystemTools::ConvertToUnixSlashes(s2
);
576 macrosRegistered
= true;
579 std::string fullname
= cmsys::Encoding::ToNarrow(data1
);
580 std::string filename
;
581 std::string filepath
;
582 std::string filepathname
;
583 std::string filepathpath
;
584 if (cmSystemTools::FileExists(fullname
)) {
585 filename
= cmSystemTools::GetFilenameName(fullname
);
586 filepath
= cmSystemTools::GetFilenamePath(fullname
);
587 filepathname
= cmSystemTools::GetFilenameName(filepath
);
588 filepathpath
= cmSystemTools::GetFilenamePath(filepath
);
591 // std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
592 // std::cout << " Path: " << data1 << std::endl;
593 // std::cout << " Security: " << data2 << std::endl;
594 // std::cout << " StorageFormat: " << data3 << std::endl;
595 // std::cout << " filename: " << filename << std::endl;
596 // std::cout << " filepath: " << filepath << std::endl;
597 // std::cout << " filepathname: " << filepathname << std::endl;
598 // std::cout << " filepathpath: " << filepathpath << std::endl;
599 // std::cout << std::endl;
601 RegCloseKey(hsubkey
);
603 std::cout
<< "error opening subkey: "
604 << cmsys::Encoding::ToNarrow(subkeyname
) << std::endl
;
605 std::cout
<< std::endl
;
609 cch_subkeyname
= cm::size(subkeyname
);
610 cch_keyclass
= cm::size(keyclass
);
611 lastWriteTime
.dwHighDateTime
= 0;
612 lastWriteTime
.dwLowDateTime
= 0;
617 std::cout
<< "error opening key: " << keyname
<< std::endl
;
618 std::cout
<< std::endl
;
621 // Pass back next available sub key name, assuming sub keys always
622 // follow the expected naming scheme. Expected naming scheme is that
623 // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
624 // as the name of the next subkey.
625 nextAvailableSubKeyName
= std::to_string(index
);
627 keyname
= cmStrCat(regKeyBase
, "\\RecordingProject7");
630 RegOpenKeyExW(HKEY_CURRENT_USER
, cmsys::Encoding::ToWide(keyname
).c_str(),
632 if (ERROR_SUCCESS
== result
) {
633 DWORD valueType
= REG_SZ
;
635 DWORD cch_data1
= sizeof(data1
);
636 RegQueryValueExW(hkey
, L
"Path", 0, &valueType
, (LPBYTE
)data1
, &cch_data1
);
639 DWORD cch_data2
= sizeof(data2
);
640 RegQueryValueExW(hkey
, L
"Security", 0, &valueType
, (LPBYTE
)&data2
,
644 DWORD cch_data3
= sizeof(data3
);
645 RegQueryValueExW(hkey
, L
"StorageFormat", 0, &valueType
, (LPBYTE
)&data3
,
648 s2
= cmSystemTools::LowerCase(cmsys::Encoding::ToNarrow(data1
));
649 cmSystemTools::ConvertToUnixSlashes(s2
);
651 macrosRegistered
= true;
654 // std::cout << keyname << ":" << std::endl;
655 // std::cout << " Path: " << data1 << std::endl;
656 // std::cout << " Security: " << data2 << std::endl;
657 // std::cout << " StorageFormat: " << data3 << std::endl;
658 // std::cout << std::endl;
662 std::cout
<< "error opening key: " << keyname
<< std::endl
;
663 std::cout
<< std::endl
;
666 return macrosRegistered
;
669 void WriteVSMacrosFileRegistryEntry(const std::string
& nextAvailableSubKeyName
,
670 const std::string
& macrosFile
,
671 const std::string
& regKeyBase
)
673 std::string keyname
= cmStrCat(regKeyBase
, "\\OtherProjects7");
676 RegOpenKeyExW(HKEY_CURRENT_USER
, cmsys::Encoding::ToWide(keyname
).c_str(),
677 0, KEY_READ
| KEY_WRITE
, &hkey
);
678 if (ERROR_SUCCESS
== result
) {
679 // Create the subkey and set the values of interest:
680 HKEY hsubkey
= nullptr;
681 wchar_t lpClass
[] = L
"";
682 result
= RegCreateKeyExW(
683 hkey
, cmsys::Encoding::ToWide(nextAvailableSubKeyName
).c_str(), 0,
684 lpClass
, 0, KEY_READ
| KEY_WRITE
, 0, &hsubkey
, 0);
685 if (ERROR_SUCCESS
== result
) {
688 std::string
s(macrosFile
);
689 std::replace(s
.begin(), s
.end(), '/', '\\');
690 std::wstring ws
= cmsys::Encoding::ToWide(s
);
693 RegSetValueExW(hsubkey
, L
"Path", 0, REG_SZ
, (LPBYTE
)ws
.c_str(),
694 static_cast<DWORD
>(ws
.size() + 1) * sizeof(wchar_t));
695 if (ERROR_SUCCESS
!= result
) {
696 std::cout
<< "error result 1: " << result
<< std::endl
;
697 std::cout
<< std::endl
;
700 // Security value is always "1" for sample macros files (seems to be "2"
701 // if you put the file somewhere outside the standard VSMacros folder)
703 result
= RegSetValueExW(hsubkey
, L
"Security", 0, REG_DWORD
, (LPBYTE
)&dw
,
705 if (ERROR_SUCCESS
!= result
) {
706 std::cout
<< "error result 2: " << result
<< std::endl
;
707 std::cout
<< std::endl
;
710 // StorageFormat value is always "0" for sample macros files
712 result
= RegSetValueExW(hsubkey
, L
"StorageFormat", 0, REG_DWORD
,
713 (LPBYTE
)&dw
, sizeof(DWORD
));
714 if (ERROR_SUCCESS
!= result
) {
715 std::cout
<< "error result 3: " << result
<< std::endl
;
716 std::cout
<< std::endl
;
719 RegCloseKey(hsubkey
);
721 std::cout
<< "error creating subkey: " << nextAvailableSubKeyName
723 std::cout
<< std::endl
;
727 std::cout
<< "error opening key: " << keyname
<< std::endl
;
728 std::cout
<< std::endl
;
732 void RegisterVisualStudioMacros(const std::string
& macrosFile
,
733 const std::string
& regKeyBase
)
735 bool macrosRegistered
;
736 std::string nextAvailableSubKeyName
;
738 macrosRegistered
= IsVisualStudioMacrosFileRegistered(
739 macrosFile
, regKeyBase
, nextAvailableSubKeyName
);
741 if (!macrosRegistered
) {
743 cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances("ALL");
745 // Only register the macros file if there are *no* instances of Visual
746 // Studio running. If we register it while one is running, first, it has
747 // no effect on the running instance; second, and worse, Visual Studio
748 // removes our newly added registration entry when it quits. Instead,
749 // emit a warning asking the user to exit all running Visual Studio
753 std::ostringstream oss
;
754 oss
<< "Could not register CMake's Visual Studio macros file '"
755 << CMAKE_VSMACROS_FILENAME
"' while Visual Studio is running."
756 << " Please exit all running instances of Visual Studio before"
757 << " continuing." << std::endl
759 << "CMake needs to register Visual Studio macros when its macros"
760 << " file is updated or when it detects that its current macros file"
761 << " is no longer registered with Visual Studio." << std::endl
;
762 cmSystemTools::Message(oss
.str(), "Warning");
764 // Count them again now that the warning is over. In the case of a GUI
765 // warning, the user may have gone to close Visual Studio and then come
766 // back to the CMake GUI and clicked ok on the above warning. If so,
767 // then register the macros *now* if the count is *now* 0...
769 count
= cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
772 // Also re-get the nextAvailableSubKeyName in case Visual Studio
773 // wrote out new registered macros information as it was exiting:
776 IsVisualStudioMacrosFileRegistered(macrosFile
, regKeyBase
,
777 nextAvailableSubKeyName
);
781 // Do another if check - 'count' may have changed inside the above if:
784 WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName
, macrosFile
,
789 bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(
790 cmGeneratorTarget
const* gt
)
792 // If there's only one source language, Fortran has to be used
793 // in order for the sources to compile.
794 std::set
<std::string
> languages
= gt
->GetAllConfigCompileLanguages();
795 // Consider an explicit linker language property, but *not* the
796 // computed linker language that may depend on linked targets.
797 // This allows the project to control the language choice in
798 // a target with none of its own sources, e.g. when also using
800 cmValue linkLang
= gt
->GetProperty("LINKER_LANGUAGE");
801 if (cmNonempty(linkLang
)) {
802 languages
.insert(*linkLang
);
805 // Intel Fortran .vfproj files do support the resource compiler.
806 languages
.erase("RC");
808 return languages
.size() == 1 && *languages
.begin() == "Fortran"_s
;
811 bool cmGlobalVisualStudioGenerator::IsInSolution(
812 const cmGeneratorTarget
* gt
) const
814 return gt
->IsInBuildSystem();
817 bool cmGlobalVisualStudioGenerator::IsDepInSolution(
818 const std::string
& targetName
) const
820 return !targetName
.empty();
823 bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
824 cmGeneratorTarget
const* l
, cmGeneratorTarget
const* r
) const
826 // Make sure a given named target is ordered first,
827 // e.g. to set ALL_BUILD as the default active project.
828 // When the empty string is named this is a no-op.
829 if (r
->GetName() == this->First
) {
832 if (l
->GetName() == this->First
) {
835 return l
->GetName() < r
->GetName();
838 cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
839 TargetDependSet
const& targets
, std::string
const& first
)
840 : derived(TargetCompare(first
))
842 this->insert(targets
.begin(), targets
.end());
845 cmGlobalVisualStudioGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
846 TargetSet
const& targets
, std::string
const& first
)
847 : derived(TargetCompare(first
))
849 for (cmGeneratorTarget
const* it
: targets
) {
854 std::string
cmGlobalVisualStudioGenerator::ExpandCFGIntDir(
855 const std::string
& str
, const std::string
& config
) const
857 std::string replace
= GetCMakeCFGIntDir();
859 std::string tmp
= str
;
860 for (std::string::size_type i
= tmp
.find(replace
); i
!= std::string::npos
;
861 i
= tmp
.find(replace
, i
)) {
862 tmp
.replace(i
, replace
.size(), config
);
868 void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
869 cmGeneratorTarget
* gt
, std::vector
<cmCustomCommand
>& commands
,
870 std::string
const& configName
)
872 cmGeneratorTarget::ModuleDefinitionInfo
const* mdi
=
873 gt
->GetModuleDefinitionInfo(configName
);
874 if (!mdi
|| !mdi
->DefFileGenerated
) {
878 std::vector
<std::string
> outputs
;
879 outputs
.push_back(mdi
->DefFile
);
880 std::vector
<std::string
> empty
;
881 std::vector
<cmSourceFile
const*> objectSources
;
882 gt
->GetObjectSources(objectSources
, configName
);
883 std::map
<cmSourceFile
const*, std::string
> mapping
;
884 for (cmSourceFile
const* it
: objectSources
) {
887 gt
->LocalGenerator
->ComputeObjectFilenames(mapping
, gt
);
888 std::string obj_dir
= gt
->ObjectDirectory
;
889 std::string cmakeCommand
= cmSystemTools::GetCMakeCommand();
890 std::string obj_dir_expanded
= obj_dir
;
891 cmSystemTools::ReplaceString(obj_dir_expanded
, this->GetCMakeCFGIntDir(),
893 cmSystemTools::MakeDirectory(obj_dir_expanded
);
894 std::string
const objs_file
= cmStrCat(obj_dir_expanded
, "/objects.txt");
895 cmGeneratedFileStream
fout(objs_file
.c_str());
897 cmSystemTools::Error(cmStrCat("could not open ", objs_file
));
901 if (mdi
->WindowsExportAllSymbols
) {
902 std::vector
<std::string
> objs
;
903 for (cmSourceFile
const* it
: objectSources
) {
904 // Find the object file name corresponding to this source file.
905 // It must exist because we populated the mapping just above.
906 const auto& v
= mapping
[it
];
908 std::string objFile
= cmStrCat(obj_dir
, v
);
909 objs
.push_back(objFile
);
911 std::vector
<cmSourceFile
const*> externalObjectSources
;
912 gt
->GetExternalObjects(externalObjectSources
, configName
);
913 for (cmSourceFile
const* it
: externalObjectSources
) {
914 objs
.push_back(it
->GetFullPath());
917 for (std::string
const& it
: objs
) {
918 std::string objFile
= it
;
919 // replace $(ConfigurationName) in the object names
920 cmSystemTools::ReplaceString(objFile
, this->GetCMakeCFGIntDir(),
922 if (cmHasLiteralSuffix(objFile
, ".obj")) {
923 fout
<< objFile
<< "\n";
928 for (cmSourceFile
const* i
: mdi
->Sources
) {
929 fout
<< i
->GetFullPath() << "\n";
932 cmCustomCommandLines commandLines
= cmMakeSingleCommandLine(
933 { cmakeCommand
, "-E", "__create_def", mdi
->DefFile
, objs_file
});
934 cmCustomCommand command
;
935 command
.SetOutputs(outputs
);
936 command
.SetCommandLines(commandLines
);
937 command
.SetComment("Auto build dll exports");
938 command
.SetBacktrace(gt
->Target
->GetMakefile()->GetBacktrace());
939 command
.SetWorkingDirectory(".");
940 command
.SetStdPipesUTF8(true);
941 commands
.push_back(std::move(command
));
944 static bool OpenSolution(std::string
const& sln
)
946 HRESULT comInitialized
=
947 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED
| COINIT_DISABLE_OLE1DDE
);
948 if (FAILED(comInitialized
)) {
952 HINSTANCE hi
= ShellExecuteA(nullptr, "open", sln
.c_str(), nullptr, nullptr,
957 return reinterpret_cast<intptr_t>(hi
) > 32;
960 bool cmGlobalVisualStudioGenerator::Open(const std::string
& bindir
,
961 const std::string
& projectName
,
964 std::string sln
= cmStrCat(bindir
, '/', projectName
, ".sln");
967 return cmSystemTools::FileExists(sln
, true);
970 sln
= cmSystemTools::ConvertToOutputPath(sln
);
972 return std::async(std::launch::async
, OpenSolution
, sln
).get();