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"
20 static const char* getShapeForTarget(const cmTarget* target)
27 switch ( target->GetType() )
29 case cmTarget::EXECUTABLE:
31 case cmTarget::STATIC_LIBRARY:
33 case cmTarget::SHARED_LIBRARY:
35 case cmTarget::MODULE_LIBRARY:
45 cmGraphVizWriter::cmGraphVizWriter(const std::vector<cmLocalGenerator*>&
49 ,GraphHeader("node [\n fontsize = \"12\"\n];")
50 ,GraphNodePrefix("node")
51 ,GenerateForExecutables(true)
52 ,GenerateForStaticLibs(true)
53 ,GenerateForSharedLibs(true)
54 ,GenerateForModuleLibs(true)
55 ,GenerateForExternals(true)
56 ,LocalGenerators(localGenerators)
57 ,HaveTargetsAndLibs(false)
62 void cmGraphVizWriter::ReadSettings(const char* settingsFileName,
63 const char* fallbackSettingsFileName)
66 cmGlobalGenerator ggi;
67 ggi.SetCMakeInstance(&cm);
68 cmsys::auto_ptr<cmLocalGenerator> lg(ggi.CreateLocalGenerator());
69 cmMakefile *mf = lg->GetMakefile();
71 const char* inFileName = settingsFileName;
73 if ( !cmSystemTools::FileExists(inFileName) )
75 inFileName = fallbackSettingsFileName;
76 if ( !cmSystemTools::FileExists(inFileName) )
82 if ( !mf->ReadListFile(0, inFileName) )
84 cmSystemTools::Error("Problem opening GraphViz options file: ",
89 std::cout << "Reading GraphViz options file: " << inFileName << std::endl;
91 #define __set_if_set(var, cmakeDefinition) \
93 const char* value = mf->GetDefinition(cmakeDefinition); \
100 __set_if_set(this->GraphType, "GRAPHVIZ_GRAPH_TYPE");
101 __set_if_set(this->GraphName, "GRAPHVIZ_GRAPH_NAME");
102 __set_if_set(this->GraphHeader, "GRAPHVIZ_GRAPH_HEADER");
103 __set_if_set(this->GraphNodePrefix, "GRAPHVIZ_NODE_PREFIX");
105 #define __set_bool_if_set(var, cmakeDefinition) \
107 const char* value = mf->GetDefinition(cmakeDefinition); \
110 var = mf->IsOn(cmakeDefinition); \
114 __set_bool_if_set(this->GenerateForExecutables, "GRAPHVIZ_EXECUTABLES");
115 __set_bool_if_set(this->GenerateForStaticLibs, "GRAPHVIZ_STATIC_LIBS");
116 __set_bool_if_set(this->GenerateForSharedLibs, "GRAPHVIZ_SHARED_LIBS");
117 __set_bool_if_set(this->GenerateForModuleLibs, "GRAPHVIZ_MODULE_LIBS");
118 __set_bool_if_set(this->GenerateForExternals, "GRAPHVIZ_EXTERNAL_LIBS");
120 cmStdString ignoreTargetsRegexes;
121 __set_if_set(ignoreTargetsRegexes, "GRAPHVIZ_IGNORE_TARGETS");
123 this->TargetsToIgnoreRegex.clear();
124 if (ignoreTargetsRegexes.size() > 0)
126 std::vector<std::string> ignoreTargetsRegExVector;
127 cmSystemTools::ExpandListArgument(ignoreTargetsRegexes,
128 ignoreTargetsRegExVector);
129 for(std::vector<std::string>::const_iterator itvIt
130 = ignoreTargetsRegExVector.begin();
131 itvIt != ignoreTargetsRegExVector.end();
134 cmStdString currentRegexString(*itvIt);
135 cmsys::RegularExpression currentRegex;
136 if (!currentRegex.compile(currentRegexString.c_str()))
138 std::cerr << "Could not compile bad regex \"" << currentRegexString
139 << "\"" << std::endl;
141 this->TargetsToIgnoreRegex.push_back(currentRegex);
148 // Iterate over all targets and write for each one a graph which shows
149 // which other targets depend on it.
150 void cmGraphVizWriter::WriteTargetDependersFiles(const char* fileName)
152 this->CollectTargetsAndLibs();
154 for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
155 this->TargetPtrs.begin();
156 ptrIt != this->TargetPtrs.end();
159 if (ptrIt->second == NULL)
164 if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
169 std::string currentFilename = fileName;
170 currentFilename += ".";
171 currentFilename += ptrIt->first;
172 currentFilename += ".dependers";
174 cmGeneratedFileStream str(currentFilename.c_str());
180 std::set<std::string> insertedConnections;
181 std::set<std::string> insertedNodes;
183 std::cout << "Writing " << currentFilename << "..." << std::endl;
184 this->WriteHeader(str);
186 this->WriteDependerConnections(ptrIt->first.c_str(),
187 insertedNodes, insertedConnections, str);
189 this->WriteFooter(str);
194 // Iterate over all targets and write for each one a graph which shows
195 // on which targets it depends.
196 void cmGraphVizWriter::WritePerTargetFiles(const char* fileName)
198 this->CollectTargetsAndLibs();
200 for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
201 this->TargetPtrs.begin();
202 ptrIt != this->TargetPtrs.end();
205 if (ptrIt->second == NULL)
210 if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
215 std::set<std::string> insertedConnections;
216 std::set<std::string> insertedNodes;
218 std::string currentFilename = fileName;
219 currentFilename += ".";
220 currentFilename += ptrIt->first;
221 cmGeneratedFileStream str(currentFilename.c_str());
227 std::cout << "Writing " << currentFilename << "..." << std::endl;
228 this->WriteHeader(str);
230 this->WriteConnections(ptrIt->first.c_str(),
231 insertedNodes, insertedConnections, str);
232 this->WriteFooter(str);
238 void cmGraphVizWriter::WriteGlobalFile(const char* fileName)
240 this->CollectTargetsAndLibs();
242 cmGeneratedFileStream str(fileName);
247 this->WriteHeader(str);
249 std::cout << "Writing " << fileName << "..." << std::endl;
251 std::set<std::string> insertedConnections;
252 std::set<std::string> insertedNodes;
254 for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
255 this->TargetPtrs.begin();
256 ptrIt != this->TargetPtrs.end();
259 if (ptrIt->second == NULL)
264 if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
269 this->WriteConnections(ptrIt->first.c_str(),
270 insertedNodes, insertedConnections, str);
272 this->WriteFooter(str);
276 void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& str) const
278 str << this->GraphType << " " << this->GraphName << " {" << std::endl;
279 str << this->GraphHeader << std::endl;
283 void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& str) const
285 str << "}" << std::endl;
289 void cmGraphVizWriter::WriteConnections(const char* targetName,
290 std::set<std::string>& insertedNodes,
291 std::set<std::string>& insertedConnections,
292 cmGeneratedFileStream& str) const
294 std::map<cmStdString, const cmTarget* >::const_iterator targetPtrIt =
295 this->TargetPtrs.find(targetName);
297 if (targetPtrIt == this->TargetPtrs.end()) // not found at all
302 this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
304 if (targetPtrIt->second == NULL) // it's an external library
310 std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
312 const cmTarget::LinkLibraryVectorType* ll =
313 &(targetPtrIt->second->GetOriginalLinkLibraries());
315 for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
319 const char* libName = llit->first.c_str();
320 std::map<cmStdString, cmStdString>::const_iterator libNameIt =
321 this->TargetNamesNodes.find(libName);
323 // can happen e.g. if GRAPHVIZ_TARGET_IGNORE_REGEX is used
324 if(libNameIt == this->TargetNamesNodes.end())
329 std::string connectionName = myNodeName;
330 connectionName += "-";
331 connectionName += libNameIt->second;
332 if (insertedConnections.find(connectionName) == insertedConnections.end())
334 insertedConnections.insert(connectionName);
335 this->WriteNode(libName, this->TargetPtrs.find(libName)->second,
338 str << " \"" << myNodeName.c_str() << "\" -> \""
339 << libNameIt->second.c_str() << "\"";
340 str << " // " << targetName << " -> " << libName << std::endl;
341 this->WriteConnections(libName, insertedNodes, insertedConnections, str);
348 void cmGraphVizWriter::WriteDependerConnections(const char* targetName,
349 std::set<std::string>& insertedNodes,
350 std::set<std::string>& insertedConnections,
351 cmGeneratedFileStream& str) const
353 std::map<cmStdString, const cmTarget* >::const_iterator targetPtrIt =
354 this->TargetPtrs.find(targetName);
356 if (targetPtrIt == this->TargetPtrs.end()) // not found at all
361 this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
363 if (targetPtrIt->second == NULL) // it's an external library
369 std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
371 // now search who links against me
372 for(std::map<cmStdString, const cmTarget*>::const_iterator dependerIt =
373 this->TargetPtrs.begin();
374 dependerIt != this->TargetPtrs.end();
377 if (dependerIt->second == NULL)
382 if (this->GenerateForTargetType(dependerIt->second->GetType()) == false)
387 // Now we have a target, check whether it links against targetName.
388 // If so, draw a connection, and then continue with dependers on that one.
389 const cmTarget::LinkLibraryVectorType* ll =
390 &(dependerIt->second->GetOriginalLinkLibraries());
392 for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
396 std::string libName = llit->first.c_str();
397 if (libName == targetName)
399 // So this target links against targetName.
400 std::map<cmStdString, cmStdString>::const_iterator dependerNodeNameIt =
401 this->TargetNamesNodes.find(dependerIt->first);
403 if(dependerNodeNameIt != this->TargetNamesNodes.end())
405 std::string connectionName = dependerNodeNameIt->second;
406 connectionName += "-";
407 connectionName += myNodeName;
409 if (insertedConnections.find(connectionName) ==
410 insertedConnections.end())
412 insertedConnections.insert(connectionName);
413 this->WriteNode(dependerIt->first.c_str(), dependerIt->second,
416 str << " \"" << dependerNodeNameIt->second << "\" -> \""
417 << myNodeName << "\"";
418 str << " // " <<targetName<< " -> " <<dependerIt->first<<std::endl;
419 this->WriteDependerConnections(dependerIt->first.c_str(),
420 insertedNodes, insertedConnections, str);
433 void cmGraphVizWriter::WriteNode(const char* targetName,
434 const cmTarget* target,
435 std::set<std::string>& insertedNodes,
436 cmGeneratedFileStream& str) const
438 if (insertedNodes.find(targetName) == insertedNodes.end())
440 insertedNodes.insert(targetName);
441 std::map<cmStdString, cmStdString>::const_iterator nameIt =
442 this->TargetNamesNodes.find(targetName);
444 str << " \"" << nameIt->second.c_str() << "\" [ label=\""
445 << targetName << "\" shape=\"" << getShapeForTarget(target)
446 << "\"];" << std::endl;
451 void cmGraphVizWriter::CollectTargetsAndLibs()
453 if (this->HaveTargetsAndLibs == false)
455 this->HaveTargetsAndLibs = true;
456 int cnt = this->CollectAllTargets();
457 if (this->GenerateForExternals)
459 this->CollectAllExternalLibs(cnt);
465 int cmGraphVizWriter::CollectAllTargets()
468 // First pass get the list of all cmake targets
469 for (std::vector<cmLocalGenerator*>::const_iterator lit =
470 this->LocalGenerators.begin();
471 lit != this->LocalGenerators.end();
474 const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
475 for ( cmTargets::const_iterator tit = targets->begin();
476 tit != targets->end();
479 const char* realTargetName = tit->first.c_str();
480 if(this->IgnoreThisTarget(realTargetName))
482 // Skip ignored targets
485 //std::cout << "Found target: " << tit->first.c_str() << std::endl;
486 cmOStringStream ostr;
487 ostr << this->GraphNodePrefix << cnt++;
488 this->TargetNamesNodes[realTargetName] = ostr.str();
489 this->TargetPtrs[realTargetName] = &tit->second;
497 int cmGraphVizWriter::CollectAllExternalLibs(int cnt)
499 // Ok, now find all the stuff we link to that is not in cmake
500 for (std::vector<cmLocalGenerator*>::const_iterator lit =
501 this->LocalGenerators.begin();
502 lit != this->LocalGenerators.end();
505 const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
506 for ( cmTargets::const_iterator tit = targets->begin();
507 tit != targets->end();
510 const char* realTargetName = tit->first.c_str();
511 if (this->IgnoreThisTarget(realTargetName))
513 // Skip ignored targets
516 const cmTarget::LinkLibraryVectorType* ll =
517 &(tit->second.GetOriginalLinkLibraries());
518 for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
522 const char* libName = llit->first.c_str();
523 if (this->IgnoreThisTarget(libName))
525 // Skip ignored targets
529 std::map<cmStdString, const cmTarget*>::const_iterator tarIt =
530 this->TargetPtrs.find(libName);
531 if ( tarIt == this->TargetPtrs.end() )
533 cmOStringStream ostr;
534 ostr << this->GraphNodePrefix << cnt++;
535 this->TargetNamesNodes[libName] = ostr.str();
536 this->TargetPtrs[libName] = NULL;
537 //str << " \"" << ostr.c_str() << "\" [ label=\"" << libName
538 //<< "\" shape=\"ellipse\"];" << std::endl;
547 bool cmGraphVizWriter::IgnoreThisTarget(const char* name)
549 for(std::vector<cmsys::RegularExpression>::iterator itvIt
550 = this->TargetsToIgnoreRegex.begin();
551 itvIt != this->TargetsToIgnoreRegex.end();
554 cmsys::RegularExpression& regEx = *itvIt;
555 if (regEx.is_valid())
557 if (regEx.find(name))
568 bool cmGraphVizWriter::GenerateForTargetType(cmTarget::TargetType targetType)
573 case cmTarget::EXECUTABLE:
574 return this->GenerateForExecutables;
575 case cmTarget::STATIC_LIBRARY:
576 return this->GenerateForStaticLibs;
577 case cmTarget::SHARED_LIBRARY:
578 return this->GenerateForSharedLibs;
579 case cmTarget::MODULE_LIBRARY:
580 return this->GenerateForModuleLibs;