Graphviz is a great tool for the postmodern programmer. It's got a simple to integrate interface of text files and command line execution. Bjorn Freeman-Benson is visiting at the moment. He suggested we demonstrate graphviz using the dependencies between eclipse plugins. So here's the code that reads the eclipse manifest files and the resulting graph. This graph shows the dependencies between a significant subset of the standard eclipse 3.1 plugins. Bjorn and I have paired on this to bring you "postable" code that doesn't embarrass us too much.
import sys, time
from os import listdir, system
from os.path import join, isdir, exists
import os.path
from zipfile import is_zipfile, ZipFile
class DependencyGrapher:
def run(self, pluginDirectory):
files = [join(pluginDirectory, file) for file in listdir(pluginDirectory)]
graphText = self.process_files(files)
f=open("graph.txt","w")
graphText = 'digraph G {\n "' +\
"generated on " + time.asctime() + '";\n' +\
graphText + "\n}"
f.write(graphText)
f.close()
system("dot -Tpng -ograph.png graph.txt")
def process_files(self, files):
result = ""
for file in files:
if isdir(file):
result += self.process_plugin_directory(file)
if is_zipfile(file):
result += self.process_plugin_jar(file)
return result
def process_plugin_directory(self, directory):
propertiesFileName = join(directory,"META-INF","MANIFEST.MF")
if exists(propertiesFileName):
return self.process_properties(self.graphviz_friendly_name_from_plugin_filename(directory), open(propertiesFileName).read())
return ""
def process_plugin_jar(self, jarName):
try:
jar = ZipFile(jarName)
propertiesFile = jar.read("META-INF/MANIFEST.MF")
return self.process_properties(self.graphviz_friendly_name_from_plugin_filename(jarName), propertiesFile)
finally:
jar.close()
def process_properties(self, pluginName, properties):
result = ""
start = properties.find("Require-Bundle:")
if start != -1:
lines = [line.strip() for line in properties[start+len("Require-Bundle:"):].split('\n')]
for line in [line for line in lines if len(line) > 0]:
lastLine = not line.endswith(',')
if not lastLine:
line = line[:-1]
parts = line.split(';')
line = parts[0]
result += self.graphviz_friendly_name(pluginName) + " -> " + self.graphviz_friendly_name(line) + ";\n"
if lastLine:
break
return result
def graphviz_friendly_name(self, line):
return line.replace('.','_')
def graphviz_friendly_name_from_plugin_filename(self, fileName):
return self.graphviz_friendly_name(os.path.split(fileName)[-1].split('_')[0])
if __name__ == "__main__":
if len(sys.argv) != 2:
print "need plugin directory location"
print 'e.g. python dependencies.py "C:\Program Files\eclipse\plugins"'
sys.exit(1)
pluginDirectory = sys.argv[1]
DependencyGrapher().run(pluginDirectory)
Posted by ivan at February 5, 2006 7:32 PM