1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmGlobalVisualStudioGenerator.cxx,v $
6 Date: $Date: 2009-07-14 18:16:46 $
7 Version: $Revision: 1.16 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmGlobalVisualStudioGenerator.h"
19 #include "cmCallVisualStudioMacro.h"
20 #include "cmLocalGenerator.h"
21 #include "cmMakefile.h"
24 //----------------------------------------------------------------------------
25 cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator()
29 //----------------------------------------------------------------------------
30 cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator()
34 //----------------------------------------------------------------------------
35 void cmGlobalVisualStudioGenerator::Generate()
37 // Add a special target that depends on ALL projects for easy build
38 // of one configuration only.
39 const char* no_working_dir
= 0;
40 std::vector
<std::string
> no_depends
;
41 cmCustomCommandLines no_commands
;
42 std::map
<cmStdString
, std::vector
<cmLocalGenerator
*> >::iterator it
;
43 for(it
= this->ProjectMap
.begin(); it
!= this->ProjectMap
.end(); ++it
)
45 std::vector
<cmLocalGenerator
*>& gen
= it
->second
;
46 // add the ALL_BUILD to the first local generator of each project
49 // Use no actual command lines so that the target itself is not
50 // considered always out of date.
52 gen
[0]->GetMakefile()->
53 AddUtilityCommand("ALL_BUILD", true, no_working_dir
,
54 no_depends
, no_commands
, false,
55 "Build all projects");
56 // Now make all targets depend on the ALL_BUILD target
58 for(std::vector
<cmLocalGenerator
*>::iterator i
= gen
.begin();
61 cmTargets
& targets
= (*i
)->GetMakefile()->GetTargets();
62 for(cmTargets::iterator t
= targets
.begin();
63 t
!= targets
.end(); ++t
)
65 if(!this->IsExcluded(gen
[0], t
->second
))
67 allBuild
->AddUtility(t
->second
.GetName());
74 // Fix utility dependencies to avoid linking to libraries.
75 this->FixUtilityDepends();
77 // Configure CMake Visual Studio macros, for this user on this version
79 this->ConfigureCMakeVisualStudioMacros();
81 // Run all the local generators.
82 this->cmGlobalGenerator::Generate();
85 //----------------------------------------------------------------------------
86 bool IsVisualStudioMacrosFileRegistered(const std::string
& macrosFile
,
87 const std::string
& regKeyBase
,
88 std::string
& nextAvailableSubKeyName
);
90 void RegisterVisualStudioMacros(const std::string
& macrosFile
,
91 const std::string
& regKeyBase
);
93 //----------------------------------------------------------------------------
94 #define CMAKE_VSMACROS_FILENAME \
95 "CMakeVSMacros2.vsmacros"
97 #define CMAKE_VSMACROS_RELOAD_MACRONAME \
98 "Macros.CMakeVSMacros2.Macros.ReloadProjects"
100 #define CMAKE_VSMACROS_STOP_MACRONAME \
101 "Macros.CMakeVSMacros2.Macros.StopBuild"
103 //----------------------------------------------------------------------------
104 void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
106 cmMakefile
* mf
= this->LocalGenerators
[0]->GetMakefile();
107 std::string dir
= this->GetUserMacrosDirectory();
109 if (mf
!= 0 && dir
!= "")
111 std::string src
= mf
->GetRequiredDefinition("CMAKE_ROOT");
112 src
+= "/Templates/" CMAKE_VSMACROS_FILENAME
;
114 std::string dst
= dir
+ "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
;
116 // Copy the macros file to the user directory only if the
117 // destination does not exist or the source location is newer.
118 // This will allow the user to edit the macros for development
119 // purposes but newer versions distributed with CMake will replace
120 // older versions in user directories.
122 if(!cmSystemTools::FileTimeCompare(src
.c_str(), dst
.c_str(), &res
) ||
125 if (!cmSystemTools::CopyFileAlways(src
.c_str(), dst
.c_str()))
127 std::ostringstream oss
;
128 oss
<< "Could not copy from: " << src
<< std::endl
;
129 oss
<< " to: " << dst
<< std::endl
;
130 cmSystemTools::Message(oss
.str().c_str(), "Warning");
134 RegisterVisualStudioMacros(dst
, this->GetUserMacrosRegKeyBase());
138 //----------------------------------------------------------------------------
140 cmGlobalVisualStudioGenerator
141 ::CallVisualStudioMacro(MacroName m
,
142 const char* vsSolutionFile
)
144 // If any solution or project files changed during the generation,
145 // tell Visual Studio to reload them...
146 cmMakefile
* mf
= this->LocalGenerators
[0]->GetMakefile();
147 std::string dir
= this->GetUserMacrosDirectory();
149 // Only really try to call the macro if:
151 // - there is a UserMacrosDirectory
152 // - the CMake vsmacros file exists
153 // - the CMake vsmacros file is registered
154 // - there were .sln/.vcproj files changed during generation
156 if (mf
!= 0 && dir
!= "")
158 std::string macrosFile
= dir
+ "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
;
159 std::string nextSubkeyName
;
160 if (cmSystemTools::FileExists(macrosFile
.c_str()) &&
161 IsVisualStudioMacrosFileRegistered(macrosFile
,
162 this->GetUserMacrosRegKeyBase(), nextSubkeyName
)
165 std::string topLevelSlnName
;
168 topLevelSlnName
= vsSolutionFile
;
172 topLevelSlnName
= mf
->GetStartOutputDirectory();
173 topLevelSlnName
+= "/";
174 topLevelSlnName
+= mf
->GetProjectName();
175 topLevelSlnName
+= ".sln";
180 std::vector
<std::string
> filenames
;
181 this->GetFilesReplacedDuringGenerate(filenames
);
182 if (filenames
.size() > 0)
184 // Convert vector to semi-colon delimited string of filenames:
185 std::string projects
;
186 std::vector
<std::string
>::iterator it
= filenames
.begin();
187 if (it
!= filenames
.end())
192 for (; it
!= filenames
.end(); ++it
)
197 cmCallVisualStudioMacro::CallMacro(topLevelSlnName
,
198 CMAKE_VSMACROS_RELOAD_MACRONAME
, projects
,
199 this->GetCMakeInstance()->GetDebugOutput());
202 else if(m
== MacroStop
)
204 cmCallVisualStudioMacro::CallMacro(topLevelSlnName
,
205 CMAKE_VSMACROS_STOP_MACRONAME
, "",
206 this->GetCMakeInstance()->GetDebugOutput());
212 //----------------------------------------------------------------------------
213 std::string
cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
218 //----------------------------------------------------------------------------
219 std::string
cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
224 //----------------------------------------------------------------------------
225 void cmGlobalVisualStudioGenerator::FixUtilityDepends()
227 // Skip for VS versions 8 and above.
228 if(!this->VSLinksDependencies())
233 // For VS versions before 8:
235 // When a target that links contains a project-level dependency on a
236 // library target that library is automatically linked. In order to
237 // allow utility-style project-level dependencies that do not
238 // actually link we need to automatically insert an intermediate
241 // Here we edit the utility dependencies of a target to add the
242 // intermediate custom target when necessary.
243 for(unsigned i
= 0; i
< this->LocalGenerators
.size(); ++i
)
246 &(this->LocalGenerators
[i
]->GetMakefile()->GetTargets());
247 for(cmTargets::iterator tarIt
= targets
->begin();
248 tarIt
!= targets
->end(); ++tarIt
)
250 this->FixUtilityDependsForTarget(tarIt
->second
);
255 //----------------------------------------------------------------------------
257 cmGlobalVisualStudioGenerator::FixUtilityDependsForTarget(cmTarget
& target
)
259 // Only targets that link need to be fixed.
260 if(target
.GetType() != cmTarget::STATIC_LIBRARY
&&
261 target
.GetType() != cmTarget::SHARED_LIBRARY
&&
262 target
.GetType() != cmTarget::MODULE_LIBRARY
&&
263 target
.GetType() != cmTarget::EXECUTABLE
)
269 // This feature makes a mess in SLN files for VS 7.1 and below. It
270 // creates an extra target for every target that is "linked" by a
271 // static library. Without this feature static libraries do not
272 // wait until their "link" dependencies are built to build. This is
273 // not a problem 99.9% of the time, and projects that do have the
274 // problem can enable this work-around by using add_dependencies.
276 // Static libraries cannot depend directly on the targets to which
277 // they link because VS will copy those targets into the library
278 // (for VS < 8). To work around the problem we copy the
279 // dependencies to be utility dependencies so that the work-around
281 if(target
.GetType() == cmTarget::STATIC_LIBRARY
)
283 cmTarget::LinkLibraryVectorType
const& libs
= target
.GetLinkLibraries();
284 for(cmTarget::LinkLibraryVectorType::const_iterator i
= libs
.begin();
285 i
!= libs
.end(); ++i
)
287 if(cmTarget
* depTarget
= this->FindTarget(0, i
->first
.c_str(), false))
289 target
.AddUtility(depTarget
->GetName());
295 // Look at each utility dependency.
296 for(std::set
<cmStdString
>::const_iterator ui
=
297 target
.GetUtilities().begin();
298 ui
!= target
.GetUtilities().end(); ++ui
)
300 if(cmTarget
* depTarget
= this->FindTarget(0, ui
->c_str()))
302 if(depTarget
->GetType() == cmTarget::STATIC_LIBRARY
||
303 depTarget
->GetType() == cmTarget::SHARED_LIBRARY
||
304 depTarget
->GetType() == cmTarget::MODULE_LIBRARY
)
306 // This utility dependency will cause an attempt to link. If
307 // the depender does not already link the dependee we need an
308 // intermediate target.
309 if(!this->CheckTargetLinks(target
, ui
->c_str()))
311 this->CreateUtilityDependTarget(*depTarget
);
318 //----------------------------------------------------------------------------
320 cmGlobalVisualStudioGenerator::CreateUtilityDependTarget(cmTarget
& target
)
322 // This target is a library on which a utility dependency exists.
323 // We need to create an intermediate custom target to hook up the
324 // dependency without causing a link.
325 const char* altName
= target
.GetProperty("ALTERNATIVE_DEPENDENCY_NAME");
328 // Create the intermediate utility target.
329 std::string altNameStr
= target
.GetName();
330 altNameStr
+= "_UTILITY";
331 const std::vector
<std::string
> no_depends
;
332 cmCustomCommandLines no_commands
;
333 const char* no_working_dir
= 0;
334 const char* no_comment
= 0;
335 target
.GetMakefile()->AddUtilityCommand(altNameStr
.c_str(), true,
336 no_working_dir
, no_depends
,
337 no_commands
, false, no_comment
);
338 target
.SetProperty("ALTERNATIVE_DEPENDENCY_NAME", altNameStr
.c_str());
340 // Most targets have a GUID created in ConfigureFinalPass. Since
341 // that has already been called, create one for this target now.
342 this->CreateGUID(altNameStr
.c_str());
344 // The intermediate target should depend on the original target.
345 if(cmTarget
* alt
= this->FindTarget(0, altNameStr
.c_str()))
347 alt
->AddUtility(target
.GetName());
352 //----------------------------------------------------------------------------
353 bool cmGlobalVisualStudioGenerator::CheckTargetLinks(cmTarget
& target
,
356 // Return whether the given target links to a target with the given name.
357 if(target
.GetType() == cmTarget::STATIC_LIBRARY
)
359 // Static libraries never link to anything.
362 cmTarget::LinkLibraryVectorType
const& libs
= target
.GetLinkLibraries();
363 for(cmTarget::LinkLibraryVectorType::const_iterator i
= libs
.begin();
364 i
!= libs
.end(); ++i
)
374 //----------------------------------------------------------------------------
376 cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget
& target
,
379 // Possibly depend on an intermediate utility target to avoid
381 if(target
.GetType() == cmTarget::STATIC_LIBRARY
||
382 target
.GetType() == cmTarget::SHARED_LIBRARY
||
383 target
.GetType() == cmTarget::MODULE_LIBRARY
||
384 target
.GetType() == cmTarget::EXECUTABLE
)
386 // The depender is a target that links. Lookup the dependee to
387 // see if it provides an alternative dependency name.
388 if(cmTarget
* depTarget
= this->FindTarget(0, name
))
390 // Check for an alternative name created by FixUtilityDepends.
391 if(const char* altName
=
392 depTarget
->GetProperty("ALTERNATIVE_DEPENDENCY_NAME"))
394 // The alternative name is needed only if the depender does
395 // not really link to the dependee.
396 if(!this->CheckTargetLinks(target
, name
))
404 // No special case. Just use the original dependency name.
408 //----------------------------------------------------------------------------
411 //----------------------------------------------------------------------------
412 bool IsVisualStudioMacrosFileRegistered(const std::string
& macrosFile
,
413 const std::string
& regKeyBase
,
414 std::string
& nextAvailableSubKeyName
)
416 bool macrosRegistered
= false;
421 // Make lowercase local copies, convert to Unix slashes, and
422 // see if the resulting strings are the same:
423 s1
= cmSystemTools::LowerCase(macrosFile
);
424 cmSystemTools::ConvertToUnixSlashes(s1
);
428 LONG result
= ERROR_SUCCESS
;
431 keyname
= regKeyBase
+ "\\OtherProjects7";
433 result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(),
435 if (ERROR_SUCCESS
== result
)
437 // Iterate the subkeys and look for the values of interest in each subkey:
438 CHAR subkeyname
[256];
439 DWORD cch_subkeyname
= sizeof(subkeyname
)/sizeof(subkeyname
[0]);
441 DWORD cch_keyclass
= sizeof(keyclass
)/sizeof(keyclass
[0]);
442 FILETIME lastWriteTime
;
443 lastWriteTime
.dwHighDateTime
= 0;
444 lastWriteTime
.dwLowDateTime
= 0;
446 while (ERROR_SUCCESS
== RegEnumKeyEx(hkey
, index
, subkeyname
,
448 0, keyclass
, &cch_keyclass
, &lastWriteTime
))
450 // Open the subkey and query the values of interest:
452 result
= RegOpenKeyEx(hkey
, subkeyname
, 0, KEY_READ
, &hsubkey
);
453 if (ERROR_SUCCESS
== result
)
455 DWORD valueType
= REG_SZ
;
457 DWORD cch_data1
= sizeof(data1
)/sizeof(data1
[0]);
458 RegQueryValueEx(hsubkey
, "Path", 0, &valueType
,
459 (LPBYTE
) &data1
[0], &cch_data1
);
462 DWORD cch_data2
= sizeof(data2
);
463 RegQueryValueEx(hsubkey
, "Security", 0, &valueType
,
464 (LPBYTE
) &data2
, &cch_data2
);
467 DWORD cch_data3
= sizeof(data3
);
468 RegQueryValueEx(hsubkey
, "StorageFormat", 0, &valueType
,
469 (LPBYTE
) &data3
, &cch_data3
);
471 s2
= cmSystemTools::LowerCase(data1
);
472 cmSystemTools::ConvertToUnixSlashes(s2
);
475 macrosRegistered
= true;
478 std::string
fullname(data1
);
479 std::string filename
;
480 std::string filepath
;
481 std::string filepathname
;
482 std::string filepathpath
;
483 if (cmSystemTools::FileExists(fullname
.c_str()))
485 filename
= cmSystemTools::GetFilenameName(fullname
);
486 filepath
= cmSystemTools::GetFilenamePath(fullname
);
487 filepathname
= cmSystemTools::GetFilenameName(filepath
);
488 filepathpath
= cmSystemTools::GetFilenamePath(filepath
);
491 //std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
492 //std::cout << " Path: " << data1 << std::endl;
493 //std::cout << " Security: " << data2 << std::endl;
494 //std::cout << " StorageFormat: " << data3 << std::endl;
495 //std::cout << " filename: " << filename << std::endl;
496 //std::cout << " filepath: " << filepath << std::endl;
497 //std::cout << " filepathname: " << filepathname << std::endl;
498 //std::cout << " filepathpath: " << filepathpath << std::endl;
499 //std::cout << std::endl;
501 RegCloseKey(hsubkey
);
505 std::cout
<< "error opening subkey: " << subkeyname
<< std::endl
;
506 std::cout
<< std::endl
;
510 cch_subkeyname
= sizeof(subkeyname
)/sizeof(subkeyname
[0]);
511 cch_keyclass
= sizeof(keyclass
)/sizeof(keyclass
[0]);
512 lastWriteTime
.dwHighDateTime
= 0;
513 lastWriteTime
.dwLowDateTime
= 0;
520 std::cout
<< "error opening key: " << keyname
<< std::endl
;
521 std::cout
<< std::endl
;
525 // Pass back next available sub key name, assuming sub keys always
526 // follow the expected naming scheme. Expected naming scheme is that
527 // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
528 // as the name of the next subkey.
529 std::ostringstream ossNext
;
531 nextAvailableSubKeyName
= ossNext
.str();
534 keyname
= regKeyBase
+ "\\RecordingProject7";
536 result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(),
538 if (ERROR_SUCCESS
== result
)
540 DWORD valueType
= REG_SZ
;
542 DWORD cch_data1
= sizeof(data1
)/sizeof(data1
[0]);
543 RegQueryValueEx(hkey
, "Path", 0, &valueType
,
544 (LPBYTE
) &data1
[0], &cch_data1
);
547 DWORD cch_data2
= sizeof(data2
);
548 RegQueryValueEx(hkey
, "Security", 0, &valueType
,
549 (LPBYTE
) &data2
, &cch_data2
);
552 DWORD cch_data3
= sizeof(data3
);
553 RegQueryValueEx(hkey
, "StorageFormat", 0, &valueType
,
554 (LPBYTE
) &data3
, &cch_data3
);
556 s2
= cmSystemTools::LowerCase(data1
);
557 cmSystemTools::ConvertToUnixSlashes(s2
);
560 macrosRegistered
= true;
563 //std::cout << keyname << ":" << std::endl;
564 //std::cout << " Path: " << data1 << std::endl;
565 //std::cout << " Security: " << data2 << std::endl;
566 //std::cout << " StorageFormat: " << data3 << std::endl;
567 //std::cout << std::endl;
573 std::cout
<< "error opening key: " << keyname
<< std::endl
;
574 std::cout
<< std::endl
;
577 return macrosRegistered
;
580 //----------------------------------------------------------------------------
581 void WriteVSMacrosFileRegistryEntry(
582 const std::string
& nextAvailableSubKeyName
,
583 const std::string
& macrosFile
,
584 const std::string
& regKeyBase
)
586 std::string keyname
= regKeyBase
+ "\\OtherProjects7";
588 LONG result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(), 0,
589 KEY_READ
|KEY_WRITE
, &hkey
);
590 if (ERROR_SUCCESS
== result
)
592 // Create the subkey and set the values of interest:
594 result
= RegCreateKeyEx(hkey
, nextAvailableSubKeyName
.c_str(), 0, "", 0,
595 KEY_READ
|KEY_WRITE
, 0, &hsubkey
, 0);
596 if (ERROR_SUCCESS
== result
)
600 std::string
s(macrosFile
);
601 cmSystemTools::ReplaceString(s
, "/", "\\");
603 result
= RegSetValueEx(hsubkey
, "Path", 0, REG_SZ
, (LPBYTE
) s
.c_str(),
604 static_cast<DWORD
>(strlen(s
.c_str()) + 1));
605 if (ERROR_SUCCESS
!= result
)
607 std::cout
<< "error result 1: " << result
<< std::endl
;
608 std::cout
<< std::endl
;
611 // Security value is always "1" for sample macros files (seems to be "2"
612 // if you put the file somewhere outside the standard VSMacros folder)
614 result
= RegSetValueEx(hsubkey
, "Security",
615 0, REG_DWORD
, (LPBYTE
) &dw
, sizeof(DWORD
));
616 if (ERROR_SUCCESS
!= result
)
618 std::cout
<< "error result 2: " << result
<< std::endl
;
619 std::cout
<< std::endl
;
622 // StorageFormat value is always "0" for sample macros files
624 result
= RegSetValueEx(hsubkey
, "StorageFormat",
625 0, REG_DWORD
, (LPBYTE
) &dw
, sizeof(DWORD
));
626 if (ERROR_SUCCESS
!= result
)
628 std::cout
<< "error result 3: " << result
<< std::endl
;
629 std::cout
<< std::endl
;
632 RegCloseKey(hsubkey
);
636 std::cout
<< "error creating subkey: "
637 << nextAvailableSubKeyName
<< std::endl
;
638 std::cout
<< std::endl
;
644 std::cout
<< "error opening key: " << keyname
<< std::endl
;
645 std::cout
<< std::endl
;
649 //----------------------------------------------------------------------------
650 void RegisterVisualStudioMacros(const std::string
& macrosFile
,
651 const std::string
& regKeyBase
)
653 bool macrosRegistered
;
654 std::string nextAvailableSubKeyName
;
656 macrosRegistered
= IsVisualStudioMacrosFileRegistered(macrosFile
,
657 regKeyBase
, nextAvailableSubKeyName
);
659 if (!macrosRegistered
)
661 int count
= cmCallVisualStudioMacro::
662 GetNumberOfRunningVisualStudioInstances("ALL");
664 // Only register the macros file if there are *no* instances of Visual
665 // Studio running. If we register it while one is running, first, it has
666 // no effect on the running instance; second, and worse, Visual Studio
667 // removes our newly added registration entry when it quits. Instead,
668 // emit a warning asking the user to exit all running Visual Studio
673 std::ostringstream oss
;
674 oss
<< "Could not register CMake's Visual Studio macros file '"
675 << CMAKE_VSMACROS_FILENAME
"' while Visual Studio is running."
676 << " Please exit all running instances of Visual Studio before"
677 << " continuing." << std::endl
679 << "CMake needs to register Visual Studio macros when its macros"
680 << " file is updated or when it detects that its current macros file"
681 << " is no longer registered with Visual Studio."
683 cmSystemTools::Message(oss
.str().c_str(), "Warning");
685 // Count them again now that the warning is over. In the case of a GUI
686 // warning, the user may have gone to close Visual Studio and then come
687 // back to the CMake GUI and clicked ok on the above warning. If so,
688 // then register the macros *now* if the count is *now* 0...
690 count
= cmCallVisualStudioMacro::
691 GetNumberOfRunningVisualStudioInstances("ALL");
693 // Also re-get the nextAvailableSubKeyName in case Visual Studio
694 // wrote out new registered macros information as it was exiting:
698 IsVisualStudioMacrosFileRegistered(macrosFile
, regKeyBase
,
699 nextAvailableSubKeyName
);
703 // Do another if check - 'count' may have changed inside the above if:
707 WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName
, macrosFile
,
712 bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(cmTarget
& target
)
714 // check to see if this is a fortran build
715 std::set
<cmStdString
> languages
;
716 target
.GetLanguages(languages
);
717 if(languages
.size() == 1)
719 if(*languages
.begin() == "Fortran")