1#!/usr/bin/env python3 2 3# Copyright (c) 2011 Google Inc. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Using the JSON dumped by the dump-dependency-json generator, 8generate input suitable for graphviz to render a dependency graph of 9targets.""" 10 11 12import collections 13import json 14import sys 15 16 17def ParseTarget(target): 18 target, _, suffix = target.partition("#") 19 filename, _, target = target.partition(":") 20 return filename, target, suffix 21 22 23def LoadEdges(filename, targets): 24 """Load the edges map from the dump file, and filter it to only 25 show targets in |targets| and their depedendents.""" 26 27 file = open("dump.json") 28 edges = json.load(file) 29 file.close() 30 31 # Copy out only the edges we're interested in from the full edge list. 32 target_edges = {} 33 to_visit = targets[:] 34 while to_visit: 35 src = to_visit.pop() 36 if src in target_edges: 37 continue 38 target_edges[src] = edges[src] 39 to_visit.extend(edges[src]) 40 41 return target_edges 42 43 44def WriteGraph(edges): 45 """Print a graphviz graph to stdout. 46 |edges| is a map of target to a list of other targets it depends on.""" 47 48 # Bucket targets by file. 49 files = collections.defaultdict(list) 50 for src, dst in edges.items(): 51 build_file, target_name, toolset = ParseTarget(src) 52 files[build_file].append(src) 53 54 print("digraph D {") 55 print(" fontsize=8") # Used by subgraphs. 56 print(" node [fontsize=8]") 57 58 # Output nodes by file. We must first write out each node within 59 # its file grouping before writing out any edges that may refer 60 # to those nodes. 61 for filename, targets in files.items(): 62 if len(targets) == 1: 63 # If there's only one node for this file, simplify 64 # the display by making it a box without an internal node. 65 target = targets[0] 66 build_file, target_name, toolset = ParseTarget(target) 67 print( 68 f' "{target}" [shape=box, label="{filename}\\n{target_name}"]' 69 ) 70 else: 71 # Group multiple nodes together in a subgraph. 72 print(' subgraph "cluster_%s" {' % filename) 73 print(' label = "%s"' % filename) 74 for target in targets: 75 build_file, target_name, toolset = ParseTarget(target) 76 print(f' "{target}" [label="{target_name}"]') 77 print(" }") 78 79 # Now that we've placed all the nodes within subgraphs, output all 80 # the edges between nodes. 81 for src, dsts in edges.items(): 82 for dst in dsts: 83 print(f' "{src}" -> "{dst}"') 84 85 print("}") 86 87 88def main(): 89 if len(sys.argv) < 2: 90 print(__doc__, file=sys.stderr) 91 print(file=sys.stderr) 92 print("usage: %s target1 target2..." % (sys.argv[0]), file=sys.stderr) 93 return 1 94 95 edges = LoadEdges("dump.json", sys.argv[1:]) 96 97 WriteGraph(edges) 98 return 0 99 100 101if __name__ == "__main__": 102 sys.exit(main()) 103