ENH: keep cleaning up Tcl/Tk modules
[cmake.git] / Source / cmGlobalVisualStudioGenerator.cxx
blob2573f92498728947b40e5561fd3d4ed18ffa2e98
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmGlobalVisualStudioGenerator.cxx,v $
5 Language: C++
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"
22 #include "cmTarget.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
47 if(gen.size())
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
62 // of Visual Studio.
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.
103 int res;
104 if(!cmSystemTools::FileTimeCompare(src.c_str(), dst.c_str(), &res) ||
105 res > 0)
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 //----------------------------------------------------------------------------
121 void
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:
132 // - mf is non-NULL
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;
147 if(vsSolutionFile)
149 topLevelSlnName = vsSolutionFile;
151 else
153 topLevelSlnName = mf->GetStartOutputDirectory();
154 topLevelSlnName += "/";
155 topLevelSlnName += mf->GetProjectName();
156 topLevelSlnName += ".sln";
159 if(m == MacroReload)
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())
170 projects = *it;
171 ++it;
173 for (; it != filenames.end(); ++it)
175 projects += ";";
176 projects += *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()
194 return "";
197 //----------------------------------------------------------------------------
198 void cmGlobalVisualStudioGenerator::FixUtilityDepends()
200 // Skip for VS versions 8 and above.
201 if(!this->VSLinksDependencies())
203 return;
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
212 // custom target.
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)
218 cmTargets* targets =
219 &(this->LocalGenerators[i]->GetMakefile()->GetTargets());
220 for(cmTargets::iterator tarIt = targets->begin();
221 tarIt != targets->end(); ++tarIt)
223 this->FixUtilityDependsForTarget(tarIt->second);
228 //----------------------------------------------------------------------------
229 void
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)
238 return;
241 #if 0
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
253 // below is used.
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());
266 #endif
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 //----------------------------------------------------------------------------
292 void
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");
299 if(!altName)
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,
327 const char* name)
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.
333 return false;
335 cmTarget::LinkLibraryVectorType const& libs = target.GetLinkLibraries();
336 for(cmTarget::LinkLibraryVectorType::const_iterator i = libs.begin();
337 i != libs.end(); ++i)
339 if(i->first == name)
341 return true;
344 return false;
347 //----------------------------------------------------------------------------
348 const char*
349 cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target,
350 const char* name)
352 // Handle the external MS project special case.
353 if(strncmp(name, "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
355 // Note from Ken:
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.
362 return name+27;
365 // Possibly depend on an intermediate utility target to avoid
366 // linking.
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))
384 return altName;
390 // No special case. Just use the original dependency name.
391 return name;
394 //----------------------------------------------------------------------------
395 #include <windows.h>
397 //----------------------------------------------------------------------------
398 bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
399 std::string& nextAvailableSubKeyName)
401 bool macrosRegistered = false;
403 std::string s1;
404 std::string s2;
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);
411 std::string keyname;
412 HKEY hkey = NULL;
413 LONG result = ERROR_SUCCESS;
414 DWORD index = 0;
416 keyname =
417 "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\OtherProjects7";
418 hkey = NULL;
419 result = RegOpenKeyEx(HKEY_CURRENT_USER, keyname.c_str(),
420 0, KEY_READ, &hkey);
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]);
426 CHAR keyclass[256];
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,
433 &cch_subkeyname,
434 0, keyclass, &cch_keyclass, &lastWriteTime))
436 // Open the subkey and query the values of interest:
437 HKEY hsubkey = NULL;
438 result = RegOpenKeyEx(hkey, subkeyname, 0, KEY_READ, &hsubkey);
439 if (ERROR_SUCCESS == result)
441 DWORD valueType = REG_SZ;
442 CHAR data1[256];
443 DWORD cch_data1 = sizeof(data1)/sizeof(data1[0]);
444 RegQueryValueEx(hsubkey, "Path", 0, &valueType,
445 (LPBYTE) &data1[0], &cch_data1);
447 DWORD data2 = 0;
448 DWORD cch_data2 = sizeof(data2);
449 RegQueryValueEx(hsubkey, "Security", 0, &valueType,
450 (LPBYTE) &data2, &cch_data2);
452 DWORD data3 = 0;
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);
459 if (s2 == s1)
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);
489 else
491 std::cout << "error opening subkey: " << subkeyname << std::endl;
492 std::cout << std::endl;
495 ++index;
496 cch_subkeyname = sizeof(subkeyname)/sizeof(subkeyname[0]);
497 cch_keyclass = sizeof(keyclass)/sizeof(keyclass[0]);
498 lastWriteTime.dwHighDateTime = 0;
499 lastWriteTime.dwLowDateTime = 0;
502 RegCloseKey(hkey);
504 else
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;
516 ossNext << index;
517 nextAvailableSubKeyName = ossNext.str();
520 keyname =
521 "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\RecordingProject7";
522 hkey = NULL;
523 result = RegOpenKeyEx(HKEY_CURRENT_USER, keyname.c_str(),
524 0, KEY_READ, &hkey);
525 if (ERROR_SUCCESS == result)
527 DWORD valueType = REG_SZ;
528 CHAR data1[256];
529 DWORD cch_data1 = sizeof(data1)/sizeof(data1[0]);
530 RegQueryValueEx(hkey, "Path", 0, &valueType,
531 (LPBYTE) &data1[0], &cch_data1);
533 DWORD data2 = 0;
534 DWORD cch_data2 = sizeof(data2);
535 RegQueryValueEx(hkey, "Security", 0, &valueType,
536 (LPBYTE) &data2, &cch_data2);
538 DWORD data3 = 0;
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);
545 if (s2 == s1)
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;
556 RegCloseKey(hkey);
558 else
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";
574 HKEY hkey = NULL;
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:
580 HKEY hsubkey = NULL;
581 result = RegCreateKeyEx(hkey, nextAvailableSubKeyName.c_str(), 0, "", 0,
582 KEY_READ|KEY_WRITE, 0, &hsubkey, 0);
583 if (ERROR_SUCCESS == result)
585 DWORD dw = 0;
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)
600 dw = 1;
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
610 dw = 0;
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);
621 else
623 std::cout << "error creating subkey: "
624 << nextAvailableSubKeyName << std::endl;
625 std::cout << std::endl;
627 RegCloseKey(hkey);
629 else
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
655 // instances...
657 if (0 != count)
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
664 << 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."
668 << std::endl;
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:
682 if (0 == count)
684 IsVisualStudioMacrosFileRegistered(macrosFile,
685 nextAvailableSubKeyName);
689 // Do another if check - 'count' may have changed inside the above if:
691 if (0 == count)
693 WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName, macrosFile);