1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmGlobalVisualStudioGenerator.cxx,v $
6 Date: $Date: 2007-12-21 20:04:06 $
7 Version: $Revision: 1.9 $
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.
51 gen
[0]->GetMakefile()->
52 AddUtilityCommand("ALL_BUILD", true, no_working_dir
,
53 no_depends
, no_commands
, false,
54 "Build all projects");
58 // Fix utility dependencies to avoid linking to libraries.
59 this->FixUtilityDepends();
61 // Configure CMake Visual Studio macros, for this user on this version
63 this->ConfigureCMakeVisualStudioMacros();
65 // Run all the local generators.
66 this->cmGlobalGenerator::Generate();
69 //----------------------------------------------------------------------------
70 bool IsVisualStudioMacrosFileRegistered(const std::string
& macrosFile
,
71 std::string
& nextAvailableSubKeyName
);
73 void RegisterVisualStudioMacros(const std::string
& macrosFile
);
75 //----------------------------------------------------------------------------
76 #define CMAKE_VSMACROS_FILENAME \
77 "CMakeVSMacros1.vsmacros"
79 #define CMAKE_VSMACROS_RELOAD_MACRONAME \
80 "Macros.CMakeVSMacros1.Macros.ReloadProjects"
82 #define CMAKE_VSMACROS_STOP_MACRONAME \
83 "Macros.CMakeVSMacros1.Macros.StopBuild"
85 //----------------------------------------------------------------------------
86 void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
88 cmMakefile
* mf
= this->LocalGenerators
[0]->GetMakefile();
89 std::string dir
= this->GetUserMacrosDirectory();
91 if (mf
!= 0 && dir
!= "")
93 std::string src
= mf
->GetRequiredDefinition("CMAKE_ROOT");
94 src
+= "/Templates/" CMAKE_VSMACROS_FILENAME
;
96 std::string dst
= dir
+ "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
;
98 // Copy the macros file to the user directory only if the
99 // destination does not exist or the source location is newer.
100 // This will allow the user to edit the macros for development
101 // purposes but newer versions distributed with CMake will replace
102 // older versions in user directories.
104 if(!cmSystemTools::FileTimeCompare(src
.c_str(), dst
.c_str(), &res
) ||
107 if (!cmSystemTools::CopyFileAlways(src
.c_str(), dst
.c_str()))
109 std::ostringstream oss
;
110 oss
<< "Could not copy from: " << src
<< std::endl
;
111 oss
<< " to: " << dst
<< std::endl
;
112 cmSystemTools::Message(oss
.str().c_str(), "Warning");
116 RegisterVisualStudioMacros(dst
);
120 //----------------------------------------------------------------------------
122 cmGlobalVisualStudioGenerator
123 ::CallVisualStudioMacro(MacroName m
,
124 const char* vsSolutionFile
)
126 // If any solution or project files changed during the generation,
127 // tell Visual Studio to reload them...
128 cmMakefile
* mf
= this->LocalGenerators
[0]->GetMakefile();
129 std::string dir
= this->GetUserMacrosDirectory();
131 // Only really try to call the macro if:
133 // - there is a UserMacrosDirectory
134 // - the CMake vsmacros file exists
135 // - the CMake vsmacros file is registered
136 // - there were .sln/.vcproj files changed during generation
138 if (mf
!= 0 && dir
!= "")
140 std::string macrosFile
= dir
+ "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
;
141 std::string nextSubkeyName
;
142 if (cmSystemTools::FileExists(macrosFile
.c_str()) &&
143 IsVisualStudioMacrosFileRegistered(macrosFile
, nextSubkeyName
)
146 std::string topLevelSlnName
;
149 topLevelSlnName
= vsSolutionFile
;
153 topLevelSlnName
= mf
->GetStartOutputDirectory();
154 topLevelSlnName
+= "/";
155 topLevelSlnName
+= mf
->GetProjectName();
156 topLevelSlnName
+= ".sln";
161 std::vector
<std::string
> filenames
;
162 this->GetFilesReplacedDuringGenerate(filenames
);
163 if (filenames
.size() > 0)
165 // Convert vector to semi-colon delimited string of filenames:
166 std::string projects
;
167 std::vector
<std::string
>::iterator it
= filenames
.begin();
168 if (it
!= filenames
.end())
173 for (; it
!= filenames
.end(); ++it
)
178 cmCallVisualStudioMacro::CallMacro
179 (topLevelSlnName
, CMAKE_VSMACROS_RELOAD_MACRONAME
, projects
);
182 else if(m
== MacroStop
)
184 cmCallVisualStudioMacro::CallMacro(topLevelSlnName
,
185 CMAKE_VSMACROS_STOP_MACRONAME
, "");
191 //----------------------------------------------------------------------------
192 std::string
cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
197 //----------------------------------------------------------------------------
198 void cmGlobalVisualStudioGenerator::FixUtilityDepends()
200 // Skip for VS versions 8 and above.
201 if(!this->VSLinksDependencies())
206 // For VS versions before 8:
208 // When a target that links contains a project-level dependency on a
209 // library target that library is automatically linked. In order to
210 // allow utility-style project-level dependencies that do not
211 // actually link we need to automatically insert an intermediate
214 // Here we edit the utility dependencies of a target to add the
215 // intermediate custom target when necessary.
216 for(unsigned i
= 0; i
< this->LocalGenerators
.size(); ++i
)
219 &(this->LocalGenerators
[i
]->GetMakefile()->GetTargets());
220 for(cmTargets::iterator tarIt
= targets
->begin();
221 tarIt
!= targets
->end(); ++tarIt
)
223 this->FixUtilityDependsForTarget(tarIt
->second
);
228 //----------------------------------------------------------------------------
230 cmGlobalVisualStudioGenerator::FixUtilityDependsForTarget(cmTarget
& target
)
232 // Only targets that link need to be fixed.
233 if(target
.GetType() != cmTarget::STATIC_LIBRARY
&&
234 target
.GetType() != cmTarget::SHARED_LIBRARY
&&
235 target
.GetType() != cmTarget::MODULE_LIBRARY
&&
236 target
.GetType() != cmTarget::EXECUTABLE
)
242 // This feature makes a mess in SLN files for VS 7.1 and below. It
243 // creates an extra target for every target that is "linked" by a
244 // static library. Without this feature static libraries do not
245 // wait until their "link" dependencies are built to build. This is
246 // not a problem 99.9% of the time, and projects that do have the
247 // problem can enable this work-around by using add_dependencies.
249 // Static libraries cannot depend directly on the targets to which
250 // they link because VS will copy those targets into the library
251 // (for VS < 8). To work around the problem we copy the
252 // dependencies to be utility dependencies so that the work-around
254 if(target
.GetType() == cmTarget::STATIC_LIBRARY
)
256 cmTarget::LinkLibraryVectorType
const& libs
= target
.GetLinkLibraries();
257 for(cmTarget::LinkLibraryVectorType::const_iterator i
= libs
.begin();
258 i
!= libs
.end(); ++i
)
260 if(cmTarget
* depTarget
= this->FindTarget(0, i
->first
.c_str(), false))
262 target
.AddUtility(depTarget
->GetName());
268 // Look at each utility dependency.
269 for(std::set
<cmStdString
>::const_iterator ui
=
270 target
.GetUtilities().begin();
271 ui
!= target
.GetUtilities().end(); ++ui
)
273 if(cmTarget
* depTarget
= this->FindTarget(0, ui
->c_str(), false))
275 if(depTarget
->GetType() == cmTarget::STATIC_LIBRARY
||
276 depTarget
->GetType() == cmTarget::SHARED_LIBRARY
||
277 depTarget
->GetType() == cmTarget::MODULE_LIBRARY
)
279 // This utility dependency will cause an attempt to link. If
280 // the depender does not already link the dependee we need an
281 // intermediate target.
282 if(!this->CheckTargetLinks(target
, ui
->c_str()))
284 this->CreateUtilityDependTarget(*depTarget
);
291 //----------------------------------------------------------------------------
293 cmGlobalVisualStudioGenerator::CreateUtilityDependTarget(cmTarget
& target
)
295 // This target is a library on which a utility dependency exists.
296 // We need to create an intermediate custom target to hook up the
297 // dependency without causing a link.
298 const char* altName
= target
.GetProperty("ALTERNATIVE_DEPENDENCY_NAME");
301 // Create the intermediate utility target.
302 std::string altNameStr
= target
.GetName();
303 altNameStr
+= "_UTILITY";
304 const std::vector
<std::string
> no_depends
;
305 cmCustomCommandLines no_commands
;
306 const char* no_working_dir
= 0;
307 const char* no_comment
= 0;
308 target
.GetMakefile()->AddUtilityCommand(altNameStr
.c_str(), true,
309 no_working_dir
, no_depends
,
310 no_commands
, false, no_comment
);
311 target
.SetProperty("ALTERNATIVE_DEPENDENCY_NAME", altNameStr
.c_str());
313 // Most targets have a GUID created in ConfigureFinalPass. Since
314 // that has already been called, create one for this target now.
315 this->CreateGUID(altNameStr
.c_str());
317 // The intermediate target should depend on the original target.
318 if(cmTarget
* alt
= this->FindTarget(0, altNameStr
.c_str(), false))
320 alt
->AddUtility(target
.GetName());
325 //----------------------------------------------------------------------------
326 bool cmGlobalVisualStudioGenerator::CheckTargetLinks(cmTarget
& target
,
329 // Return whether the given target links to a target with the given name.
330 if(target
.GetType() == cmTarget::STATIC_LIBRARY
)
332 // Static libraries never link to anything.
335 cmTarget::LinkLibraryVectorType
const& libs
= target
.GetLinkLibraries();
336 for(cmTarget::LinkLibraryVectorType::const_iterator i
= libs
.begin();
337 i
!= libs
.end(); ++i
)
347 //----------------------------------------------------------------------------
349 cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget
& target
,
352 // Handle the external MS project special case.
353 if(strncmp(name
, "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
356 // kind of weird removing the first 27 letters. my
357 // recommendatsions: use cmCustomCommand::GetCommand() to get the
358 // project name or get rid of the target name starting with
359 // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag
360 // somewhere. These external project names shouldn't conflict
361 // with cmake target names anyways.
365 // Possibly depend on an intermediate utility target to avoid
367 if(target
.GetType() == cmTarget::STATIC_LIBRARY
||
368 target
.GetType() == cmTarget::SHARED_LIBRARY
||
369 target
.GetType() == cmTarget::MODULE_LIBRARY
||
370 target
.GetType() == cmTarget::EXECUTABLE
)
372 // The depender is a target that links. Lookup the dependee to
373 // see if it provides an alternative dependency name.
374 if(cmTarget
* depTarget
= this->FindTarget(0, name
, false))
376 // Check for an alternative name created by FixUtilityDepends.
377 if(const char* altName
=
378 depTarget
->GetProperty("ALTERNATIVE_DEPENDENCY_NAME"))
380 // The alternative name is needed only if the depender does
381 // not really link to the dependee.
382 if(!this->CheckTargetLinks(target
, name
))
390 // No special case. Just use the original dependency name.
394 //----------------------------------------------------------------------------
397 //----------------------------------------------------------------------------
398 bool IsVisualStudioMacrosFileRegistered(const std::string
& macrosFile
,
399 std::string
& nextAvailableSubKeyName
)
401 bool macrosRegistered
= false;
406 // Make lowercase local copies, convert to Unix slashes, and
407 // see if the resulting strings are the same:
408 s1
= cmSystemTools::LowerCase(macrosFile
);
409 cmSystemTools::ConvertToUnixSlashes(s1
);
413 LONG result
= ERROR_SUCCESS
;
417 "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\OtherProjects7";
419 result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(),
421 if (ERROR_SUCCESS
== result
)
423 // Iterate the subkeys and look for the values of interest in each subkey:
424 CHAR subkeyname
[256];
425 DWORD cch_subkeyname
= sizeof(subkeyname
)/sizeof(subkeyname
[0]);
427 DWORD cch_keyclass
= sizeof(keyclass
)/sizeof(keyclass
[0]);
428 FILETIME lastWriteTime
;
429 lastWriteTime
.dwHighDateTime
= 0;
430 lastWriteTime
.dwLowDateTime
= 0;
432 while (ERROR_SUCCESS
== RegEnumKeyEx(hkey
, index
, subkeyname
,
434 0, keyclass
, &cch_keyclass
, &lastWriteTime
))
436 // Open the subkey and query the values of interest:
438 result
= RegOpenKeyEx(hkey
, subkeyname
, 0, KEY_READ
, &hsubkey
);
439 if (ERROR_SUCCESS
== result
)
441 DWORD valueType
= REG_SZ
;
443 DWORD cch_data1
= sizeof(data1
)/sizeof(data1
[0]);
444 RegQueryValueEx(hsubkey
, "Path", 0, &valueType
,
445 (LPBYTE
) &data1
[0], &cch_data1
);
448 DWORD cch_data2
= sizeof(data2
);
449 RegQueryValueEx(hsubkey
, "Security", 0, &valueType
,
450 (LPBYTE
) &data2
, &cch_data2
);
453 DWORD cch_data3
= sizeof(data3
);
454 RegQueryValueEx(hsubkey
, "StorageFormat", 0, &valueType
,
455 (LPBYTE
) &data3
, &cch_data3
);
457 s2
= cmSystemTools::LowerCase(data1
);
458 cmSystemTools::ConvertToUnixSlashes(s2
);
461 macrosRegistered
= true;
464 std::string
fullname(data1
);
465 std::string filename
;
466 std::string filepath
;
467 std::string filepathname
;
468 std::string filepathpath
;
469 if (cmSystemTools::FileExists(fullname
.c_str()))
471 filename
= cmSystemTools::GetFilenameName(fullname
);
472 filepath
= cmSystemTools::GetFilenamePath(fullname
);
473 filepathname
= cmSystemTools::GetFilenameName(filepath
);
474 filepathpath
= cmSystemTools::GetFilenamePath(filepath
);
477 //std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
478 //std::cout << " Path: " << data1 << std::endl;
479 //std::cout << " Security: " << data2 << std::endl;
480 //std::cout << " StorageFormat: " << data3 << std::endl;
481 //std::cout << " filename: " << filename << std::endl;
482 //std::cout << " filepath: " << filepath << std::endl;
483 //std::cout << " filepathname: " << filepathname << std::endl;
484 //std::cout << " filepathpath: " << filepathpath << std::endl;
485 //std::cout << std::endl;
487 RegCloseKey(hsubkey
);
491 std::cout
<< "error opening subkey: " << subkeyname
<< std::endl
;
492 std::cout
<< std::endl
;
496 cch_subkeyname
= sizeof(subkeyname
)/sizeof(subkeyname
[0]);
497 cch_keyclass
= sizeof(keyclass
)/sizeof(keyclass
[0]);
498 lastWriteTime
.dwHighDateTime
= 0;
499 lastWriteTime
.dwLowDateTime
= 0;
506 std::cout
<< "error opening key: " << keyname
<< std::endl
;
507 std::cout
<< std::endl
;
511 // Pass back next available sub key name, assuming sub keys always
512 // follow the expected naming scheme. Expected naming scheme is that
513 // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
514 // as the name of the next subkey.
515 std::ostringstream ossNext
;
517 nextAvailableSubKeyName
= ossNext
.str();
521 "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\RecordingProject7";
523 result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(),
525 if (ERROR_SUCCESS
== result
)
527 DWORD valueType
= REG_SZ
;
529 DWORD cch_data1
= sizeof(data1
)/sizeof(data1
[0]);
530 RegQueryValueEx(hkey
, "Path", 0, &valueType
,
531 (LPBYTE
) &data1
[0], &cch_data1
);
534 DWORD cch_data2
= sizeof(data2
);
535 RegQueryValueEx(hkey
, "Security", 0, &valueType
,
536 (LPBYTE
) &data2
, &cch_data2
);
539 DWORD cch_data3
= sizeof(data3
);
540 RegQueryValueEx(hkey
, "StorageFormat", 0, &valueType
,
541 (LPBYTE
) &data3
, &cch_data3
);
543 s2
= cmSystemTools::LowerCase(data1
);
544 cmSystemTools::ConvertToUnixSlashes(s2
);
547 macrosRegistered
= true;
550 //std::cout << keyname << ":" << std::endl;
551 //std::cout << " Path: " << data1 << std::endl;
552 //std::cout << " Security: " << data2 << std::endl;
553 //std::cout << " StorageFormat: " << data3 << std::endl;
554 //std::cout << std::endl;
560 std::cout
<< "error opening key: " << keyname
<< std::endl
;
561 std::cout
<< std::endl
;
564 return macrosRegistered
;
567 //----------------------------------------------------------------------------
568 void WriteVSMacrosFileRegistryEntry(
569 const std::string
& nextAvailableSubKeyName
,
570 const std::string
& macrosFile
)
572 std::string keyname
=
573 "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\OtherProjects7";
575 LONG result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(), 0,
576 KEY_READ
|KEY_WRITE
, &hkey
);
577 if (ERROR_SUCCESS
== result
)
579 // Create the subkey and set the values of interest:
581 result
= RegCreateKeyEx(hkey
, nextAvailableSubKeyName
.c_str(), 0, "", 0,
582 KEY_READ
|KEY_WRITE
, 0, &hsubkey
, 0);
583 if (ERROR_SUCCESS
== result
)
587 std::string
s(macrosFile
);
588 cmSystemTools::ReplaceString(s
, "/", "\\");
590 result
= RegSetValueEx(hsubkey
, "Path", 0, REG_SZ
, (LPBYTE
) s
.c_str(),
591 static_cast<DWORD
>(strlen(s
.c_str()) + 1));
592 if (ERROR_SUCCESS
!= result
)
594 std::cout
<< "error result 1: " << result
<< std::endl
;
595 std::cout
<< std::endl
;
598 // Security value is always "1" for sample macros files (seems to be "2"
599 // if you put the file somewhere outside the standard VSMacros folder)
601 result
= RegSetValueEx(hsubkey
, "Security",
602 0, REG_DWORD
, (LPBYTE
) &dw
, sizeof(DWORD
));
603 if (ERROR_SUCCESS
!= result
)
605 std::cout
<< "error result 2: " << result
<< std::endl
;
606 std::cout
<< std::endl
;
609 // StorageFormat value is always "0" for sample macros files
611 result
= RegSetValueEx(hsubkey
, "StorageFormat",
612 0, REG_DWORD
, (LPBYTE
) &dw
, sizeof(DWORD
));
613 if (ERROR_SUCCESS
!= result
)
615 std::cout
<< "error result 3: " << result
<< std::endl
;
616 std::cout
<< std::endl
;
619 RegCloseKey(hsubkey
);
623 std::cout
<< "error creating subkey: "
624 << nextAvailableSubKeyName
<< std::endl
;
625 std::cout
<< std::endl
;
631 std::cout
<< "error opening key: " << keyname
<< std::endl
;
632 std::cout
<< std::endl
;
636 //----------------------------------------------------------------------------
637 void RegisterVisualStudioMacros(const std::string
& macrosFile
)
639 bool macrosRegistered
;
640 std::string nextAvailableSubKeyName
;
642 macrosRegistered
= IsVisualStudioMacrosFileRegistered(macrosFile
,
643 nextAvailableSubKeyName
);
645 if (!macrosRegistered
)
647 int count
= cmCallVisualStudioMacro::
648 GetNumberOfRunningVisualStudioInstances("ALL");
650 // Only register the macros file if there are *no* instances of Visual
651 // Studio running. If we register it while one is running, first, it has
652 // no effect on the running instance; second, and worse, Visual Studio
653 // removes our newly added registration entry when it quits. Instead,
654 // emit a warning asking the user to exit all running Visual Studio
659 std::ostringstream oss
;
660 oss
<< "Could not register CMake's Visual Studio macros file '"
661 << CMAKE_VSMACROS_FILENAME
"' while Visual Studio is running."
662 << " Please exit all running instances of Visual Studio before"
663 << " continuing." << std::endl
665 << "CMake needs to register Visual Studio macros when its macros"
666 << " file is updated or when it detects that its current macros file"
667 << " is no longer registered with Visual Studio."
669 cmSystemTools::Message(oss
.str().c_str(), "Warning");
671 // Count them again now that the warning is over. In the case of a GUI
672 // warning, the user may have gone to close Visual Studio and then come
673 // back to the CMake GUI and clicked ok on the above warning. If so,
674 // then register the macros *now* if the count is *now* 0...
676 count
= cmCallVisualStudioMacro::
677 GetNumberOfRunningVisualStudioInstances("ALL");
679 // Also re-get the nextAvailableSubKeyName in case Visual Studio
680 // wrote out new registered macros information as it was exiting:
684 IsVisualStudioMacrosFileRegistered(macrosFile
,
685 nextAvailableSubKeyName
);
689 // Do another if check - 'count' may have changed inside the above if:
693 WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName
, macrosFile
);