1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
12 #include "cmGraphVizWriter.h"
13 #include "cmMakefile.h"
14 #include "cmLocalGenerator.h"
15 #include "cmGlobalGenerator.h"
16 #include "cmGeneratedFileStream.h"
22 static const char* getShapeForTarget(const cmTarget* target)
29 switch ( target->GetType() )
31 case cmTarget::EXECUTABLE:
33 case cmTarget::STATIC_LIBRARY:
35 case cmTarget::SHARED_LIBRARY:
37 case cmTarget::MODULE_LIBRARY:
47 cmGraphVizWriter::cmGraphVizWriter(const std::vector<cmLocalGenerator*>&
51 ,GraphHeader("node [\n fontsize = \"12\"\n];")
52 ,GraphNodePrefix("node")
53 ,GenerateForExecutables(true)
54 ,GenerateForStaticLibs(true)
55 ,GenerateForSharedLibs(true)
56 ,GenerateForModuleLibs(true)
57 ,GenerateForExternals(true)
58 ,LocalGenerators(localGenerators)
59 ,HaveTargetsAndLibs(false)
64 void cmGraphVizWriter::ReadSettings(const char* settingsFileName,
65 const char* fallbackSettingsFileName)
68 cmGlobalGenerator ggi;
69 ggi.SetCMakeInstance(&cm);
70 std::auto_ptr<cmLocalGenerator> lg(ggi.CreateLocalGenerator());
71 cmMakefile *mf = lg->GetMakefile();
73 const char* inFileName = settingsFileName;
75 if ( !cmSystemTools::FileExists(inFileName) )
77 inFileName = fallbackSettingsFileName;
78 if ( !cmSystemTools::FileExists(inFileName) )
84 if ( !mf->ReadListFile(0, inFileName) )
86 cmSystemTools::Error("Problem opening GraphViz options file: ",
91 std::cout << "Reading GraphViz options file: " << inFileName << std::endl;
93 #define __set_if_set(var, cmakeDefinition) \
95 const char* value = mf->GetDefinition(cmakeDefinition); \
102 __set_if_set(this->GraphType, "GRAPHVIZ_GRAPH_TYPE");
103 __set_if_set(this->GraphName, "GRAPHVIZ_GRAPH_NAME");
104 __set_if_set(this->GraphHeader, "GRAPHVIZ_GRAPH_HEADER");
105 __set_if_set(this->GraphNodePrefix, "GRAPHVIZ_NODE_PREFIX");
107 #define __set_bool_if_set(var, cmakeDefinition) \
109 const char* value = mf->GetDefinition(cmakeDefinition); \
112 var = mf->IsOn(cmakeDefinition); \
116 __set_bool_if_set(this->GenerateForExecutables, "GRAPHVIZ_EXECUTABLES");
117 __set_bool_if_set(this->GenerateForStaticLibs, "GRAPHVIZ_STATIC_LIBS");
118 __set_bool_if_set(this->GenerateForSharedLibs, "GRAPHVIZ_SHARED_LIBS");
119 __set_bool_if_set(this->GenerateForModuleLibs, "GRAPHVIZ_MODULE_LIBS");
120 __set_bool_if_set(this->GenerateForExternals, "GRAPHVIZ_EXTERNAL_LIBS");
122 cmStdString ignoreTargetsRegexes;
123 __set_if_set(ignoreTargetsRegexes, "GRAPHVIZ_IGNORE_TARGETS");
125 this->TargetsToIgnoreRegex.clear();
126 if (ignoreTargetsRegexes.size() > 0)
128 std::vector<std::string> ignoreTargetsRegExVector;
129 cmSystemTools::ExpandListArgument(ignoreTargetsRegexes,
130 ignoreTargetsRegExVector);
131 for(std::vector<std::string>::const_iterator itvIt
132 = ignoreTargetsRegExVector.begin();
133 itvIt != ignoreTargetsRegExVector.end();
136 cmStdString currentRegexString(*itvIt);
137 cmsys::RegularExpression currentRegex;
138 if (!currentRegex.compile(currentRegexString.c_str()))
140 std::cerr << "Could not compile bad regex \"" << currentRegexString
141 << "\"" << std::endl;
143 this->TargetsToIgnoreRegex.push_back(currentRegex);
150 // Iterate over all targets and write for each one a graph which shows
151 // which other targets depend on it.
152 void cmGraphVizWriter::WriteTargetDependersFiles(const char* fileName)
154 this->CollectTargetsAndLibs();
156 for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
157 this->TargetPtrs.begin();
158 ptrIt != this->TargetPtrs.end();
161 if (ptrIt->second == NULL)
166 if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
171 std::string currentFilename = fileName;
172 currentFilename += ".";
173 currentFilename += ptrIt->first;
174 currentFilename += ".dependers";
176 cmGeneratedFileStream str(currentFilename.c_str());
182 std::set<std::string> insertedConnections;
183 std::set<std::string> insertedNodes;
185 std::cout << "Writing " << currentFilename << "..." << std::endl;
186 this->WriteHeader(str);
188 this->WriteDependerConnections(ptrIt->first.c_str(),
189 insertedNodes, insertedConnections, str);
191 this->WriteFooter(str);
196 // Iterate over all targets and write for each one a graph which shows
197 // on which targets it depends.
198 void cmGraphVizWriter::WritePerTargetFiles(const char* fileName)
200 this->CollectTargetsAndLibs();
202 for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
203 this->TargetPtrs.begin();
204 ptrIt != this->TargetPtrs.end();
207 if (ptrIt->second == NULL)
212 if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
217 std::set<std::string> insertedConnections;
218 std::set<std::string> insertedNodes;
220 std::string currentFilename = fileName;
221 currentFilename += ".";
222 currentFilename += ptrIt->first;
223 cmGeneratedFileStream str(currentFilename.c_str());
229 std::cout << "Writing " << currentFilename << "..." << std::endl;
230 this->WriteHeader(str);
232 this->WriteConnections(ptrIt->first.c_str(),
233 insertedNodes, insertedConnections, str);
234 this->WriteFooter(str);
240 void cmGraphVizWriter::WriteGlobalFile(const char* fileName)
242 this->CollectTargetsAndLibs();
244 cmGeneratedFileStream str(fileName);
249 this->WriteHeader(str);
251 std::cout << "Writing " << fileName << "..." << std::endl;
253 std::set<std::string> insertedConnections;
254 std::set<std::string> insertedNodes;
256 for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
257 this->TargetPtrs.begin();
258 ptrIt != this->TargetPtrs.end();
261 if (ptrIt->second == NULL)
266 if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
271 this->WriteConnections(ptrIt->first.c_str(),
272 insertedNodes, insertedConnections, str);
274 this->WriteFooter(str);
278 void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& str) const
280 str << this->GraphType << " " << this->GraphName << " {" << std::endl;
281 str << this->GraphHeader << std::endl;
285 void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& str) const
287 str << "}" << std::endl;
291 void cmGraphVizWriter::WriteConnections(const char* targetName,
292 std::set<std::string>& insertedNodes,
293 std::set<std::string>& insertedConnections,
294 cmGeneratedFileStream& str) const
296 std::map<cmStdString, const cmTarget* >::const_iterator targetPtrIt =
297 this->TargetPtrs.find(targetName);
299 if (targetPtrIt == this->TargetPtrs.end()) // not found at all
304 this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
306 if (targetPtrIt->second == NULL) // it's an external library
312 std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
314 const cmTarget::LinkLibraryVectorType* ll =
315 &(targetPtrIt->second->GetOriginalLinkLibraries());
317 for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
321 const char* libName = llit->first.c_str();
322 std::map<cmStdString, cmStdString>::const_iterator libNameIt =
323 this->TargetNamesNodes.find(libName);
325 // can happen e.g. if GRAPHVIZ_TARGET_IGNORE_REGEX is used
326 if(libNameIt == this->TargetNamesNodes.end())
331 std::string connectionName = myNodeName;
332 connectionName += "-";
333 connectionName += libNameIt->second;
334 if (insertedConnections.find(connectionName) == insertedConnections.end())
336 insertedConnections.insert(connectionName);
337 this->WriteNode(libName, this->TargetPtrs.find(libName)->second,
340 str << " \"" << myNodeName.c_str() << "\" -> \""
341 << libNameIt->second.c_str() << "\"";
342 str << " // " << targetName << " -> " << libName << std::endl;
343 this->WriteConnections(libName, insertedNodes, insertedConnections, str);
350 void cmGraphVizWriter::WriteDependerConnections(const char* targetName,
351 std::set<std::string>& insertedNodes,
352 std::set<std::string>& insertedConnections,
353 cmGeneratedFileStream& str) const
355 std::map<cmStdString, const cmTarget* >::const_iterator targetPtrIt =
356 this->TargetPtrs.find(targetName);
358 if (targetPtrIt == this->TargetPtrs.end()) // not found at all
363 this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
365 if (targetPtrIt->second == NULL) // it's an external library
371 std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
373 // now search who links against me
374 for(std::map<cmStdString, const cmTarget*>::const_iterator dependerIt =
375 this->TargetPtrs.begin();
376 dependerIt != this->TargetPtrs.end();
379 if (dependerIt->second == NULL)
384 if (this->GenerateForTargetType(dependerIt->second->GetType()) == false)
389 // Now we have a target, check whether it links against targetName.
390 // If so, draw a connection, and then continue with dependers on that one.
391 const cmTarget::LinkLibraryVectorType* ll =
392 &(dependerIt->second->GetOriginalLinkLibraries());
394 for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
398 std::string libName = llit->first.c_str();
399 if (libName == targetName)
401 // So this target links against targetName.
402 std::map<cmStdString, cmStdString>::const_iterator dependerNodeNameIt =
403 this->TargetNamesNodes.find(dependerIt->first);
405 if(dependerNodeNameIt != this->TargetNamesNodes.end())
407 std::string connectionName = dependerNodeNameIt->second;
408 connectionName += "-";
409 connectionName += myNodeName;
411 if (insertedConnections.find(connectionName) ==
412 insertedConnections.end())
414 insertedConnections.insert(connectionName);
415 this->WriteNode(dependerIt->first.c_str(), dependerIt->second,
418 str << " \"" << dependerNodeNameIt->second << "\" -> \""
419 << myNodeName << "\"";
420 str << " // " <<targetName<< " -> " <<dependerIt->first<<std::endl;
421 this->WriteDependerConnections(dependerIt->first.c_str(),
422 insertedNodes, insertedConnections, str);
435 void cmGraphVizWriter::WriteNode(const char* targetName,
436 const cmTarget* target,
437 std::set<std::string>& insertedNodes,
438 cmGeneratedFileStream& str) const
440 if (insertedNodes.find(targetName) == insertedNodes.end())
442 insertedNodes.insert(targetName);
443 std::map<cmStdString, cmStdString>::const_iterator nameIt =
444 this->TargetNamesNodes.find(targetName);
446 str << " \"" << nameIt->second.c_str() << "\" [ label=\""
447 << targetName << "\" shape=\"" << getShapeForTarget(target)
448 << "\"];" << std::endl;
453 void cmGraphVizWriter::CollectTargetsAndLibs()
455 if (this->HaveTargetsAndLibs == false)
457 this->HaveTargetsAndLibs = true;
458 int cnt = this->CollectAllTargets();
459 if (this->GenerateForExternals)
461 this->CollectAllExternalLibs(cnt);
467 int cmGraphVizWriter::CollectAllTargets()
470 // First pass get the list of all cmake targets
471 for (std::vector<cmLocalGenerator*>::const_iterator lit =
472 this->LocalGenerators.begin();
473 lit != this->LocalGenerators.end();
476 const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
477 for ( cmTargets::const_iterator tit = targets->begin();
478 tit != targets->end();
481 const char* realTargetName = tit->first.c_str();
482 if(this->IgnoreThisTarget(realTargetName))
484 // Skip ignored targets
487 //std::cout << "Found target: " << tit->first.c_str() << std::endl;
488 cmOStringStream ostr;
489 ostr << this->GraphNodePrefix << cnt++;
490 this->TargetNamesNodes[realTargetName] = ostr.str();
491 this->TargetPtrs[realTargetName] = &tit->second;
499 int cmGraphVizWriter::CollectAllExternalLibs(int cnt)
501 // Ok, now find all the stuff we link to that is not in cmake
502 for (std::vector<cmLocalGenerator*>::const_iterator lit =
503 this->LocalGenerators.begin();
504 lit != this->LocalGenerators.end();
507 const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
508 for ( cmTargets::const_iterator tit = targets->begin();
509 tit != targets->end();
512 const char* realTargetName = tit->first.c_str();
513 if (this->IgnoreThisTarget(realTargetName))
515 // Skip ignored targets
518 const cmTarget::LinkLibraryVectorType* ll =
519 &(tit->second.GetOriginalLinkLibraries());
520 for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
524 const char* libName = llit->first.c_str();
525 if (this->IgnoreThisTarget(libName))
527 // Skip ignored targets
531 std::map<cmStdString, const cmTarget*>::const_iterator tarIt =
532 this->TargetPtrs.find(libName);
533 if ( tarIt == this->TargetPtrs.end() )
535 cmOStringStream ostr;
536 ostr << this->GraphNodePrefix << cnt++;
537 this->TargetNamesNodes[libName] = ostr.str();
538 this->TargetPtrs[libName] = NULL;
539 //str << " \"" << ostr.c_str() << "\" [ label=\"" << libName
540 //<< "\" shape=\"ellipse\"];" << std::endl;
549 bool cmGraphVizWriter::IgnoreThisTarget(const char* name)
551 for(std::vector<cmsys::RegularExpression>::iterator itvIt
552 = this->TargetsToIgnoreRegex.begin();
553 itvIt != this->TargetsToIgnoreRegex.end();
556 cmsys::RegularExpression& regEx = *itvIt;
557 if (regEx.is_valid())
559 if (regEx.find(name))
570 bool cmGraphVizWriter::GenerateForTargetType(cmTarget::TargetType targetType)
575 case cmTarget::EXECUTABLE:
576 return this->GenerateForExecutables;
577 case cmTarget::STATIC_LIBRARY:
578 return this->GenerateForStaticLibs;
579 case cmTarget::SHARED_LIBRARY:
580 return this->GenerateForSharedLibs;
581 case cmTarget::MODULE_LIBRARY:
582 return this->GenerateForModuleLibs;