diff tests/analyzers/dep_analyzer.py @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children 81641655bc38
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/analyzers/dep_analyzer.py	Sun Jun 29 18:44:17 2008 +0000
@@ -0,0 +1,225 @@
+#!/usr/bin/python
+import sys, re, os
+
+try:
+	from os.shutil import copyfile
+
+except ImportError:
+	def copyfile(src,dest):
+		srcf = open(src)
+		destf = open(dest,"w")
+		destf.write( srcf.read() )
+		destf.close()
+		srcf.close()
+
+
+if '.' not in sys.path:
+	sys.path.append('.')
+from utils.util_scripts.path import path
+from _allowed_dependencies import ALLOWED_MODULE_DEPS
+
+_S = os.path.sep
+ROOTDIRNAME = 'engine%score' % _S
+FILE_DEPS_OUT = 'doc%sdependencies%sfiledeps' % (_S, _S)
+DIR_DEPS_OUT = 'doc%sdependencies%sdirdeps' % (_S, _S)
+MODULE_DEPS_OUT = 'doc%sdependencies%smoduledeps' % (_S, _S)
+SKIPPED_PROVIDERS = []
+SKIPPED_USERS = []
+
+reInc = re.compile('#include "(.*?)"')
+
+def add_to_dictset(d, key, val):
+	try:
+		d[key].add(val)
+	except KeyError:
+		d[key] = set([val])
+	
+# return error string in case of possible error
+unknown_modules = []
+def check_dep_error(allowed_deps, user, provider):
+	global unknown_modules
+
+	msg = ''
+	try:
+		if not provider in allowed_deps[user]:
+			msg = 'Illegal dependency between %s -> %s, % s can use only:\n' % (user, provider, user)
+			for d in allowed_deps[user]:
+				msg += '   %s\n' % d
+	except KeyError:
+		print unknown_modules
+		if user not in unknown_modules:
+			msg = 'Unknown module %s found in static check\n' % user
+			msg += '   please adjust dep_analyzer script to match new structure'
+			unknown_modules.append(user)
+	return msg
+
+def get_file2inc(sources):
+	file2inc = {}
+	for f in sources:
+		inComment = False
+		for line in open(f):
+			if not inComment and line.find('/*') != -1:
+				inComment = True
+				continue
+			elif inComment:
+				if line.find('*/') != -1:
+					inComment = False
+				continue
+			elif line.strip().startswith('//'):
+				continue
+			m = reInc.search(line)
+			if m:
+				add_to_dictset(file2inc, f, m.group(1).replace( '/', _S ))
+
+	return file2inc
+
+def fill_dep_infos(file2inc, fileUser2provider, dirUser2provider, moduleUser2provider, unknownIncludes, dirclusters):
+	for f, incs in file2inc.items():
+		#if f.find('engine.cpp') != -1:
+		#	import pdb; pdb.set_trace()
+		skip = False
+		for user in SKIPPED_USERS:
+			if f.find(user) != -1:
+				skip = True
+				break
+		if skip:
+			continue
+			
+		for i in incs:
+			user = str(f.dirname()).replace(ROOTDIRNAME + _S, '')
+			header = path((f.dirname() / path(i)).abspath().split(ROOTDIRNAME + _S)[1])
+			if not header.isfile():
+				header = path(ROOTDIRNAME) + _S + path(i)
+				if not header.isfile():
+					add_to_dictset(unknownIncludes, str(f), str(i))
+					continue
+			provider = str(header.dirname()).replace(ROOTDIRNAME + _S, '')
+			skip = False
+			for skipped in SKIPPED_PROVIDERS:
+				if header.find(skipped) != -1:
+					skip = True
+					break
+			if skip:
+				continue
+			add_to_dictset(dirUser2provider, user, provider)
+			usermodule = user.split(_S)[0]
+			providermodule = provider.split(_S)[0]
+			userfile = user.split(_S)[-1].split('.')[0]
+			providerfile = provider.split(_S)[-1].split('.')[0]
+
+			add_to_dictset(dirclusters, usermodule, user)
+			add_to_dictset(dirclusters, providermodule, provider)
+			add_to_dictset(moduleUser2provider, usermodule, providermodule)
+			add_to_dictset(fileUser2provider, userfile, providerfile)
+
+def write_dot_file(fname, contents):
+	lines = []
+	a = lines.append
+	a('digraph "source tree" {')
+	a('    overlap=scale;')
+	a('    size="8,10";')
+	a('    ratio="fill";')
+	a('    fontsize="16";')
+	a('    fontname="Helvetica";')
+	a('    clusterrank="local";')
+	if type(contents) in (list, tuple):
+		lines += contents
+	else:
+		lines.append(contents)
+	a('}')
+	open(fname, 'w').write('\n'.join(lines))
+
+def get_cluster_str(ind, elements, label):
+	lines = []
+	a = lines.append
+	a('subgraph cluster_%d {' % ind)
+	a('    style=filled;')
+	a('    color=lightgrey;')
+	a('    node [style=filled,color=white];')
+	a('    %s' % '; '.join('"%s"' % d for d in elements))
+	a('    label = "%s";' % label)
+	a('}')
+	return '\n'.join(lines)
+
+def run_dot(basename,type):
+	dotname = basename + ".dot"
+	outname  = basename + "." + type
+
+	dotchanged = True
+
+	try:
+		olddot = open(dotname + "~").read()
+		dotchanged =  olddot != open(dotname).read()
+		dotchanged = dotchanged or not os.path.exists(outname)
+	except IOError: pass
+	if not dotchanged:
+		return
+	print "Generating: ",outname
+	cmd = 'dot -T%(type)s %(dotname)s > %(outname)s' % locals()
+	os.system(cmd)
+	copyfile(dotname,dotname + "~")
+
+def analyze(write_postscript=False):
+	root = path(ROOTDIRNAME)
+	headers = list(root.walkfiles('*.h'))
+	sources = headers + list(root.walkfiles('*.cpp'))
+	file2inc = get_file2inc(sources)
+	
+	moduleUser2provider = {}
+	dirUser2provider = {}
+	fileUser2provider = {}	
+	unknownIncludes = {}
+	dirclusters = {}
+	fill_dep_infos(file2inc, fileUser2provider, dirUser2provider, moduleUser2provider, unknownIncludes, dirclusters)
+	
+	# write module dep graph
+	out = []
+	illegalModuleDeps = []
+	for user, providers in sorted(moduleUser2provider.items()):
+		for provider in sorted(providers):
+			if user != provider:
+				out.append('    "' + user + '" -> "' + provider + '"')
+				msg = check_dep_error(ALLOWED_MODULE_DEPS, user, provider)
+				if msg:
+					illegalModuleDeps.append(msg)
+	write_dot_file('%s.dot' % MODULE_DEPS_OUT, out)
+	if write_postscript:
+		run_dot(MODULE_DEPS_OUT, "ps")
+	run_dot(MODULE_DEPS_OUT,"png")
+	
+	# write dir dep graph
+	out = []
+	for cluster, subdirs in sorted(dirclusters.items()):
+		out.append(get_cluster_str(len(out), sorted(subdirs), cluster))
+	for user, providers in sorted(dirUser2provider.items()):
+		for provider in sorted(providers):
+			if user != provider:
+				out.append('    "' + user + '" -> "' + provider + '"')
+	write_dot_file('%s.dot' % DIR_DEPS_OUT, out)
+	if write_postscript:
+		run_dot(DIR_DEPS_OUT, "ps")
+	
+	# write file dep graph
+	out = []
+	for user, providers in sorted(file2inc.items()):
+		for provider in sorted(providers):
+			if user != provider:
+				out.append('    "' + user + '" -> "' + provider + '"')
+	write_dot_file('%s.dot' % FILE_DEPS_OUT, out)
+	# os.system('dot -Tps %s.dot > %s.ps' % (MODULE_DEPS_OUT, MODULE_DEPS_OUT))
+	
+	# write raw dep info
+	#out = []
+	#for f, file2inc
+	result = '\n'.join(illegalModuleDeps)
+	if result:
+		print result
+	else:
+		print "no dependency analyzer errors found"
+	return result
+
+
+_ANALYZE_FN_ = analyze
+
+if __name__ == '__main__':
+	analyze(True)