11cb0ef41Sopenharmony_ci#!/usr/bin/env python3 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ci# Copyright (c) 2011 Google Inc. All rights reserved. 41cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 51cb0ef41Sopenharmony_ci# found in the LICENSE file. 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ci"""Using the JSON dumped by the dump-dependency-json generator, 81cb0ef41Sopenharmony_cigenerate input suitable for graphviz to render a dependency graph of 91cb0ef41Sopenharmony_citargets.""" 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ciimport collections 131cb0ef41Sopenharmony_ciimport json 141cb0ef41Sopenharmony_ciimport sys 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_cidef ParseTarget(target): 181cb0ef41Sopenharmony_ci target, _, suffix = target.partition("#") 191cb0ef41Sopenharmony_ci filename, _, target = target.partition(":") 201cb0ef41Sopenharmony_ci return filename, target, suffix 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_cidef LoadEdges(filename, targets): 241cb0ef41Sopenharmony_ci """Load the edges map from the dump file, and filter it to only 251cb0ef41Sopenharmony_ci show targets in |targets| and their depedendents.""" 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci file = open("dump.json") 281cb0ef41Sopenharmony_ci edges = json.load(file) 291cb0ef41Sopenharmony_ci file.close() 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci # Copy out only the edges we're interested in from the full edge list. 321cb0ef41Sopenharmony_ci target_edges = {} 331cb0ef41Sopenharmony_ci to_visit = targets[:] 341cb0ef41Sopenharmony_ci while to_visit: 351cb0ef41Sopenharmony_ci src = to_visit.pop() 361cb0ef41Sopenharmony_ci if src in target_edges: 371cb0ef41Sopenharmony_ci continue 381cb0ef41Sopenharmony_ci target_edges[src] = edges[src] 391cb0ef41Sopenharmony_ci to_visit.extend(edges[src]) 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_ci return target_edges 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_cidef WriteGraph(edges): 451cb0ef41Sopenharmony_ci """Print a graphviz graph to stdout. 461cb0ef41Sopenharmony_ci |edges| is a map of target to a list of other targets it depends on.""" 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci # Bucket targets by file. 491cb0ef41Sopenharmony_ci files = collections.defaultdict(list) 501cb0ef41Sopenharmony_ci for src, dst in edges.items(): 511cb0ef41Sopenharmony_ci build_file, target_name, toolset = ParseTarget(src) 521cb0ef41Sopenharmony_ci files[build_file].append(src) 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci print("digraph D {") 551cb0ef41Sopenharmony_ci print(" fontsize=8") # Used by subgraphs. 561cb0ef41Sopenharmony_ci print(" node [fontsize=8]") 571cb0ef41Sopenharmony_ci 581cb0ef41Sopenharmony_ci # Output nodes by file. We must first write out each node within 591cb0ef41Sopenharmony_ci # its file grouping before writing out any edges that may refer 601cb0ef41Sopenharmony_ci # to those nodes. 611cb0ef41Sopenharmony_ci for filename, targets in files.items(): 621cb0ef41Sopenharmony_ci if len(targets) == 1: 631cb0ef41Sopenharmony_ci # If there's only one node for this file, simplify 641cb0ef41Sopenharmony_ci # the display by making it a box without an internal node. 651cb0ef41Sopenharmony_ci target = targets[0] 661cb0ef41Sopenharmony_ci build_file, target_name, toolset = ParseTarget(target) 671cb0ef41Sopenharmony_ci print( 681cb0ef41Sopenharmony_ci f' "{target}" [shape=box, label="{filename}\\n{target_name}"]' 691cb0ef41Sopenharmony_ci ) 701cb0ef41Sopenharmony_ci else: 711cb0ef41Sopenharmony_ci # Group multiple nodes together in a subgraph. 721cb0ef41Sopenharmony_ci print(' subgraph "cluster_%s" {' % filename) 731cb0ef41Sopenharmony_ci print(' label = "%s"' % filename) 741cb0ef41Sopenharmony_ci for target in targets: 751cb0ef41Sopenharmony_ci build_file, target_name, toolset = ParseTarget(target) 761cb0ef41Sopenharmony_ci print(f' "{target}" [label="{target_name}"]') 771cb0ef41Sopenharmony_ci print(" }") 781cb0ef41Sopenharmony_ci 791cb0ef41Sopenharmony_ci # Now that we've placed all the nodes within subgraphs, output all 801cb0ef41Sopenharmony_ci # the edges between nodes. 811cb0ef41Sopenharmony_ci for src, dsts in edges.items(): 821cb0ef41Sopenharmony_ci for dst in dsts: 831cb0ef41Sopenharmony_ci print(f' "{src}" -> "{dst}"') 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci print("}") 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci 881cb0ef41Sopenharmony_cidef main(): 891cb0ef41Sopenharmony_ci if len(sys.argv) < 2: 901cb0ef41Sopenharmony_ci print(__doc__, file=sys.stderr) 911cb0ef41Sopenharmony_ci print(file=sys.stderr) 921cb0ef41Sopenharmony_ci print("usage: %s target1 target2..." % (sys.argv[0]), file=sys.stderr) 931cb0ef41Sopenharmony_ci return 1 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_ci edges = LoadEdges("dump.json", sys.argv[1:]) 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci WriteGraph(edges) 981cb0ef41Sopenharmony_ci return 0 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_ciif __name__ == "__main__": 1021cb0ef41Sopenharmony_ci sys.exit(main()) 103