1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmGlobalVisualStudioGenerator.cxx,v $
6 Date: $Date: 2008-07-30 19:26:34 $
7 Version: $Revision: 1.14 $
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 const std::string
& regKeyBase
,
72 std::string
& nextAvailableSubKeyName
);
74 void RegisterVisualStudioMacros(const std::string
& macrosFile
,
75 const std::string
& regKeyBase
);
77 //----------------------------------------------------------------------------
78 #define CMAKE_VSMACROS_FILENAME \
79 "CMakeVSMacros2.vsmacros"
81 #define CMAKE_VSMACROS_RELOAD_MACRONAME \
82 "Macros.CMakeVSMacros2.Macros.ReloadProjects"
84 #define CMAKE_VSMACROS_STOP_MACRONAME \
85 "Macros.CMakeVSMacros2.Macros.StopBuild"
87 //----------------------------------------------------------------------------
88 void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
90 cmMakefile
* mf
= this->LocalGenerators
[0]->GetMakefile();
91 std::string dir
= this->GetUserMacrosDirectory();
93 if (mf
!= 0 && dir
!= "")
95 std::string src
= mf
->GetRequiredDefinition("CMAKE_ROOT");
96 src
+= "/Templates/" CMAKE_VSMACROS_FILENAME
;
98 std::string dst
= dir
+ "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
;
100 // Copy the macros file to the user directory only if the
101 // destination does not exist or the source location is newer.
102 // This will allow the user to edit the macros for development
103 // purposes but newer versions distributed with CMake will replace
104 // older versions in user directories.
106 if(!cmSystemTools::FileTimeCompare(src
.c_str(), dst
.c_str(), &res
) ||
109 if (!cmSystemTools::CopyFileAlways(src
.c_str(), dst
.c_str()))
111 std::ostringstream oss
;
112 oss
<< "Could not copy from: " << src
<< std::endl
;
113 oss
<< " to: " << dst
<< std::endl
;
114 cmSystemTools::Message(oss
.str().c_str(), "Warning");
118 RegisterVisualStudioMacros(dst
, this->GetUserMacrosRegKeyBase());
122 //----------------------------------------------------------------------------
124 cmGlobalVisualStudioGenerator
125 ::CallVisualStudioMacro(MacroName m
,
126 const char* vsSolutionFile
)
128 // If any solution or project files changed during the generation,
129 // tell Visual Studio to reload them...
130 cmMakefile
* mf
= this->LocalGenerators
[0]->GetMakefile();
131 std::string dir
= this->GetUserMacrosDirectory();
133 // Only really try to call the macro if:
135 // - there is a UserMacrosDirectory
136 // - the CMake vsmacros file exists
137 // - the CMake vsmacros file is registered
138 // - there were .sln/.vcproj files changed during generation
140 if (mf
!= 0 && dir
!= "")
142 std::string macrosFile
= dir
+ "/CMakeMacros/" CMAKE_VSMACROS_FILENAME
;
143 std::string nextSubkeyName
;
144 if (cmSystemTools::FileExists(macrosFile
.c_str()) &&
145 IsVisualStudioMacrosFileRegistered(macrosFile
,
146 this->GetUserMacrosRegKeyBase(), nextSubkeyName
)
149 std::string topLevelSlnName
;
152 topLevelSlnName
= vsSolutionFile
;
156 topLevelSlnName
= mf
->GetStartOutputDirectory();
157 topLevelSlnName
+= "/";
158 topLevelSlnName
+= mf
->GetProjectName();
159 topLevelSlnName
+= ".sln";
164 std::vector
<std::string
> filenames
;
165 this->GetFilesReplacedDuringGenerate(filenames
);
166 if (filenames
.size() > 0)
168 // Convert vector to semi-colon delimited string of filenames:
169 std::string projects
;
170 std::vector
<std::string
>::iterator it
= filenames
.begin();
171 if (it
!= filenames
.end())
176 for (; it
!= filenames
.end(); ++it
)
181 cmCallVisualStudioMacro::CallMacro(topLevelSlnName
,
182 CMAKE_VSMACROS_RELOAD_MACRONAME
, projects
,
183 this->GetCMakeInstance()->GetDebugOutput());
186 else if(m
== MacroStop
)
188 cmCallVisualStudioMacro::CallMacro(topLevelSlnName
,
189 CMAKE_VSMACROS_STOP_MACRONAME
, "",
190 this->GetCMakeInstance()->GetDebugOutput());
196 //----------------------------------------------------------------------------
197 std::string
cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
202 //----------------------------------------------------------------------------
203 std::string
cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
208 //----------------------------------------------------------------------------
209 void cmGlobalVisualStudioGenerator::FixUtilityDepends()
211 // Skip for VS versions 8 and above.
212 if(!this->VSLinksDependencies())
217 // For VS versions before 8:
219 // When a target that links contains a project-level dependency on a
220 // library target that library is automatically linked. In order to
221 // allow utility-style project-level dependencies that do not
222 // actually link we need to automatically insert an intermediate
225 // Here we edit the utility dependencies of a target to add the
226 // intermediate custom target when necessary.
227 for(unsigned i
= 0; i
< this->LocalGenerators
.size(); ++i
)
230 &(this->LocalGenerators
[i
]->GetMakefile()->GetTargets());
231 for(cmTargets::iterator tarIt
= targets
->begin();
232 tarIt
!= targets
->end(); ++tarIt
)
234 this->FixUtilityDependsForTarget(tarIt
->second
);
239 //----------------------------------------------------------------------------
241 cmGlobalVisualStudioGenerator::FixUtilityDependsForTarget(cmTarget
& target
)
243 // Only targets that link need to be fixed.
244 if(target
.GetType() != cmTarget::STATIC_LIBRARY
&&
245 target
.GetType() != cmTarget::SHARED_LIBRARY
&&
246 target
.GetType() != cmTarget::MODULE_LIBRARY
&&
247 target
.GetType() != cmTarget::EXECUTABLE
)
253 // This feature makes a mess in SLN files for VS 7.1 and below. It
254 // creates an extra target for every target that is "linked" by a
255 // static library. Without this feature static libraries do not
256 // wait until their "link" dependencies are built to build. This is
257 // not a problem 99.9% of the time, and projects that do have the
258 // problem can enable this work-around by using add_dependencies.
260 // Static libraries cannot depend directly on the targets to which
261 // they link because VS will copy those targets into the library
262 // (for VS < 8). To work around the problem we copy the
263 // dependencies to be utility dependencies so that the work-around
265 if(target
.GetType() == cmTarget::STATIC_LIBRARY
)
267 cmTarget::LinkLibraryVectorType
const& libs
= target
.GetLinkLibraries();
268 for(cmTarget::LinkLibraryVectorType::const_iterator i
= libs
.begin();
269 i
!= libs
.end(); ++i
)
271 if(cmTarget
* depTarget
= this->FindTarget(0, i
->first
.c_str(), false))
273 target
.AddUtility(depTarget
->GetName());
279 // Look at each utility dependency.
280 for(std::set
<cmStdString
>::const_iterator ui
=
281 target
.GetUtilities().begin();
282 ui
!= target
.GetUtilities().end(); ++ui
)
284 if(cmTarget
* depTarget
= this->FindTarget(0, ui
->c_str()))
286 if(depTarget
->GetType() == cmTarget::STATIC_LIBRARY
||
287 depTarget
->GetType() == cmTarget::SHARED_LIBRARY
||
288 depTarget
->GetType() == cmTarget::MODULE_LIBRARY
)
290 // This utility dependency will cause an attempt to link. If
291 // the depender does not already link the dependee we need an
292 // intermediate target.
293 if(!this->CheckTargetLinks(target
, ui
->c_str()))
295 this->CreateUtilityDependTarget(*depTarget
);
302 //----------------------------------------------------------------------------
304 cmGlobalVisualStudioGenerator::CreateUtilityDependTarget(cmTarget
& target
)
306 // This target is a library on which a utility dependency exists.
307 // We need to create an intermediate custom target to hook up the
308 // dependency without causing a link.
309 const char* altName
= target
.GetProperty("ALTERNATIVE_DEPENDENCY_NAME");
312 // Create the intermediate utility target.
313 std::string altNameStr
= target
.GetName();
314 altNameStr
+= "_UTILITY";
315 const std::vector
<std::string
> no_depends
;
316 cmCustomCommandLines no_commands
;
317 const char* no_working_dir
= 0;
318 const char* no_comment
= 0;
319 target
.GetMakefile()->AddUtilityCommand(altNameStr
.c_str(), true,
320 no_working_dir
, no_depends
,
321 no_commands
, false, no_comment
);
322 target
.SetProperty("ALTERNATIVE_DEPENDENCY_NAME", altNameStr
.c_str());
324 // Most targets have a GUID created in ConfigureFinalPass. Since
325 // that has already been called, create one for this target now.
326 this->CreateGUID(altNameStr
.c_str());
328 // The intermediate target should depend on the original target.
329 if(cmTarget
* alt
= this->FindTarget(0, altNameStr
.c_str()))
331 alt
->AddUtility(target
.GetName());
336 //----------------------------------------------------------------------------
337 bool cmGlobalVisualStudioGenerator::CheckTargetLinks(cmTarget
& target
,
340 // Return whether the given target links to a target with the given name.
341 if(target
.GetType() == cmTarget::STATIC_LIBRARY
)
343 // Static libraries never link to anything.
346 cmTarget::LinkLibraryVectorType
const& libs
= target
.GetLinkLibraries();
347 for(cmTarget::LinkLibraryVectorType::const_iterator i
= libs
.begin();
348 i
!= libs
.end(); ++i
)
358 //----------------------------------------------------------------------------
360 cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget
& target
,
363 // Handle the external MS project special case.
364 if(strncmp(name
, "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
367 // kind of weird removing the first 27 letters. my
368 // recommendatsions: use cmCustomCommand::GetCommand() to get the
369 // project name or get rid of the target name starting with
370 // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag
371 // somewhere. These external project names shouldn't conflict
372 // with cmake target names anyways.
376 // Possibly depend on an intermediate utility target to avoid
378 if(target
.GetType() == cmTarget::STATIC_LIBRARY
||
379 target
.GetType() == cmTarget::SHARED_LIBRARY
||
380 target
.GetType() == cmTarget::MODULE_LIBRARY
||
381 target
.GetType() == cmTarget::EXECUTABLE
)
383 // The depender is a target that links. Lookup the dependee to
384 // see if it provides an alternative dependency name.
385 if(cmTarget
* depTarget
= this->FindTarget(0, name
))
387 // Check for an alternative name created by FixUtilityDepends.
388 if(const char* altName
=
389 depTarget
->GetProperty("ALTERNATIVE_DEPENDENCY_NAME"))
391 // The alternative name is needed only if the depender does
392 // not really link to the dependee.
393 if(!this->CheckTargetLinks(target
, name
))
401 // No special case. Just use the original dependency name.
405 //----------------------------------------------------------------------------
408 //----------------------------------------------------------------------------
409 bool IsVisualStudioMacrosFileRegistered(const std::string
& macrosFile
,
410 const std::string
& regKeyBase
,
411 std::string
& nextAvailableSubKeyName
)
413 bool macrosRegistered
= false;
418 // Make lowercase local copies, convert to Unix slashes, and
419 // see if the resulting strings are the same:
420 s1
= cmSystemTools::LowerCase(macrosFile
);
421 cmSystemTools::ConvertToUnixSlashes(s1
);
425 LONG result
= ERROR_SUCCESS
;
428 keyname
= regKeyBase
+ "\\OtherProjects7";
430 result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(),
432 if (ERROR_SUCCESS
== result
)
434 // Iterate the subkeys and look for the values of interest in each subkey:
435 CHAR subkeyname
[256];
436 DWORD cch_subkeyname
= sizeof(subkeyname
)/sizeof(subkeyname
[0]);
438 DWORD cch_keyclass
= sizeof(keyclass
)/sizeof(keyclass
[0]);
439 FILETIME lastWriteTime
;
440 lastWriteTime
.dwHighDateTime
= 0;
441 lastWriteTime
.dwLowDateTime
= 0;
443 while (ERROR_SUCCESS
== RegEnumKeyEx(hkey
, index
, subkeyname
,
445 0, keyclass
, &cch_keyclass
, &lastWriteTime
))
447 // Open the subkey and query the values of interest:
449 result
= RegOpenKeyEx(hkey
, subkeyname
, 0, KEY_READ
, &hsubkey
);
450 if (ERROR_SUCCESS
== result
)
452 DWORD valueType
= REG_SZ
;
454 DWORD cch_data1
= sizeof(data1
)/sizeof(data1
[0]);
455 RegQueryValueEx(hsubkey
, "Path", 0, &valueType
,
456 (LPBYTE
) &data1
[0], &cch_data1
);
459 DWORD cch_data2
= sizeof(data2
);
460 RegQueryValueEx(hsubkey
, "Security", 0, &valueType
,
461 (LPBYTE
) &data2
, &cch_data2
);
464 DWORD cch_data3
= sizeof(data3
);
465 RegQueryValueEx(hsubkey
, "StorageFormat", 0, &valueType
,
466 (LPBYTE
) &data3
, &cch_data3
);
468 s2
= cmSystemTools::LowerCase(data1
);
469 cmSystemTools::ConvertToUnixSlashes(s2
);
472 macrosRegistered
= true;
475 std::string
fullname(data1
);
476 std::string filename
;
477 std::string filepath
;
478 std::string filepathname
;
479 std::string filepathpath
;
480 if (cmSystemTools::FileExists(fullname
.c_str()))
482 filename
= cmSystemTools::GetFilenameName(fullname
);
483 filepath
= cmSystemTools::GetFilenamePath(fullname
);
484 filepathname
= cmSystemTools::GetFilenameName(filepath
);
485 filepathpath
= cmSystemTools::GetFilenamePath(filepath
);
488 //std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
489 //std::cout << " Path: " << data1 << std::endl;
490 //std::cout << " Security: " << data2 << std::endl;
491 //std::cout << " StorageFormat: " << data3 << std::endl;
492 //std::cout << " filename: " << filename << std::endl;
493 //std::cout << " filepath: " << filepath << std::endl;
494 //std::cout << " filepathname: " << filepathname << std::endl;
495 //std::cout << " filepathpath: " << filepathpath << std::endl;
496 //std::cout << std::endl;
498 RegCloseKey(hsubkey
);
502 std::cout
<< "error opening subkey: " << subkeyname
<< std::endl
;
503 std::cout
<< std::endl
;
507 cch_subkeyname
= sizeof(subkeyname
)/sizeof(subkeyname
[0]);
508 cch_keyclass
= sizeof(keyclass
)/sizeof(keyclass
[0]);
509 lastWriteTime
.dwHighDateTime
= 0;
510 lastWriteTime
.dwLowDateTime
= 0;
517 std::cout
<< "error opening key: " << keyname
<< std::endl
;
518 std::cout
<< std::endl
;
522 // Pass back next available sub key name, assuming sub keys always
523 // follow the expected naming scheme. Expected naming scheme is that
524 // the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
525 // as the name of the next subkey.
526 std::ostringstream ossNext
;
528 nextAvailableSubKeyName
= ossNext
.str();
531 keyname
= regKeyBase
+ "\\RecordingProject7";
533 result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(),
535 if (ERROR_SUCCESS
== result
)
537 DWORD valueType
= REG_SZ
;
539 DWORD cch_data1
= sizeof(data1
)/sizeof(data1
[0]);
540 RegQueryValueEx(hkey
, "Path", 0, &valueType
,
541 (LPBYTE
) &data1
[0], &cch_data1
);
544 DWORD cch_data2
= sizeof(data2
);
545 RegQueryValueEx(hkey
, "Security", 0, &valueType
,
546 (LPBYTE
) &data2
, &cch_data2
);
549 DWORD cch_data3
= sizeof(data3
);
550 RegQueryValueEx(hkey
, "StorageFormat", 0, &valueType
,
551 (LPBYTE
) &data3
, &cch_data3
);
553 s2
= cmSystemTools::LowerCase(data1
);
554 cmSystemTools::ConvertToUnixSlashes(s2
);
557 macrosRegistered
= true;
560 //std::cout << keyname << ":" << std::endl;
561 //std::cout << " Path: " << data1 << std::endl;
562 //std::cout << " Security: " << data2 << std::endl;
563 //std::cout << " StorageFormat: " << data3 << std::endl;
564 //std::cout << std::endl;
570 std::cout
<< "error opening key: " << keyname
<< std::endl
;
571 std::cout
<< std::endl
;
574 return macrosRegistered
;
577 //----------------------------------------------------------------------------
578 void WriteVSMacrosFileRegistryEntry(
579 const std::string
& nextAvailableSubKeyName
,
580 const std::string
& macrosFile
,
581 const std::string
& regKeyBase
)
583 std::string keyname
= regKeyBase
+ "\\OtherProjects7";
585 LONG result
= RegOpenKeyEx(HKEY_CURRENT_USER
, keyname
.c_str(), 0,
586 KEY_READ
|KEY_WRITE
, &hkey
);
587 if (ERROR_SUCCESS
== result
)
589 // Create the subkey and set the values of interest:
591 result
= RegCreateKeyEx(hkey
, nextAvailableSubKeyName
.c_str(), 0, "", 0,
592 KEY_READ
|KEY_WRITE
, 0, &hsubkey
, 0);
593 if (ERROR_SUCCESS
== result
)
597 std::string
s(macrosFile
);
598 cmSystemTools::ReplaceString(s
, "/", "\\");
600 result
= RegSetValueEx(hsubkey
, "Path", 0, REG_SZ
, (LPBYTE
) s
.c_str(),
601 static_cast<DWORD
>(strlen(s
.c_str()) + 1));
602 if (ERROR_SUCCESS
!= result
)
604 std::cout
<< "error result 1: " << result
<< std::endl
;
605 std::cout
<< std::endl
;
608 // Security value is always "1" for sample macros files (seems to be "2"
609 // if you put the file somewhere outside the standard VSMacros folder)
611 result
= RegSetValueEx(hsubkey
, "Security",
612 0, REG_DWORD
, (LPBYTE
) &dw
, sizeof(DWORD
));
613 if (ERROR_SUCCESS
!= result
)
615 std::cout
<< "error result 2: " << result
<< std::endl
;
616 std::cout
<< std::endl
;
619 // StorageFormat value is always "0" for sample macros files
621 result
= RegSetValueEx(hsubkey
, "StorageFormat",
622 0, REG_DWORD
, (LPBYTE
) &dw
, sizeof(DWORD
));
623 if (ERROR_SUCCESS
!= result
)
625 std::cout
<< "error result 3: " << result
<< std::endl
;
626 std::cout
<< std::endl
;
629 RegCloseKey(hsubkey
);
633 std::cout
<< "error creating subkey: "
634 << nextAvailableSubKeyName
<< std::endl
;
635 std::cout
<< std::endl
;
641 std::cout
<< "error opening key: " << keyname
<< std::endl
;
642 std::cout
<< std::endl
;
646 //----------------------------------------------------------------------------
647 void RegisterVisualStudioMacros(const std::string
& macrosFile
,
648 const std::string
& regKeyBase
)
650 bool macrosRegistered
;
651 std::string nextAvailableSubKeyName
;
653 macrosRegistered
= IsVisualStudioMacrosFileRegistered(macrosFile
,
654 regKeyBase
, nextAvailableSubKeyName
);
656 if (!macrosRegistered
)
658 int count
= cmCallVisualStudioMacro::
659 GetNumberOfRunningVisualStudioInstances("ALL");
661 // Only register the macros file if there are *no* instances of Visual
662 // Studio running. If we register it while one is running, first, it has
663 // no effect on the running instance; second, and worse, Visual Studio
664 // removes our newly added registration entry when it quits. Instead,
665 // emit a warning asking the user to exit all running Visual Studio
670 std::ostringstream oss
;
671 oss
<< "Could not register CMake's Visual Studio macros file '"
672 << CMAKE_VSMACROS_FILENAME
"' while Visual Studio is running."
673 << " Please exit all running instances of Visual Studio before"
674 << " continuing." << std::endl
676 << "CMake needs to register Visual Studio macros when its macros"
677 << " file is updated or when it detects that its current macros file"
678 << " is no longer registered with Visual Studio."
680 cmSystemTools::Message(oss
.str().c_str(), "Warning");
682 // Count them again now that the warning is over. In the case of a GUI
683 // warning, the user may have gone to close Visual Studio and then come
684 // back to the CMake GUI and clicked ok on the above warning. If so,
685 // then register the macros *now* if the count is *now* 0...
687 count
= cmCallVisualStudioMacro::
688 GetNumberOfRunningVisualStudioInstances("ALL");
690 // Also re-get the nextAvailableSubKeyName in case Visual Studio
691 // wrote out new registered macros information as it was exiting:
695 IsVisualStudioMacrosFileRegistered(macrosFile
, regKeyBase
,
696 nextAvailableSubKeyName
);
700 // Do another if check - 'count' may have changed inside the above if:
704 WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName
, macrosFile
,
709 bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly(cmTarget
& target
)
711 // check to see if this is a fortran build
712 std::set
<cmStdString
> languages
;
713 target
.GetLanguages(languages
);
714 if(languages
.size() == 1)
716 if(*languages
.begin() == "Fortran")