diff -r 60053dab7e2a -r 842a773e65f2 jamesa/get_deps.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jamesa/get_deps.py Wed Nov 04 17:40:17 2009 +0000 @@ -0,0 +1,174 @@ +# Copyright (c) 2009 Symbian Foundation Ltd +# This component and the accompanying materials are made available +# under the terms of the License "Eclipse Public License v1.0" +# which accompanies this distribution, and is available +# at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Symbian Foundation Ltd - initial contribution. +# +# Contributors: +# +# Description: +# Walk all nodes in the dependency graph to create a dependency report. + +"""Walk a dependency graph to find dependencies for a particular set of +components. This script uses the output of build_graph.py to trace +dependencies. + +The directory_list refer to diretories in the source tree for which +you wish to trace dependencies. The script will find all components +in the graph file under these directories and trace dependencies from +that point. +""" + +from optparse import OptionParser +from _common import Node + +import sys +import pickle +import logging + +__author__ = 'James Aley' +__email__ = 'jamesa@symbian.org' +__version__ = '1.0' + +_LOG_FORMAT = '%(levelname)s: %(message)s' +logging.basicConfig(format=_LOG_FORMAT, level=logging.WARNING, stream=sys.stdout) + +# Internalized graph object +graph = {} + +# Report formatting +_REPORT_HEADER = """// Generated by get_deps.py +// +// Dependency information for: +// +%s + +""" + +_DEPENDENCY_FORMAT = """ +// Required components: + +%s + +""" + +_MISSING_FORMAT = """ +// The following binary objects were referenced from the build files for +// components required by your specified root components. However, there +// were no build files for these objects found in the source tree parsing, +// so dependencies for them may be missing in the above listing. + +%s + +""" + +def load_graph(path): + """Return the internalized graph dictionary object. + """ + graph_file = None + graph_loaded = {} + + try: + graph_file = open(path, 'rb') + except IOError, e: + logging.critical('Unable to open graph from file: %s: %s' % (path, repr(e))) + exit(1) + try: + graph_loaded = pickle.load(graph_file) + except Exception, e: + logging.critical('File %s does not contain a valid graph: %s' % (path, repr(e))) + return graph_loaded + +def find_roots(root_dirs): + """Return a list of root nodes from the graph for tracing, based on + the specified component directories in the root_dirs list. + """ + roots = [] + for root in root_dirs: + for node in graph.keys(): + if node.startswith(root.lower()): + if node not in roots: + roots.append(node) + return roots + +def trace(root, visited = set()): + """Return a list of components required to support root. + """ + node = graph[root] + visited |= set([node.node_path]) | set(node.dependencies) + for dep in node.dependencies: + if dep not in visited: + return trace(dep, visited) + return visited + +def unresolved(deps): + """Return a set of components with unknown dependencies from a + provided list of node names. + """ + unresolved = set() + for dep in deps: + node = graph[dep] + unresolved |= set(node.unresolved) + return unresolved + +def report(out_path, roots, dependencies, missing): + """Output the dependency information to file. + """ + # open report file + out_file = None + try: + out_file = open(out_path, 'w') + except IOError, e: + logging.critical('Unable to write report: %s' % (repr(e))) + exit(1) + + # easier to read report with these sorted + roots.sort() + dependencies.sort() + missing.sort() + + # format report + formatted_header = _REPORT_HEADER % ('\n'.join(['// %s' % (line, ) for line in roots]), ) + formatted_body = _DEPENDENCY_FORMAT % ('\n'.join(dependencies)) + formatted_missing = _MISSING_FORMAT % ('\n'.join(['// %s' % (line, ) for line in missing]), ) + + # write report + out_file.write(formatted_header) + out_file.write(formatted_body) + out_file.write(formatted_missing) + + out_file.close() + +if __name__ == '__main__': + # Options config + parser = OptionParser() + parser.set_description(__doc__) + parser.set_usage('python get_deps.py [options] directory_list') + parser.add_option('-g', '--graph', dest='graph_file', + help='File name to write the graph to.', + metavar='GRAPH_FILE', default='dependencies.graph') + parser.add_option('-o', '--output', dest='output_file', + help='File to write the dependency report to.', + metavar='OUT_FILE', default='dependencies.txt') + (options, args) = parser.parse_args() + + # Intenalize the graph file + print 'Loading graph from %s' % (options.graph_file, ) + graph = load_graph(options.graph_file) + + # Extract relevant slices and merge dependencies + roots = find_roots(args) + print 'Tracing dependencies for %d components under %s' % (len(roots), ', '.join(args)) + deps = set() + for root in roots: + deps |= trace(root) + print 'Dependency graph slice yields %d of %d components.' % (len(deps), len(graph)) + unresolved_deps = unresolved(deps) + print 'Component dependencies for %d binaries are unresolved' % (len(unresolved_deps, )) + + # Write the report to the output file + report(options.output_file, roots, list(deps), list(unresolved_deps)) + print 'Report written to: %s' % (options.output_file, )