YaST/Development/Graphs of includes and imports/Ycp graph
From openSUSE
#!/usr/bin/env python
import os
import sys
import re
##
# Constants:
YaST_INCLUDE_DIR = "/usr/share/YaST2/include/"
YaST_IMPORT_DIR = "/usr/share/YaST2/modules/"
class Pair:
def __init__(self, first, sec):
self.first = first
self.second = sec
class List:
def __init__(self, list=None):
self._items = []
if list is not None:
for item in list:
self.append(item)
def __len__(self):
return len(self._items)
def __iter__(self):
return self._items.__iter__()
def append(self, name):
for item in self._items:
if item == name:
return
self._items.append(name)
def getItems(self):
return self._items
def isIn(self, name):
for item in self._items:
if name == item:
return True
return False
##
# Read files from include or import clause
include_files = List()
import_files = List()
##
# List of includes/imports which must be done in next cycle
new_includes = List()
new_imports = List()
##
# List of connections (Pair)
includes = []
imports = []
##
# List of modules which should be scanned into.
# None means all modules
important_modules = None
def usage():
print """
This program generate dot definition of graph to stdout by scanning given
files recursively.
"""
print "Usage:", sys.argv[0], "filename max_depth [list of modules to scan]"
print " ", " if no modules given, all modules will be scaned"
print ""
print "Example:", sys.argv[0], "src/clients/bootloader.ycp 10 Bootloader BootGRUB"
print " ", " - will scan Bootloader and BootGRUB modules"
print " ", sys.argv[0], "src/clients/bootloader.ycp 10 \"\""
print " ", " - won't scan any modules"
print " ", sys.argv[0], "src/clients/bootloader.ycp 10"
print " ", " - will scan all modules"
print ""
print "How to use this program:"
print " Generate graph definition into graph.txt:"
print " $", sys.argv[0], "src/squid.ycp 10 Squid > graph.txt"
print " Create graph.png from graph.txt:"
print " $ dot -Tpng -ograph.png graph.txt"
print " See generated graph:"
print " $ display graph.png"
def printDotGraph(include_files, import_files, includes, imports):
color_import = "blue"
color_include = "black"
print """
digraph "include/import" {
"""
for item in include_files:
print " ",
print "\"" + item + "\" [fontcolor=" + color_include + "];"
for item in import_files:
print " ",
print "\"" + item + "\"[fontcolor=" + color_import + "];"
for conn in includes:
print " ",
print "\"" + conn.first + "\"",
print " -> ",
print "\"" + conn.second + "\"", "[ color=\"" + color_include + "\"]"
for conn in imports:
print " ",
print "\"" + conn.first + "\"",
print " -> ",
print "\"" + conn.second + "\"", "[ color=\"" + color_import + "\"]"
print """
}
"""
def getNameFromFilename(filename):
pattern = re.compile(r'^/{0,}([^/]+/){0,}([^/]+)(\.[^/]*){1,}$')
match = pattern.match(filename)
if match is not None:
return match.group(2)
else:
return filename
def scanFile(name, filename):
p_include = re.compile(r'^.*include.*"(.*)".*$')
p_import = re.compile(r'^.*import.*"(.*)".*$')
try:
file = open(filename, "r")
except IOError, e:
print >>sys.stderr, "Error: Unable to read from " +filename
return
line = file.readline()
while len(line) > 0:
# scan includes
match = p_include.match(line)
if match is not None:
tmp = match.group(1)
# if not already scanned
if not include_files.isIn(tmp):
new_includes.append(tmp)
include_files.append(tmp)
includes.append(Pair(name, tmp))
# scan imports
match = p_import.match(line)
if match is not None:
tmp = match.group(1)
# if this modules is important for me
if important_modules is None or important_modules.isIn(tmp):
# if not already scanned
if not import_files.isIn(tmp):
new_imports.append(tmp)
import_files.append(tmp)
imports.append(Pair(name, tmp))
line = file.readline()
if __name__ == "__main__":
if len(sys.argv) < 3:
usage()
sys.exit(1)
try:
max_depth = int(sys.argv[2])
except:
print >>sys.stderr, "Error: Second argument is not a number."
sys.exit(1)
# set up important_modules
if len(sys.argv) > 3:
important_modules = List(sys.argv[3:])
else:
important_modules = None
current_depth = 0
print >>sys.stderr, "Start scanning"
filename = sys.argv[1]
scanFile(getNameFromFilename(filename), filename)
while (len(new_includes) > 0 or len(new_imports) > 0) and \
current_depth < max_depth:
new_includes_tmp = new_includes
new_imports_tmp = new_imports
new_includes = []
new_imports = []
for filename in new_includes_tmp:
scanFile(filename, YaST_INCLUDE_DIR + filename)
for filename in new_imports_tmp:
scanFile(filename, YaST_IMPORT_DIR + filename + ".ycp")
current_depth += 1
print >>sys.stderr, "Scanning successfuly stoped in depth:", current_depth
printDotGraph(include_files, import_files, includes, imports)
sys.exit(0)