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