#!/usr/bin/env python2 # -*- coding: utf-8 -*- # This program is free software. It comes without any warranty, to # the extent permitted by applicable law. You can redistribute it # and/or modify it under the terms of the Do What The Fuck You Want # To Public License, Version 2, as published by Sam Hocevar. See # COPYING for more details. # 2013-08-11: made by kahrl import os import sys import re import getopt scriptpath = os.path.abspath(__file__) basepath = os.path.realpath(os.path.join(os.path.dirname(scriptpath), '../src')) os.chdir(basepath) class DirectedGraph(object): def __init__(self, n): self.adj = [set() for i in range(0,n)] def vertexcount(self): return len(self.adj) def edgecount_from(self, i): return len(self.adj[i]) def edges_from(self, i): return sorted(self.adj[i]) def add(self, i, j): self.adj[i].add(j) def square(self): n = self.vertexcount() newadj = [set(self.adj[i]) for i in range(0,n)] changed = False for i in range(0,n): for j in range(0,n): if j not in newadj[i]: for k in self.adj[i]: if j in self.adj[k]: newadj[i].add(j) changed = True self.adj = newadj return changed def power(self): while self.square(): pass def is_cpp(name): return name[-4:] == '.cpp' def is_h(name): return name[-2:] == '.h' def get_codefiles_set(): subpaths = ['', 'script/common', 'script/cpp_api', 'script/lua_api', 'util'] codefiles_set = set() for subpath in subpaths: for filename in os.listdir(os.path.join('.', subpath)): filepath = os.path.join(subpath, filename) if os.path.isfile(filepath): if is_cpp(filepath) or is_h(filepath): codefiles_set.add(filepath) return codefiles_set def get_included_file(includetext, curpath, codefiles_set): includepaths = [curpath, '', 'script'] for includepath in includepaths: filepath = os.path.normpath(os.path.join(includepath, includetext)) if filepath in codefiles_set: return filepath return None def get_includes(filepath, codefiles_set): includes_set = set() curpath = os.path.dirname(filepath) with open(filepath) as f: for line in f: match = re.match(r'^\s*#\s*include\s+"([^"]+)"\s*$', line) if match: included_file = get_included_file( match.group(1), curpath, codefiles_set) if included_file is not None: includes_set.add(included_file) else: #print(repr(line)) pass return sorted(includes_set) def make_depend_graph(codefiles, codefiles_set): n = len(codefiles) graph = DirectedGraph(n) codefiles_index = dict() for i in range(0,n): codefiles_index[codefiles[i]] = i for i in range(0,n): filepath = codefiles[i] for include in get_includes(filepath, codefiles_set): j = codefiles_index[include] graph.add(i, j) graph.add(i, i) return graph def print_depends(codefiles, graph, only_sources): n = len(codefiles) for i in range(0,n): if not only_sources or is_cpp(codefiles[i]): for j in graph.edges_from(i): if i != j: print(codefiles[i] + '\t' + codefiles[j]) def print_depend_stats(codefiles, graph, only_sources): n = len(codefiles) for i in range(0,n): if not only_sources or is_cpp(codefiles[i]): # minus one because each vertex is connected to itself, # but we don't want to count that edge print(codefiles[i] + '\t' + str(graph.edgecount_from(i) - 1)) codefiles_set = get_codefiles_set() codefiles = sorted(codefiles_set) graph = make_depend_graph(codefiles, codefiles_set) graph.power() print_depend_stats(codefiles, graph, True)