CMake/Source/cmGraphVizWriter.cxx
Alex Neundorf 78c86f4542 Exclude targets from the graphviz file based on a regex
This commit adds support for a GRAPHVIZ_TARGET_IGNORE_REGEX variable
which can be set() in CMakeGraphVizOptions.cmake.
Targets matching this regex will be skipped when generating the graphviz
graphs.

Alex
2010-11-14 19:47:28 +01:00

436 lines
12 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmGraphVizWriter.h"
#include "cmMakefile.h"
#include "cmLocalGenerator.h"
#include "cmGlobalGenerator.h"
#include "cmGeneratedFileStream.h"
#include <memory>
static const char* getShapeForTarget(const cmTarget* target)
{
if (!target)
{
return "ellipse";
}
switch ( target->GetType() )
{
case cmTarget::EXECUTABLE:
return "house";
case cmTarget::STATIC_LIBRARY:
return "diamond";
case cmTarget::SHARED_LIBRARY:
return "polygon";
case cmTarget::MODULE_LIBRARY:
return "octagon";
default:
break;
}
return "box";
}
cmGraphVizWriter::cmGraphVizWriter(const std::vector<cmLocalGenerator*>&
localGenerators)
:GraphType("digraph")
,GraphName("GG")
,GraphHeader("node [\n fontsize = \"12\"\n];")
,GraphNodePrefix("node")
,GenerateForExecutables(true)
,GenerateForStaticLibs(true)
,GenerateForSharedLibs(true)
,GenerateForModuleLibs(true)
,LocalGenerators(localGenerators)
,HaveTargetsAndLibs(false)
{
}
void cmGraphVizWriter::ReadSettings(const char* settingsFileName,
const char* fallbackSettingsFileName)
{
cmake cm;
cmGlobalGenerator ggi;
ggi.SetCMakeInstance(&cm);
std::auto_ptr<cmLocalGenerator> lg(ggi.CreateLocalGenerator());
cmMakefile *mf = lg->GetMakefile();
const char* inFileName = settingsFileName;
if ( !cmSystemTools::FileExists(inFileName) )
{
inFileName = fallbackSettingsFileName;
if ( !cmSystemTools::FileExists(inFileName) )
{
return;
}
}
if ( !mf->ReadListFile(0, inFileName) )
{
cmSystemTools::Error("Problem opening GraphViz options file: ",
inFileName);
return;
}
std::cout << "Reading GraphViz options file: " << inFileName << std::endl;
#define __set_if_set(var, cmakeDefinition) \
{ \
const char* value = mf->GetDefinition(cmakeDefinition); \
if ( value ) \
{ \
var = value; \
} \
}
__set_if_set(this->GraphType, "GRAPHVIZ_GRAPH_TYPE");
__set_if_set(this->GraphName, "GRAPHVIZ_GRAPH_NAME");
__set_if_set(this->GraphHeader, "GRAPHVIZ_GRAPH_HEADER");
__set_if_set(this->GraphNodePrefix, "GRAPHVIZ_NODE_PREFIX");
#define __set_bool_if_set(var, cmakeDefinition) \
{ \
const char* value = mf->GetDefinition(cmakeDefinition); \
if ( value ) \
{ \
var = mf->IsOn(cmakeDefinition); \
} \
}
__set_bool_if_set(this->GenerateForExecutables, "GRAPHVIZ_EXECUTABLES");
__set_bool_if_set(this->GenerateForStaticLibs, "GRAPHVIZ_STATIC_LIBS");
__set_bool_if_set(this->GenerateForSharedLibs, "GRAPHVIZ_SHARED_LIBS");
__set_bool_if_set(this->GenerateForModuleLibs , "GRAPHVIZ_MODULE_LIBS");
cmStdString tmpRegexString;
__set_if_set(tmpRegexString, "GRAPHVIZ_TARGET_IGNORE_REGEX");
if (tmpRegexString.size() > 0)
{
if (!this->TargetIgnoreRegex.compile(tmpRegexString.c_str()))
{
std::cerr << "Could not compile bad regex \"" << tmpRegexString << "\""
<< std::endl;
}
}
this->TargetsToIgnore.clear();
const char* ignoreTargets = mf->GetDefinition("GRAPHVIZ_IGNORE_TARGETS");
if ( ignoreTargets )
{
std::vector<std::string> ignoreTargetsVector;
cmSystemTools::ExpandListArgument(ignoreTargets,ignoreTargetsVector);
for(std::vector<std::string>::iterator itvIt = ignoreTargetsVector.begin();
itvIt != ignoreTargetsVector.end();
++ itvIt )
{
this->TargetsToIgnore.insert(itvIt->c_str());
}
}
}
void cmGraphVizWriter::WritePerTargetFiles(const char* fileName)
{
this->CollectTargetsAndLibs();
for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
this->TargetPtrs.begin();
ptrIt != this->TargetPtrs.end();
++ptrIt)
{
if (ptrIt->second == NULL)
{
continue;
}
if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
{
continue;
}
std::set<std::string> insertedConnections;
std::set<std::string> insertedNodes;
std::string currentFilename = fileName;
currentFilename += ".";
currentFilename += ptrIt->first;
cmGeneratedFileStream str(currentFilename.c_str());
if ( !str )
{
return;
}
std::cout << "Writing " << currentFilename << "..." << std::endl;
this->WriteHeader(str);
this->WriteConnections(ptrIt->first.c_str(),
insertedNodes, insertedConnections, str);
this->WriteFooter(str);
}
}
void cmGraphVizWriter::WriteGlobalFile(const char* fileName)
{
this->CollectTargetsAndLibs();
cmGeneratedFileStream str(fileName);
if ( !str )
{
return;
}
this->WriteHeader(str);
std::cout << "Writing " << fileName << "..." << std::endl;
std::set<std::string> insertedConnections;
std::set<std::string> insertedNodes;
for(std::map<cmStdString, const cmTarget*>::const_iterator ptrIt =
this->TargetPtrs.begin();
ptrIt != this->TargetPtrs.end();
++ptrIt)
{
if (ptrIt->second == NULL)
{
continue;
}
if (this->GenerateForTargetType(ptrIt->second->GetType()) == false)
{
continue;
}
this->WriteConnections(ptrIt->first.c_str(),
insertedNodes, insertedConnections, str);
}
this->WriteFooter(str);
}
void cmGraphVizWriter::WriteHeader(cmGeneratedFileStream& str) const
{
str << this->GraphType << " " << this->GraphName << " {" << std::endl;
str << this->GraphHeader << std::endl;
}
void cmGraphVizWriter::WriteFooter(cmGeneratedFileStream& str) const
{
str << "}" << std::endl;
}
void cmGraphVizWriter::WriteConnections(const char* targetName,
std::set<std::string>& insertedNodes,
std::set<std::string>& insertedConnections,
cmGeneratedFileStream& str) const
{
std::map<cmStdString, const cmTarget* >::const_iterator targetPtrIt =
this->TargetPtrs.find(targetName);
if (targetPtrIt == this->TargetPtrs.end()) // not found at all
{
return;
}
this->WriteNode(targetName, targetPtrIt->second, insertedNodes, str);
if (targetPtrIt->second == NULL) // it's an external library
{
return;
}
std::string myNodeName = this->TargetNamesNodes.find(targetName)->second;
const cmTarget::LinkLibraryVectorType* ll =
&(targetPtrIt->second->GetOriginalLinkLibraries());
for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
llit != ll->end();
++ llit )
{
const char* libName = llit->first.c_str();
std::map<cmStdString, cmStdString>::const_iterator libNameIt =
this->TargetNamesNodes.find(libName);
std::string connectionName = myNodeName;
connectionName += "-";
connectionName += libNameIt->second;
if (insertedConnections.find(connectionName) == insertedConnections.end())
{
insertedConnections.insert(connectionName);
this->WriteNode(libName, this->TargetPtrs.find(libName)->second,
insertedNodes, str);
str << " \"" << myNodeName.c_str() << "\" -> \""
<< libNameIt->second.c_str() << "\"";
str << " // " << targetName << " -> " << libName << std::endl;
this->WriteConnections(libName, insertedNodes, insertedConnections, str);
}
}
}
void cmGraphVizWriter::WriteNode(const char* targetName,
const cmTarget* target,
std::set<std::string>& insertedNodes,
cmGeneratedFileStream& str) const
{
if (insertedNodes.find(targetName) == insertedNodes.end())
{
insertedNodes.insert(targetName);
std::map<cmStdString, cmStdString>::const_iterator nameIt =
this->TargetNamesNodes.find(targetName);
str << " \"" << nameIt->second.c_str() << "\" [ label=\""
<< targetName << "\" shape=\"" << getShapeForTarget(target)
<< "\"];" << std::endl;
}
}
void cmGraphVizWriter::CollectTargetsAndLibs()
{
if (this->HaveTargetsAndLibs == false)
{
this->HaveTargetsAndLibs = true;
int cnt = this->CollectAllTargets();
this->CollectAllExternalLibs(cnt);
}
}
int cmGraphVizWriter::CollectAllTargets()
{
int cnt = 0;
// First pass get the list of all cmake targets
for (std::vector<cmLocalGenerator*>::const_iterator lit =
this->LocalGenerators.begin();
lit != this->LocalGenerators.end();
++ lit )
{
const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
for ( cmTargets::const_iterator tit = targets->begin();
tit != targets->end();
++ tit )
{
const char* realTargetName = tit->first.c_str();
if(this->IgnoreThisTarget(realTargetName))
{
// Skip ignored targets
continue;
}
//std::cout << "Found target: " << tit->first.c_str() << std::endl;
cmOStringStream ostr;
ostr << this->GraphNodePrefix << cnt++;
this->TargetNamesNodes[realTargetName] = ostr.str();
this->TargetPtrs[realTargetName] = &tit->second;
}
}
return cnt;
}
int cmGraphVizWriter::CollectAllExternalLibs(int cnt)
{
// Ok, now find all the stuff we link to that is not in cmake
for (std::vector<cmLocalGenerator*>::const_iterator lit =
this->LocalGenerators.begin();
lit != this->LocalGenerators.end();
++ lit )
{
const cmTargets* targets = &((*lit)->GetMakefile()->GetTargets());
for ( cmTargets::const_iterator tit = targets->begin();
tit != targets->end();
++ tit )
{
const char* realTargetName = tit->first.c_str();
if (this->IgnoreThisTarget(realTargetName))
{
// Skip ignored targets
continue;
}
const cmTarget::LinkLibraryVectorType* ll =
&(tit->second.GetOriginalLinkLibraries());
for (cmTarget::LinkLibraryVectorType::const_iterator llit = ll->begin();
llit != ll->end();
++ llit )
{
const char* libName = llit->first.c_str();
if (this->IgnoreThisTarget(libName))
{
// Skip ignored targets
continue;
}
std::map<cmStdString, const cmTarget*>::const_iterator tarIt =
this->TargetPtrs.find(libName);
if ( tarIt == this->TargetPtrs.end() )
{
cmOStringStream ostr;
ostr << this->GraphNodePrefix << cnt++;
this->TargetNamesNodes[libName] = ostr.str();
this->TargetPtrs[libName] = NULL;
//str << " \"" << ostr.c_str() << "\" [ label=\"" << libName
//<< "\" shape=\"ellipse\"];" << std::endl;
}
}
}
}
return cnt;
}
bool cmGraphVizWriter::IgnoreThisTarget(const char* name)
{
if (this->TargetIgnoreRegex.is_valid())
{
if (this->TargetIgnoreRegex.find(name))
{
return true;
}
}
return (this->TargetsToIgnore.find(name) != this->TargetsToIgnore.end());
}
bool cmGraphVizWriter::GenerateForTargetType(cmTarget::TargetType targetType)
const
{
switch (targetType)
{
case cmTarget::EXECUTABLE:
return this->GenerateForExecutables;
case cmTarget::STATIC_LIBRARY:
return this->GenerateForStaticLibs;
case cmTarget::SHARED_LIBRARY:
return this->GenerateForSharedLibs;
case cmTarget::MODULE_LIBRARY:
return this->GenerateForModuleLibs;
default:
break;
}
return false;
}