OLD | NEW |
1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 from __future__ import print_function | 5 from __future__ import print_function |
6 | 6 |
7 import sys | 7 import sys |
8 import os | 8 import os |
| 9 import collections |
9 | 10 |
10 from . import loader | 11 from . import loader |
11 | 12 |
12 | 13 |
13 _GRAPH_HEADER = """strict digraph { | 14 _GRAPH_HEADER = """strict digraph { |
14 concentrate = true; | 15 concentrate = true; |
15 ranksep = 2; | 16 ranksep = 2; |
16 nodesep = 0.25; | 17 nodesep = 0.25; |
17 """ | 18 """ |
18 | 19 |
19 _GRAPH_FOOTER = """} | 20 _GRAPH_FOOTER = """} |
20 """ | 21 """ |
21 | 22 |
22 | 23 |
23 def main(universe, ignore_packages, stdout): | 24 def main(universe, own_package, ignore_packages, stdout, recipe_filter): |
24 packages = {} | |
25 module_to_package = {} | 25 module_to_package = {} |
26 edges = [] | 26 |
| 27 # All deps maps a tuple of (is_recipe, id) to deps (list of ids). is_recipe is |
| 28 # a boolean, all ids are strings. |
| 29 all_deps = {} |
27 for package, module_name in universe.loop_over_recipe_modules(): | 30 for package, module_name in universe.loop_over_recipe_modules(): |
| 31 if package in ignore_packages: |
| 32 continue |
28 mod = universe.load(package, module_name) | 33 mod = universe.load(package, module_name) |
29 | 34 |
30 for dep in mod.LOADED_DEPS: | 35 all_deps[(False, mod.NAME)] = mod.LOADED_DEPS |
31 edges.append((mod.NAME, dep)) | |
32 packages.setdefault(package.name, []).append(mod.NAME) | |
33 module_to_package[mod.NAME] = package.name | 36 module_to_package[mod.NAME] = package.name |
34 | 37 |
| 38 if recipe_filter: |
| 39 recipe_to_package = {} |
| 40 universe_view = loader.UniverseView(universe, own_package) |
| 41 for _, recipe_name in universe_view.loop_over_recipes(): |
| 42 if recipe_filter not in recipe_name: |
| 43 continue |
| 44 |
| 45 recipe = universe_view.load_recipe(recipe_name) |
| 46 |
| 47 all_deps[(True, recipe_name)] = recipe.LOADED_DEPS |
| 48 recipe_to_package[recipe_name] = own_package |
| 49 |
| 50 # If we actually found any recipes |
| 51 if recipe_to_package: |
| 52 # Prune anything our recipe doesn't see via BFS. |
| 53 queue = [ |
| 54 (True, name) for (is_recipe, name), _ in all_deps.items() |
| 55 if is_recipe] |
| 56 |
| 57 to_keep = set() |
| 58 while queue: |
| 59 itm = queue.pop() |
| 60 to_keep.add(itm) |
| 61 for dep in all_deps[itm]: |
| 62 queue.append((False, dep)) |
| 63 |
| 64 all_deps = {key: deps for key, deps in all_deps.items() if key in to_keep} |
| 65 |
| 66 mod_names = [ |
| 67 name for (is_recipe, name), _ in all_deps.items() if not is_recipe] |
| 68 module_to_package = { |
| 69 m_name: p_name for m_name, p_name in module_to_package.items() |
| 70 if m_name in mod_names} |
| 71 |
| 72 recipe_names = [ |
| 73 name for (is_recipe, name), _ in all_deps.items() if is_recipe] |
| 74 recipe_to_package = { |
| 75 r_name: p_name for r_name, p_name in recipe_to_package.items() |
| 76 if r_name in recipe_names} |
| 77 |
| 78 |
35 print(_GRAPH_HEADER, file=stdout) | 79 print(_GRAPH_HEADER, file=stdout) |
| 80 edges = [] |
| 81 for (is_recipe, name), deps in all_deps.items(): |
| 82 for dep in deps: |
| 83 edges.append(((is_recipe, name), dep)) |
| 84 |
36 for edge in edges: | 85 for edge in edges: |
37 if (module_to_package[edge[0]] in ignore_packages or | 86 (is_recipe, first_name), second_name = edge |
38 module_to_package[edge[1]] in ignore_packages): | 87 |
| 88 if not is_recipe: |
| 89 if module_to_package[first_name] in ignore_packages: |
| 90 continue |
| 91 else: |
| 92 if recipe_to_package[first_name] in ignore_packages: |
| 93 continue |
| 94 first_name = 'recipe ' + first_name |
| 95 |
| 96 if module_to_package[second_name] in ignore_packages: |
39 continue | 97 continue |
40 print(' %s -> %s' % (edge[0], edge[1]), file=stdout) | 98 |
| 99 print(' "%s" -> "%s"' % (first_name, second_name), file=stdout) |
| 100 |
| 101 packages = {} |
| 102 for module, package in module_to_package.iteritems(): |
| 103 packages.setdefault(package, []).append(module) |
41 for package, modules in packages.iteritems(): | 104 for package, modules in packages.iteritems(): |
42 if package in ignore_packages: | 105 if package in ignore_packages: |
43 continue | 106 continue |
44 # The "cluster_" prefix has magic meaning for graphviz and makes it | 107 # The "cluster_" prefix has magic meaning for graphviz and makes it |
45 # draw a box around the subgraph. | 108 # draw a box around the subgraph. |
46 print(' subgraph "cluster_%s" { label="%s"; %s; }' % ( | 109 print(' subgraph "cluster_%s" { label="%s"; %s; }' % ( |
47 package, package, '; '.join(modules)), file=stdout) | 110 package, package, '; '.join(modules)), file=stdout) |
| 111 |
| 112 if recipe_filter and recipe_to_package: |
| 113 recipe_names = [ |
| 114 '"recipe %s"' % name for name in recipe_to_package.keys()] |
| 115 print(' subgraph "cluster_recipes" { label="recipes"; %s; }' % ( |
| 116 '; '.join(recipe_names)), file=stdout) |
| 117 |
48 print(_GRAPH_FOOTER, file=stdout) | 118 print(_GRAPH_FOOTER, file=stdout) |
OLD | NEW |