1cb93a386Sopenharmony_ci#!/usr/bin/env python
2cb93a386Sopenharmony_ci# Copyright 2013 The Chromium Authors. All rights reserved.
3cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
4cb93a386Sopenharmony_ci# found in the LICENSE file.
5cb93a386Sopenharmony_ci
6cb93a386Sopenharmony_ci"""Dumps a graph of allowed and disallowed inter-module dependencies described
7cb93a386Sopenharmony_ciby the DEPS files in the source tree. Supports DOT and PNG as the output format.
8cb93a386Sopenharmony_ci
9cb93a386Sopenharmony_ciEnables filtering and differential highlighting of parts of the graph based on
10cb93a386Sopenharmony_cithe specified criteria. This allows for a much easier visual analysis of the
11cb93a386Sopenharmony_cidependencies, including answering questions such as "if a new source must
12cb93a386Sopenharmony_cidepend on modules A, B, and C, what valid options among the existing modules
13cb93a386Sopenharmony_ciare there to put it in."
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ciSee README.md for a detailed description of the DEPS format.
16cb93a386Sopenharmony_ci"""
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_ciimport os
19cb93a386Sopenharmony_ciimport optparse
20cb93a386Sopenharmony_ciimport pipes
21cb93a386Sopenharmony_ciimport re
22cb93a386Sopenharmony_ciimport sys
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_cifrom builddeps import DepsBuilder
25cb93a386Sopenharmony_cifrom rules import Rule
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ciclass DepsGrapher(DepsBuilder):
29cb93a386Sopenharmony_ci  """Parses include_rules from DEPS files and outputs a DOT graph of the
30cb93a386Sopenharmony_ci  allowed and disallowed dependencies between directories and specific file
31cb93a386Sopenharmony_ci  regexps. Can generate only a subgraph of the whole dependency graph
32cb93a386Sopenharmony_ci  corresponding to the provided inclusion and exclusion regexp filters.
33cb93a386Sopenharmony_ci  Also can highlight fanins and/or fanouts of certain nodes matching the
34cb93a386Sopenharmony_ci  provided regexp patterns.
35cb93a386Sopenharmony_ci  """
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_ci  def __init__(self,
38cb93a386Sopenharmony_ci               base_directory,
39cb93a386Sopenharmony_ci               extra_repos,
40cb93a386Sopenharmony_ci               verbose,
41cb93a386Sopenharmony_ci               being_tested,
42cb93a386Sopenharmony_ci               ignore_temp_rules,
43cb93a386Sopenharmony_ci               ignore_specific_rules,
44cb93a386Sopenharmony_ci               hide_disallowed_deps,
45cb93a386Sopenharmony_ci               out_file,
46cb93a386Sopenharmony_ci               out_format,
47cb93a386Sopenharmony_ci               layout_engine,
48cb93a386Sopenharmony_ci               unflatten_graph,
49cb93a386Sopenharmony_ci               incl,
50cb93a386Sopenharmony_ci               excl,
51cb93a386Sopenharmony_ci               hilite_fanins,
52cb93a386Sopenharmony_ci               hilite_fanouts):
53cb93a386Sopenharmony_ci    """Creates a new DepsGrapher.
54cb93a386Sopenharmony_ci
55cb93a386Sopenharmony_ci    Args:
56cb93a386Sopenharmony_ci      base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src.
57cb93a386Sopenharmony_ci      verbose: Set to true for debug output.
58cb93a386Sopenharmony_ci      being_tested: Set to true to ignore the DEPS file at tools/graphdeps/DEPS.
59cb93a386Sopenharmony_ci      ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!").
60cb93a386Sopenharmony_ci      ignore_specific_rules: Ignore rules from specific_include_rules sections.
61cb93a386Sopenharmony_ci      hide_disallowed_deps: Hide disallowed dependencies from the output graph.
62cb93a386Sopenharmony_ci      out_file: Output file name.
63cb93a386Sopenharmony_ci      out_format: Output format (anything GraphViz dot's -T option supports).
64cb93a386Sopenharmony_ci      layout_engine: Layout engine for formats other than 'dot'
65cb93a386Sopenharmony_ci                     (anything that GraphViz dot's -K option supports).
66cb93a386Sopenharmony_ci      unflatten_graph: Try to reformat the output graph so it is narrower and
67cb93a386Sopenharmony_ci                       taller. Helps fight overly flat and wide graphs, but
68cb93a386Sopenharmony_ci                       sometimes produces a worse result.
69cb93a386Sopenharmony_ci      incl: Include only nodes matching this regexp; such nodes' fanin/fanout
70cb93a386Sopenharmony_ci            is also included.
71cb93a386Sopenharmony_ci      excl: Exclude nodes matching this regexp; such nodes' fanin/fanout is
72cb93a386Sopenharmony_ci            processed independently.
73cb93a386Sopenharmony_ci      hilite_fanins: Highlight fanins of nodes matching this regexp with a
74cb93a386Sopenharmony_ci                     different edge and node color.
75cb93a386Sopenharmony_ci      hilite_fanouts: Highlight fanouts of nodes matching this regexp with a
76cb93a386Sopenharmony_ci                      different edge and node color.
77cb93a386Sopenharmony_ci    """
78cb93a386Sopenharmony_ci    DepsBuilder.__init__(
79cb93a386Sopenharmony_ci        self,
80cb93a386Sopenharmony_ci        base_directory,
81cb93a386Sopenharmony_ci        extra_repos,
82cb93a386Sopenharmony_ci        verbose,
83cb93a386Sopenharmony_ci        being_tested,
84cb93a386Sopenharmony_ci        ignore_temp_rules,
85cb93a386Sopenharmony_ci        ignore_specific_rules)
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci    self.ignore_temp_rules = ignore_temp_rules
88cb93a386Sopenharmony_ci    self.ignore_specific_rules = ignore_specific_rules
89cb93a386Sopenharmony_ci    self.hide_disallowed_deps = hide_disallowed_deps
90cb93a386Sopenharmony_ci    self.out_file = out_file
91cb93a386Sopenharmony_ci    self.out_format = out_format
92cb93a386Sopenharmony_ci    self.layout_engine = layout_engine
93cb93a386Sopenharmony_ci    self.unflatten_graph = unflatten_graph
94cb93a386Sopenharmony_ci    self.incl = incl
95cb93a386Sopenharmony_ci    self.excl = excl
96cb93a386Sopenharmony_ci    self.hilite_fanins = hilite_fanins
97cb93a386Sopenharmony_ci    self.hilite_fanouts = hilite_fanouts
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    self.deps = set()
100cb93a386Sopenharmony_ci
101cb93a386Sopenharmony_ci  def DumpDependencies(self):
102cb93a386Sopenharmony_ci    """ Builds a dependency rule table and dumps the corresponding dependency
103cb93a386Sopenharmony_ci    graph to all requested formats."""
104cb93a386Sopenharmony_ci    self._BuildDepsGraph()
105cb93a386Sopenharmony_ci    self._DumpDependencies()
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci  def _BuildDepsGraph(self):
108cb93a386Sopenharmony_ci    """Recursively traverses the source tree starting at the specified directory
109cb93a386Sopenharmony_ci    and builds a dependency graph representation in self.deps."""
110cb93a386Sopenharmony_ci    for (rules, _) in self.GetAllRulesAndFiles():
111cb93a386Sopenharmony_ci      deps = rules.AsDependencyTuples(
112cb93a386Sopenharmony_ci          include_general_rules=True,
113cb93a386Sopenharmony_ci          include_specific_rules=not self.ignore_specific_rules)
114cb93a386Sopenharmony_ci      self.deps.update(deps)
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci  def _DumpDependencies(self):
117cb93a386Sopenharmony_ci    """Dumps the built dependency graph to the specified file with specified
118cb93a386Sopenharmony_ci    format."""
119cb93a386Sopenharmony_ci    if self.out_format == 'dot' and not self.layout_engine:
120cb93a386Sopenharmony_ci      if self.unflatten_graph:
121cb93a386Sopenharmony_ci        pipe = pipes.Template()
122cb93a386Sopenharmony_ci        pipe.append('unflatten -l 2 -c 3', '--')
123cb93a386Sopenharmony_ci        out = pipe.open(self.out_file, 'w')
124cb93a386Sopenharmony_ci      else:
125cb93a386Sopenharmony_ci        out = open(self.out_file, 'w')
126cb93a386Sopenharmony_ci    else:
127cb93a386Sopenharmony_ci      pipe = pipes.Template()
128cb93a386Sopenharmony_ci      if self.unflatten_graph:
129cb93a386Sopenharmony_ci        pipe.append('unflatten -l 2 -c 3', '--')
130cb93a386Sopenharmony_ci      dot_cmd = 'dot -T' + self.out_format
131cb93a386Sopenharmony_ci      if self.layout_engine:
132cb93a386Sopenharmony_ci        dot_cmd += ' -K' + self.layout_engine
133cb93a386Sopenharmony_ci      pipe.append(dot_cmd, '--')
134cb93a386Sopenharmony_ci      out = pipe.open(self.out_file, 'w')
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_ci    self._DumpDependenciesImpl(self.deps, out)
137cb93a386Sopenharmony_ci    out.close()
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci  def _DumpDependenciesImpl(self, deps, out):
140cb93a386Sopenharmony_ci    """Computes nodes' and edges' properties for the dependency graph |deps| and
141cb93a386Sopenharmony_ci    carries out the actual dumping to a file/pipe |out|."""
142cb93a386Sopenharmony_ci    deps_graph = dict()
143cb93a386Sopenharmony_ci    deps_srcs = set()
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_ci    # Pre-initialize the graph with src->(dst, allow) pairs.
146cb93a386Sopenharmony_ci    for (allow, src, dst) in deps:
147cb93a386Sopenharmony_ci      if allow == Rule.TEMP_ALLOW and self.ignore_temp_rules:
148cb93a386Sopenharmony_ci        continue
149cb93a386Sopenharmony_ci
150cb93a386Sopenharmony_ci      deps_srcs.add(src)
151cb93a386Sopenharmony_ci      if src not in deps_graph:
152cb93a386Sopenharmony_ci        deps_graph[src] = []
153cb93a386Sopenharmony_ci      deps_graph[src].append((dst, allow))
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci      # Add all hierarchical parents too, in case some of them don't have their
156cb93a386Sopenharmony_ci      # own DEPS, and therefore are missing from the list of rules. Those will
157cb93a386Sopenharmony_ci      # be recursively populated with their parents' rules in the next block.
158cb93a386Sopenharmony_ci      parent_src = os.path.dirname(src)
159cb93a386Sopenharmony_ci      while parent_src:
160cb93a386Sopenharmony_ci        if parent_src not in deps_graph:
161cb93a386Sopenharmony_ci          deps_graph[parent_src] = []
162cb93a386Sopenharmony_ci        parent_src = os.path.dirname(parent_src)
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci    # For every node, propagate its rules down to all its children.
165cb93a386Sopenharmony_ci    deps_srcs = list(deps_srcs)
166cb93a386Sopenharmony_ci    deps_srcs.sort()
167cb93a386Sopenharmony_ci    for src in deps_srcs:
168cb93a386Sopenharmony_ci      parent_src = os.path.dirname(src)
169cb93a386Sopenharmony_ci      if parent_src:
170cb93a386Sopenharmony_ci        # We presort the list, so parents are guaranteed to precede children.
171cb93a386Sopenharmony_ci        assert parent_src in deps_graph,\
172cb93a386Sopenharmony_ci               "src: %s, parent_src: %s" % (src, parent_src)
173cb93a386Sopenharmony_ci        for (dst, allow) in deps_graph[parent_src]:
174cb93a386Sopenharmony_ci          # Check that this node does not explicitly override a rule from the
175cb93a386Sopenharmony_ci          # parent that we're about to add.
176cb93a386Sopenharmony_ci          if ((dst, Rule.ALLOW) not in deps_graph[src]) and \
177cb93a386Sopenharmony_ci             ((dst, Rule.TEMP_ALLOW) not in deps_graph[src]) and \
178cb93a386Sopenharmony_ci             ((dst, Rule.DISALLOW) not in deps_graph[src]):
179cb93a386Sopenharmony_ci            deps_graph[src].append((dst, allow))
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci    node_props = {}
182cb93a386Sopenharmony_ci    edges = []
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci    # 1) Populate a list of edge specifications in DOT format;
185cb93a386Sopenharmony_ci    # 2) Populate a list of computed raw node attributes to be output as node
186cb93a386Sopenharmony_ci    #    specifications in DOT format later on.
187cb93a386Sopenharmony_ci    # Edges and nodes are emphasized with color and line/border weight depending
188cb93a386Sopenharmony_ci    # on how many of incl/excl/hilite_fanins/hilite_fanouts filters they hit,
189cb93a386Sopenharmony_ci    # and in what way.
190cb93a386Sopenharmony_ci    for src in deps_graph.keys():
191cb93a386Sopenharmony_ci      for (dst, allow) in deps_graph[src]:
192cb93a386Sopenharmony_ci        if allow == Rule.DISALLOW and self.hide_disallowed_deps:
193cb93a386Sopenharmony_ci          continue
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_ci        if allow == Rule.ALLOW and src == dst:
196cb93a386Sopenharmony_ci          continue
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ci        edge_spec = "%s->%s" % (src, dst)
199cb93a386Sopenharmony_ci        if not re.search(self.incl, edge_spec) or \
200cb93a386Sopenharmony_ci               re.search(self.excl, edge_spec):
201cb93a386Sopenharmony_ci          continue
202cb93a386Sopenharmony_ci
203cb93a386Sopenharmony_ci        if src not in node_props:
204cb93a386Sopenharmony_ci          node_props[src] = {'hilite': None, 'degree': 0}
205cb93a386Sopenharmony_ci        if dst not in node_props:
206cb93a386Sopenharmony_ci          node_props[dst] = {'hilite': None, 'degree': 0}
207cb93a386Sopenharmony_ci
208cb93a386Sopenharmony_ci        edge_weight = 1
209cb93a386Sopenharmony_ci
210cb93a386Sopenharmony_ci        if self.hilite_fanouts and re.search(self.hilite_fanouts, src):
211cb93a386Sopenharmony_ci          node_props[src]['hilite'] = 'lightgreen'
212cb93a386Sopenharmony_ci          node_props[dst]['hilite'] = 'lightblue'
213cb93a386Sopenharmony_ci          node_props[dst]['degree'] += 1
214cb93a386Sopenharmony_ci          edge_weight += 1
215cb93a386Sopenharmony_ci
216cb93a386Sopenharmony_ci        if self.hilite_fanins and re.search(self.hilite_fanins, dst):
217cb93a386Sopenharmony_ci          node_props[src]['hilite'] = 'lightblue'
218cb93a386Sopenharmony_ci          node_props[dst]['hilite'] = 'lightgreen'
219cb93a386Sopenharmony_ci          node_props[src]['degree'] += 1
220cb93a386Sopenharmony_ci          edge_weight += 1
221cb93a386Sopenharmony_ci
222cb93a386Sopenharmony_ci        if allow == Rule.ALLOW:
223cb93a386Sopenharmony_ci          edge_color = (edge_weight > 1) and 'blue' or 'green'
224cb93a386Sopenharmony_ci          edge_style = 'solid'
225cb93a386Sopenharmony_ci        elif allow == Rule.TEMP_ALLOW:
226cb93a386Sopenharmony_ci          edge_color = (edge_weight > 1) and 'blue' or 'green'
227cb93a386Sopenharmony_ci          edge_style = 'dashed'
228cb93a386Sopenharmony_ci        else:
229cb93a386Sopenharmony_ci          edge_color = 'red'
230cb93a386Sopenharmony_ci          edge_style = 'dashed'
231cb93a386Sopenharmony_ci        edges.append('    "%s" -> "%s" [style=%s,color=%s,penwidth=%d];' % \
232cb93a386Sopenharmony_ci            (src, dst, edge_style, edge_color, edge_weight))
233cb93a386Sopenharmony_ci
234cb93a386Sopenharmony_ci    # Reformat the computed raw node attributes into a final DOT representation.
235cb93a386Sopenharmony_ci    nodes = []
236cb93a386Sopenharmony_ci    for (node, attrs) in node_props.items():
237cb93a386Sopenharmony_ci      attr_strs = []
238cb93a386Sopenharmony_ci      if attrs['hilite']:
239cb93a386Sopenharmony_ci        attr_strs.append('style=filled,fillcolor=%s' % attrs['hilite'])
240cb93a386Sopenharmony_ci      attr_strs.append('penwidth=%d' % (attrs['degree'] or 1))
241cb93a386Sopenharmony_ci      nodes.append('    "%s" [%s];' % (node, ','.join(attr_strs)))
242cb93a386Sopenharmony_ci
243cb93a386Sopenharmony_ci    # Output nodes and edges to |out| (can be a file or a pipe).
244cb93a386Sopenharmony_ci    edges.sort()
245cb93a386Sopenharmony_ci    nodes.sort()
246cb93a386Sopenharmony_ci    out.write('digraph DEPS {\n'
247cb93a386Sopenharmony_ci              '    fontsize=8;\n')
248cb93a386Sopenharmony_ci    out.write('\n'.join(nodes))
249cb93a386Sopenharmony_ci    out.write('\n\n')
250cb93a386Sopenharmony_ci    out.write('\n'.join(edges))
251cb93a386Sopenharmony_ci    out.write('\n}\n')
252cb93a386Sopenharmony_ci    out.close()
253cb93a386Sopenharmony_ci
254cb93a386Sopenharmony_ci
255cb93a386Sopenharmony_cidef PrintUsage():
256cb93a386Sopenharmony_ci  print("""Usage: python graphdeps.py [--root <root>]
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ci  --root ROOT Specifies the repository root. This defaults to "../../.."
259cb93a386Sopenharmony_ci              relative to the script file. This will be correct given the
260cb93a386Sopenharmony_ci              normal location of the script in "<root>/tools/graphdeps".
261cb93a386Sopenharmony_ci
262cb93a386Sopenharmony_ci  --(others)  There are a few lesser-used options; run with --help to show them.
263cb93a386Sopenharmony_ci
264cb93a386Sopenharmony_ciExamples:
265cb93a386Sopenharmony_ci  Dump the whole dependency graph:
266cb93a386Sopenharmony_ci    graphdeps.py
267cb93a386Sopenharmony_ci  Find a suitable place for a new source that must depend on /apps and
268cb93a386Sopenharmony_ci  /content/browser/renderer_host. Limit potential candidates to /apps,
269cb93a386Sopenharmony_ci  /chrome/browser and content/browser, and descendants of those three.
270cb93a386Sopenharmony_ci  Generate both DOT and PNG output. The output will highlight the fanins
271cb93a386Sopenharmony_ci  of /apps and /content/browser/renderer_host. Overlapping nodes in both fanins
272cb93a386Sopenharmony_ci  will be emphasized by a thicker border. Those nodes are the ones that are
273cb93a386Sopenharmony_ci  allowed to depend on both targets, therefore they are all legal candidates
274cb93a386Sopenharmony_ci  to place the new source in:
275cb93a386Sopenharmony_ci    graphdeps.py \
276cb93a386Sopenharmony_ci      --root=./src \
277cb93a386Sopenharmony_ci      --out=./DEPS.svg \
278cb93a386Sopenharmony_ci      --format=svg \
279cb93a386Sopenharmony_ci      --incl='^(apps|chrome/browser|content/browser)->.*' \
280cb93a386Sopenharmony_ci      --excl='.*->third_party' \
281cb93a386Sopenharmony_ci      --fanin='^(apps|content/browser/renderer_host)$' \
282cb93a386Sopenharmony_ci      --ignore-specific-rules \
283cb93a386Sopenharmony_ci      --ignore-temp-rules""")
284cb93a386Sopenharmony_ci
285cb93a386Sopenharmony_ci
286cb93a386Sopenharmony_cidef main():
287cb93a386Sopenharmony_ci  option_parser = optparse.OptionParser()
288cb93a386Sopenharmony_ci  option_parser.add_option(
289cb93a386Sopenharmony_ci      "", "--root",
290cb93a386Sopenharmony_ci      default="", dest="base_directory",
291cb93a386Sopenharmony_ci      help="Specifies the repository root. This defaults "
292cb93a386Sopenharmony_ci           "to '../../..' relative to the script file, which "
293cb93a386Sopenharmony_ci           "will normally be the repository root.")
294cb93a386Sopenharmony_ci  option_parser.add_option(
295cb93a386Sopenharmony_ci      '', '--extra-repos',
296cb93a386Sopenharmony_ci      action='append', dest='extra_repos', default=[],
297cb93a386Sopenharmony_ci      help='Specifies extra repositories relative to root repository.')
298cb93a386Sopenharmony_ci  option_parser.add_option(
299cb93a386Sopenharmony_ci      "-f", "--format",
300cb93a386Sopenharmony_ci      dest="out_format", default="dot",
301cb93a386Sopenharmony_ci      help="Output file format. "
302cb93a386Sopenharmony_ci           "Can be anything that GraphViz dot's -T option supports. "
303cb93a386Sopenharmony_ci           "The most useful ones are: dot (text), svg (image), pdf (image)."
304cb93a386Sopenharmony_ci           "NOTES: dotty has a known problem with fonts when displaying DOT "
305cb93a386Sopenharmony_ci           "files on Ubuntu - if labels are unreadable, try other formats.")
306cb93a386Sopenharmony_ci  option_parser.add_option(
307cb93a386Sopenharmony_ci      "-o", "--out",
308cb93a386Sopenharmony_ci      dest="out_file", default="DEPS",
309cb93a386Sopenharmony_ci      help="Output file name. If the name does not end in an extension "
310cb93a386Sopenharmony_ci           "matching the output format, that extension is automatically "
311cb93a386Sopenharmony_ci           "appended.")
312cb93a386Sopenharmony_ci  option_parser.add_option(
313cb93a386Sopenharmony_ci      "-l", "--layout-engine",
314cb93a386Sopenharmony_ci      dest="layout_engine", default="",
315cb93a386Sopenharmony_ci      help="Layout rendering engine. "
316cb93a386Sopenharmony_ci           "Can be anything that GraphViz dot's -K option supports. "
317cb93a386Sopenharmony_ci           "The most useful are in decreasing order: dot, fdp, circo, osage. "
318cb93a386Sopenharmony_ci           "NOTE: '-f dot' and '-f dot -l dot' are different: the former "
319cb93a386Sopenharmony_ci           "will dump a raw DOT graph and stop; the latter will further "
320cb93a386Sopenharmony_ci           "filter it through 'dot -Tdot -Kdot' layout engine.")
321cb93a386Sopenharmony_ci  option_parser.add_option(
322cb93a386Sopenharmony_ci      "-i", "--incl",
323cb93a386Sopenharmony_ci      default="^.*$", dest="incl",
324cb93a386Sopenharmony_ci      help="Include only edges of the graph that match the specified regexp. "
325cb93a386Sopenharmony_ci           "The regexp is applied to edges of the graph formatted as "
326cb93a386Sopenharmony_ci           "'source_node->target_node', where the '->' part is vebatim. "
327cb93a386Sopenharmony_ci           "Therefore, a reliable regexp should look like "
328cb93a386Sopenharmony_ci           "'^(chrome|chrome/browser|chrome/common)->content/public/browser$' "
329cb93a386Sopenharmony_ci           "or similar, with both source and target node regexps present, "
330cb93a386Sopenharmony_ci           "explicit ^ and $, and otherwise being as specific as possible.")
331cb93a386Sopenharmony_ci  option_parser.add_option(
332cb93a386Sopenharmony_ci      "-e", "--excl",
333cb93a386Sopenharmony_ci      default="^$", dest="excl",
334cb93a386Sopenharmony_ci      help="Exclude dependent nodes that match the specified regexp. "
335cb93a386Sopenharmony_ci           "See --incl for details on the format.")
336cb93a386Sopenharmony_ci  option_parser.add_option(
337cb93a386Sopenharmony_ci      "", "--fanin",
338cb93a386Sopenharmony_ci      default="", dest="hilite_fanins",
339cb93a386Sopenharmony_ci      help="Highlight fanins of nodes matching the specified regexp.")
340cb93a386Sopenharmony_ci  option_parser.add_option(
341cb93a386Sopenharmony_ci      "", "--fanout",
342cb93a386Sopenharmony_ci      default="", dest="hilite_fanouts",
343cb93a386Sopenharmony_ci      help="Highlight fanouts of nodes matching the specified regexp.")
344cb93a386Sopenharmony_ci  option_parser.add_option(
345cb93a386Sopenharmony_ci      "", "--ignore-temp-rules",
346cb93a386Sopenharmony_ci      action="store_true", dest="ignore_temp_rules", default=False,
347cb93a386Sopenharmony_ci      help="Ignore !-prefixed (temporary) rules in DEPS files.")
348cb93a386Sopenharmony_ci  option_parser.add_option(
349cb93a386Sopenharmony_ci      "", "--ignore-specific-rules",
350cb93a386Sopenharmony_ci      action="store_true", dest="ignore_specific_rules", default=False,
351cb93a386Sopenharmony_ci      help="Ignore specific_include_rules section of DEPS files.")
352cb93a386Sopenharmony_ci  option_parser.add_option(
353cb93a386Sopenharmony_ci      "", "--hide-disallowed-deps",
354cb93a386Sopenharmony_ci      action="store_true", dest="hide_disallowed_deps", default=False,
355cb93a386Sopenharmony_ci      help="Hide disallowed dependencies in the output graph.")
356cb93a386Sopenharmony_ci  option_parser.add_option(
357cb93a386Sopenharmony_ci      "", "--unflatten",
358cb93a386Sopenharmony_ci      action="store_true", dest="unflatten_graph", default=False,
359cb93a386Sopenharmony_ci      help="Try to reformat the output graph so it is narrower and taller. "
360cb93a386Sopenharmony_ci           "Helps fight overly flat and wide graphs, but sometimes produces "
361cb93a386Sopenharmony_ci           "inferior results.")
362cb93a386Sopenharmony_ci  option_parser.add_option(
363cb93a386Sopenharmony_ci      "-v", "--verbose",
364cb93a386Sopenharmony_ci      action="store_true", default=False,
365cb93a386Sopenharmony_ci      help="Print debug logging")
366cb93a386Sopenharmony_ci  options, args = option_parser.parse_args()
367cb93a386Sopenharmony_ci
368cb93a386Sopenharmony_ci  if not options.out_file.endswith(options.out_format):
369cb93a386Sopenharmony_ci    options.out_file += '.' + options.out_format
370cb93a386Sopenharmony_ci
371cb93a386Sopenharmony_ci  deps_grapher = DepsGrapher(
372cb93a386Sopenharmony_ci      base_directory=options.base_directory,
373cb93a386Sopenharmony_ci      extra_repos=options.extra_repos,
374cb93a386Sopenharmony_ci      verbose=options.verbose,
375cb93a386Sopenharmony_ci      being_tested=False,
376cb93a386Sopenharmony_ci
377cb93a386Sopenharmony_ci      ignore_temp_rules=options.ignore_temp_rules,
378cb93a386Sopenharmony_ci      ignore_specific_rules=options.ignore_specific_rules,
379cb93a386Sopenharmony_ci      hide_disallowed_deps=options.hide_disallowed_deps,
380cb93a386Sopenharmony_ci
381cb93a386Sopenharmony_ci      out_file=options.out_file,
382cb93a386Sopenharmony_ci      out_format=options.out_format,
383cb93a386Sopenharmony_ci      layout_engine=options.layout_engine,
384cb93a386Sopenharmony_ci      unflatten_graph=options.unflatten_graph,
385cb93a386Sopenharmony_ci
386cb93a386Sopenharmony_ci      incl=options.incl,
387cb93a386Sopenharmony_ci      excl=options.excl,
388cb93a386Sopenharmony_ci      hilite_fanins=options.hilite_fanins,
389cb93a386Sopenharmony_ci      hilite_fanouts=options.hilite_fanouts)
390cb93a386Sopenharmony_ci
391cb93a386Sopenharmony_ci  if len(args) > 0:
392cb93a386Sopenharmony_ci    PrintUsage()
393cb93a386Sopenharmony_ci    return 1
394cb93a386Sopenharmony_ci
395cb93a386Sopenharmony_ci  print('Using base directory: ', deps_grapher.base_directory)
396cb93a386Sopenharmony_ci  print('include nodes       : ', options.incl)
397cb93a386Sopenharmony_ci  print('exclude nodes       : ', options.excl)
398cb93a386Sopenharmony_ci  print('highlight fanins of : ', options.hilite_fanins)
399cb93a386Sopenharmony_ci  print('highlight fanouts of: ', options.hilite_fanouts)
400cb93a386Sopenharmony_ci
401cb93a386Sopenharmony_ci  deps_grapher.DumpDependencies()
402cb93a386Sopenharmony_ci  return 0
403cb93a386Sopenharmony_ci
404cb93a386Sopenharmony_ci
405cb93a386Sopenharmony_ciif '__main__' == __name__:
406cb93a386Sopenharmony_ci  sys.exit(main())
407