Imported Upstream version 2.8.12.2
[platform/upstream/cmake.git] / Source / cmGraphVizWriter.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
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"
17
18
19
20 static const char* getShapeForTarget(const cmTarget* target)
21 {
22   if (!target)
23     {
24     return "ellipse";
25     }
26
27   switch ( target->GetType() )
28     {
29     case cmTarget::EXECUTABLE:
30       return "house";
31     case cmTarget::STATIC_LIBRARY:
32       return "diamond";
33     case cmTarget::SHARED_LIBRARY:
34       return "polygon";
35     case cmTarget::MODULE_LIBRARY:
36       return "octagon";
37     default:
38       break;
39     }
40
41   return "box";
42 }
43
44
45 cmGraphVizWriter::cmGraphVizWriter(const std::vector<cmLocalGenerator*>&
46                                                                localGenerators)
47 :GraphType("digraph")
48 ,GraphName("GG")
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)
58 {
59 }
60
61
62 void cmGraphVizWriter::ReadSettings(const char* settingsFileName,
63                                     const char* fallbackSettingsFileName)
64 {
65   cmake cm;
66   cmGlobalGenerator ggi;
67   ggi.SetCMakeInstance(&cm);
68   cmsys::auto_ptr<cmLocalGenerator> lg(ggi.CreateLocalGenerator());
69   cmMakefile *mf = lg->GetMakefile();
70
71   const char* inFileName = settingsFileName;
72
73   if ( !cmSystemTools::FileExists(inFileName) )
74     {
75     inFileName = fallbackSettingsFileName;
76     if ( !cmSystemTools::FileExists(inFileName) )
77       {
78       return;
79       }
80     }
81
82   if ( !mf->ReadListFile(0, inFileName) )
83     {
84     cmSystemTools::Error("Problem opening GraphViz options file: ",
85                          inFileName);
86     return;
87     }
88
89   std::cout << "Reading GraphViz options file: " << inFileName << std::endl;
90
91 #define __set_if_set(var, cmakeDefinition) \
92   { \
93   const char* value = mf->GetDefinition(cmakeDefinition); \
94   if ( value ) \
95     { \
96     var = value; \
97     } \
98   }
99
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");
104
105 #define __set_bool_if_set(var, cmakeDefinition) \
106   { \
107   const char* value = mf->GetDefinition(cmakeDefinition); \
108   if ( value ) \
109     { \
110     var = mf->IsOn(cmakeDefinition); \
111     } \
112   }
113
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");
119
120   cmStdString ignoreTargetsRegexes;
121   __set_if_set(ignoreTargetsRegexes, "GRAPHVIZ_IGNORE_TARGETS");
122
123   this->TargetsToIgnoreRegex.clear();
124   if (ignoreTargetsRegexes.size() > 0)
125     {
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();
132         ++ itvIt )
133       {
134       cmStdString currentRegexString(*itvIt);
135       cmsys::RegularExpression currentRegex;
136       if (!currentRegex.compile(currentRegexString.c_str()))
137         {
138         std::cerr << "Could not compile bad regex \"" << currentRegexString
139                   << "\"" << std::endl;
140         }
141       this->TargetsToIgnoreRegex.push_back(currentRegex);
142       }
143     }
144
145 }
146
147
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)
151 {
152   this->CollectTargetsAndLibs();
153
154   for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
155                                                       this->TargetPtrs.begin();
156       ptrIt != this->TargetPtrs.end();
157       ++ptrIt)
158     {
159     if (ptrIt->second == NULL)
160       {
161       continue;
162       }
163
164     if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
165       {
166       continue;
167       }
168
169     std::string currentFilename = fileName;
170     currentFilename += ".";
171     currentFilename += ptrIt->first;
172     currentFilename += ".dependers";
173
174     cmGeneratedFileStream str(currentFilename.c_str());
175     if ( !str )
176       {
177       return;
178       }
179
180     std::set<std::string> insertedConnections;
181     std::set<std::string> insertedNodes;
182
183     std::cout << "Writing " << currentFilename << "..." << std::endl;
184     this->WriteHeader(str);
185
186     this->WriteDependerConnections(ptrIt->first.c_str(),
187                                    insertedNodes, insertedConnections, str);
188
189     this->WriteFooter(str);
190     }
191 }
192
193
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)
197 {
198   this->CollectTargetsAndLibs();
199
200   for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
201                                                       this->TargetPtrs.begin();
202       ptrIt != this->TargetPtrs.end();
203       ++ptrIt)
204     {
205     if (ptrIt->second == NULL)
206       {
207       continue;
208       }
209
210     if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
211       {
212       continue;
213       }
214
215     std::set<std::string> insertedConnections;
216     std::set<std::string> insertedNodes;
217
218     std::string currentFilename = fileName;
219     currentFilename += ".";
220     currentFilename += ptrIt->first;
221     cmGeneratedFileStream str(currentFilename.c_str());
222     if ( !str )
223       {
224       return;
225       }
226
227     std::cout << "Writing " << currentFilename << "..." << std::endl;
228     this->WriteHeader(str);
229
230     this->WriteConnections(ptrIt->first.c_str(),
231                               insertedNodes, insertedConnections, str);
232     this->WriteFooter(str);
233     }
234
235 }
236
237
238 void cmGraphVizWriter::WriteGlobalFile(const char* fileName)
239 {
240   this->CollectTargetsAndLibs();
241
242   cmGeneratedFileStream str(fileName);
243   if ( !str )
244     {
245     return;
246     }
247   this->WriteHeader(str);
248
249   std::cout << "Writing " << fileName << "..." << std::endl;
250
251   std::set<std::string> insertedConnections;
252   std::set<std::string> insertedNodes;
253
254   for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
255                                                       this->TargetPtrs.begin();
256       ptrIt != this->TargetPtrs.end();
257       ++ptrIt)
258     {
259     if (ptrIt->second == NULL)
260       {
261       continue;
262       }
263
264     if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
265       {
266       continue;
267       }
268
269     this->WriteConnections(ptrIt->first.c_str(),
270                               insertedNodes, insertedConnections, str);
271     }
272   this->WriteFooter(str);
273 }
274
275
276 void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& str) const
277 {
278   str << this->GraphType << " " << this->GraphName << " {" << std::endl;
279   str << this->GraphHeader << std::endl;
280 }
281
282
283 void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& str) const
284 {
285   str << "}" << std::endl;
286 }
287
288
289 void cmGraphVizWriter::WriteConnections(const char* targetName,
290                                     std::set<std::string>& insertedNodes,
291                                     std::set<std::string>& insertedConnections,
292                                     cmGeneratedFileStream& str) const
293 {
294   std::map<cmStdString, const cmTarget* >::const_iterator targetPtrIt =
295                                              this->TargetPtrs.find(targetName);
296
297   if (targetPtrIt == this->TargetPtrs.end())  // not found at all
298     {
299     return;
300     }
301
302   this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
303
304   if (targetPtrIt->second == NULL) // it's an external library
305     {
306     return;
307     }
308
309
310   std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
311
312   const cmTarget::LinkLibraryVectorType* ll =
313                             &(targetPtrIt->second->GetOriginalLinkLibraries());
314
315   for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
316        llit != ll->end();
317        ++ llit )
318     {
319     const char* libName = llit->first.c_str();
320     std::map<cmStdString, cmStdString>::const_iterator libNameIt =
321                                           this->TargetNamesNodes.find(libName);
322
323     // can happen e.g. if GRAPHVIZ_TARGET_IGNORE_REGEX is used
324     if(libNameIt == this->TargetNamesNodes.end())
325       {
326       continue;
327       }
328
329     std::string connectionName = myNodeName;
330     connectionName += "-";
331     connectionName += libNameIt->second;
332     if (insertedConnections.find(connectionName) == insertedConnections.end())
333       {
334       insertedConnections.insert(connectionName);
335       this->WriteNode(libName, this->TargetPtrs.find(libName)->second,
336                       insertedNodes, str);
337
338       str << "    \"" << myNodeName.c_str() << "\" -> \""
339           << libNameIt->second.c_str() << "\"";
340       str << " // " << targetName << " -> " << libName << std::endl;
341       this->WriteConnections(libName, insertedNodes, insertedConnections, str);
342       }
343     }
344
345 }
346
347
348 void cmGraphVizWriter::WriteDependerConnections(const char* targetName,
349                                     std::set<std::string>& insertedNodes,
350                                     std::set<std::string>& insertedConnections,
351                                     cmGeneratedFileStream& str) const
352 {
353   std::map<cmStdString, const cmTarget* >::const_iterator targetPtrIt =
354                                              this->TargetPtrs.find(targetName);
355
356   if (targetPtrIt == this->TargetPtrs.end())  // not found at all
357     {
358     return;
359     }
360
361   this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
362
363   if (targetPtrIt->second == NULL) // it's an external library
364     {
365     return;
366     }
367
368
369   std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
370
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();
375       ++dependerIt)
376     {
377     if (dependerIt->second == NULL)
378       {
379       continue;
380       }
381
382     if (this->GenerateForTargetType(dependerIt->second->GetType()) == false)
383       {
384       continue;
385       }
386
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());
391
392     for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
393          llit != ll->end();
394          ++ llit )
395       {
396       std::string libName = llit->first.c_str();
397       if (libName == targetName)
398         {
399         // So this target links against targetName.
400         std::map<cmStdString, cmStdString>::const_iterator dependerNodeNameIt =
401                                 this->TargetNamesNodes.find(dependerIt->first);
402
403         if(dependerNodeNameIt != this->TargetNamesNodes.end())
404           {
405           std::string connectionName = dependerNodeNameIt->second;
406           connectionName += "-";
407           connectionName += myNodeName;
408
409           if (insertedConnections.find(connectionName) ==
410                                                      insertedConnections.end())
411             {
412             insertedConnections.insert(connectionName);
413             this->WriteNode(dependerIt->first.c_str(), dependerIt->second,
414                             insertedNodes, str);
415
416             str << "    \"" << dependerNodeNameIt->second << "\" -> \""
417                 << myNodeName << "\"";
418             str << " // " <<targetName<< " -> " <<dependerIt->first<<std::endl;
419             this->WriteDependerConnections(dependerIt->first.c_str(),
420                                       insertedNodes, insertedConnections, str);
421             }
422
423
424           }
425         break;
426         }
427       }
428     }
429
430 }
431
432
433 void cmGraphVizWriter::WriteNode(const char* targetName,
434                                  const cmTarget* target,
435                                  std::set<std::string>& insertedNodes,
436                                  cmGeneratedFileStream& str) const
437 {
438   if (insertedNodes.find(targetName) == insertedNodes.end())
439   {
440     insertedNodes.insert(targetName);
441     std::map<cmStdString, cmStdString>::const_iterator nameIt =
442                                        this->TargetNamesNodes.find(targetName);
443
444     str << "    \"" << nameIt->second.c_str() << "\" [ label=\""
445         << targetName <<  "\" shape=\"" << getShapeForTarget(target)
446         << "\"];" << std::endl;
447   }
448 }
449
450
451 void cmGraphVizWriter::CollectTargetsAndLibs()
452 {
453   if (this->HaveTargetsAndLibs == false)
454     {
455     this->HaveTargetsAndLibs = true;
456     int cnt = this->CollectAllTargets();
457     if (this->GenerateForExternals)
458       {
459       this->CollectAllExternalLibs(cnt);
460       }
461     }
462 }
463
464
465 int cmGraphVizWriter::CollectAllTargets()
466 {
467   int cnt = 0;
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();
472        ++ lit )
473     {
474     const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
475     for ( cmTargets::const_iterator tit = targets->begin();
476           tit != targets->end();
477           ++ tit )
478       {
479       const char* realTargetName = tit->first.c_str();
480       if(this->IgnoreThisTarget(realTargetName))
481         {
482         // Skip ignored targets
483         continue;
484         }
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;
490       }
491     }
492
493   return cnt;
494 }
495
496
497 int cmGraphVizWriter::CollectAllExternalLibs(int cnt)
498 {
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();
503        ++ lit )
504     {
505     const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
506     for ( cmTargets::const_iterator tit = targets->begin();
507           tit != targets->end();
508           ++ tit )
509       {
510       const char* realTargetName = tit->first.c_str();
511       if (this->IgnoreThisTarget(realTargetName))
512         {
513         // Skip ignored targets
514         continue;
515         }
516       const cmTarget::LinkLibraryVectorType* ll =
517                                      &(tit->second.GetOriginalLinkLibraries());
518       for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
519            llit != ll->end();
520            ++ llit )
521         {
522         const char* libName = llit->first.c_str();
523         if (this->IgnoreThisTarget(libName))
524           {
525           // Skip ignored targets
526           continue;
527           }
528
529         std::map<cmStdString, const cmTarget*>::const_iterator tarIt =
530                                                 this->TargetPtrs.find(libName);
531         if ( tarIt == this->TargetPtrs.end() )
532           {
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;
539           }
540         }
541       }
542     }
543    return cnt;
544 }
545
546
547 bool cmGraphVizWriter::IgnoreThisTarget(const char* name)
548 {
549   for(std::vector<cmsys::RegularExpression>::iterator itvIt
550                                           = this->TargetsToIgnoreRegex.begin();
551       itvIt != this->TargetsToIgnoreRegex.end();
552       ++ itvIt )
553     {
554     cmsys::RegularExpression& regEx = *itvIt;
555     if (regEx.is_valid())
556       {
557       if (regEx.find(name))
558         {
559         return true;
560         }
561       }
562     }
563
564   return false;
565 }
566
567
568 bool cmGraphVizWriter::GenerateForTargetType(cmTarget::TargetType targetType)
569                                                                           const
570 {
571   switch (targetType)
572   {
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;
581     default:
582       break;
583   }
584   return false;
585 }