1cb93a386Sopenharmony_ci# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
3cb93a386Sopenharmony_ci# found in the LICENSE file.
4cb93a386Sopenharmony_ci
5cb93a386Sopenharmony_ci"""Results object and results formatters for checkdeps tool."""
6cb93a386Sopenharmony_ci
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci
9cb93a386Sopenharmony_ciimport json
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ciclass DependencyViolation(object):
13cb93a386Sopenharmony_ci  """A single dependency violation."""
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ci  def __init__(self, include_path, violated_rule, rules):
16cb93a386Sopenharmony_ci    # The include or import path that is in violation of a rule.
17cb93a386Sopenharmony_ci    self.include_path = include_path
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci    # The violated rule.
20cb93a386Sopenharmony_ci    self.violated_rule = violated_rule
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ci    # The set of rules containing self.violated_rule.
23cb93a386Sopenharmony_ci    self.rules = rules
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ciclass DependeeStatus(object):
27cb93a386Sopenharmony_ci  """Results object for a dependee file."""
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci  def __init__(self, dependee_path):
30cb93a386Sopenharmony_ci    # Path of the file whose nonconforming dependencies are listed in
31cb93a386Sopenharmony_ci    # self.violations.
32cb93a386Sopenharmony_ci    self.dependee_path = dependee_path
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ci    # List of DependencyViolation objects that apply to the dependee
35cb93a386Sopenharmony_ci    # file.  May be empty.
36cb93a386Sopenharmony_ci    self.violations = []
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci  def AddViolation(self, violation):
39cb93a386Sopenharmony_ci    """Adds a violation."""
40cb93a386Sopenharmony_ci    self.violations.append(violation)
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci  def HasViolations(self):
43cb93a386Sopenharmony_ci    """Returns True if this dependee is violating one or more rules."""
44cb93a386Sopenharmony_ci    return not not self.violations
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ciclass ResultsFormatter(object):
48cb93a386Sopenharmony_ci  """Base class for results formatters."""
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ci  def AddError(self, dependee_status):
51cb93a386Sopenharmony_ci    """Add a formatted result to |self.results| for |dependee_status|,
52cb93a386Sopenharmony_ci    which is guaranteed to return True for
53cb93a386Sopenharmony_ci    |dependee_status.HasViolations|.
54cb93a386Sopenharmony_ci    """
55cb93a386Sopenharmony_ci    raise NotImplementedError()
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci  def GetResults(self):
58cb93a386Sopenharmony_ci    """Returns the results.  May be overridden e.g. to process the
59cb93a386Sopenharmony_ci    results that have been accumulated.
60cb93a386Sopenharmony_ci    """
61cb93a386Sopenharmony_ci    raise NotImplementedError()
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_ci  def PrintResults(self):
64cb93a386Sopenharmony_ci    """Prints the results to stdout."""
65cb93a386Sopenharmony_ci    raise NotImplementedError()
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ciclass NormalResultsFormatter(ResultsFormatter):
69cb93a386Sopenharmony_ci  """A results formatting object that produces the classical,
70cb93a386Sopenharmony_ci  detailed, human-readable output of the checkdeps tool.
71cb93a386Sopenharmony_ci  """
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci  def __init__(self, verbose):
74cb93a386Sopenharmony_ci    self.results = []
75cb93a386Sopenharmony_ci    self.verbose = verbose
76cb93a386Sopenharmony_ci
77cb93a386Sopenharmony_ci  def AddError(self, dependee_status):
78cb93a386Sopenharmony_ci    lines = []
79cb93a386Sopenharmony_ci    lines.append('\nERROR in %s' % dependee_status.dependee_path)
80cb93a386Sopenharmony_ci    for violation in dependee_status.violations:
81cb93a386Sopenharmony_ci      lines.append(self.FormatViolation(violation, self.verbose))
82cb93a386Sopenharmony_ci    self.results.append('\n'.join(lines))
83cb93a386Sopenharmony_ci
84cb93a386Sopenharmony_ci  @staticmethod
85cb93a386Sopenharmony_ci  def FormatViolation(violation, verbose=False):
86cb93a386Sopenharmony_ci    lines = []
87cb93a386Sopenharmony_ci    if verbose:
88cb93a386Sopenharmony_ci      lines.append('  For %s' % violation.rules)
89cb93a386Sopenharmony_ci    lines.append(
90cb93a386Sopenharmony_ci        '  Illegal include: "%s"\n    Because of %s' %
91cb93a386Sopenharmony_ci        (violation.include_path, str(violation.violated_rule)))
92cb93a386Sopenharmony_ci    return '\n'.join(lines)
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci  def GetResults(self):
95cb93a386Sopenharmony_ci    return self.results
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_ci  def PrintResults(self):
98cb93a386Sopenharmony_ci    for result in self.results:
99cb93a386Sopenharmony_ci      print(result)
100cb93a386Sopenharmony_ci    if self.results:
101cb93a386Sopenharmony_ci      print('\nFAILED\n')
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ciclass JSONResultsFormatter(ResultsFormatter):
105cb93a386Sopenharmony_ci  """A results formatter that outputs results to a file as JSON."""
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci  def __init__(self, output_path, wrapped_formatter=None):
108cb93a386Sopenharmony_ci    self.output_path = output_path
109cb93a386Sopenharmony_ci    self.wrapped_formatter = wrapped_formatter
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_ci    self.results = []
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_ci  def AddError(self, dependee_status):
114cb93a386Sopenharmony_ci    self.results.append({
115cb93a386Sopenharmony_ci        'dependee_path': dependee_status.dependee_path,
116cb93a386Sopenharmony_ci        'violations': [{
117cb93a386Sopenharmony_ci            'include_path': violation.include_path,
118cb93a386Sopenharmony_ci            'violated_rule': violation.violated_rule.AsDependencyTuple(),
119cb93a386Sopenharmony_ci        } for violation in dependee_status.violations]
120cb93a386Sopenharmony_ci    })
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    if self.wrapped_formatter:
123cb93a386Sopenharmony_ci      self.wrapped_formatter.AddError(dependee_status)
124cb93a386Sopenharmony_ci
125cb93a386Sopenharmony_ci  def GetResults(self):
126cb93a386Sopenharmony_ci    with open(self.output_path, 'w') as f:
127cb93a386Sopenharmony_ci      f.write(json.dumps(self.results))
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci    return self.results
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci  def PrintResults(self):
132cb93a386Sopenharmony_ci    if self.wrapped_formatter:
133cb93a386Sopenharmony_ci      self.wrapped_formatter.PrintResults()
134cb93a386Sopenharmony_ci      return
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_ci    print(self.results)
137cb93a386Sopenharmony_ci
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ciclass TemporaryRulesFormatter(ResultsFormatter):
140cb93a386Sopenharmony_ci  """A results formatter that produces a single line per nonconforming
141cb93a386Sopenharmony_ci  include. The combined output is suitable for directly pasting into a
142cb93a386Sopenharmony_ci  DEPS file as a list of temporary-allow rules.
143cb93a386Sopenharmony_ci  """
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_ci  def __init__(self):
146cb93a386Sopenharmony_ci    self.violations = set()
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_ci  def AddError(self, dependee_status):
149cb93a386Sopenharmony_ci    for violation in dependee_status.violations:
150cb93a386Sopenharmony_ci      self.violations.add(violation.include_path)
151cb93a386Sopenharmony_ci
152cb93a386Sopenharmony_ci  def GetResults(self):
153cb93a386Sopenharmony_ci    return ['  "!%s",' % path for path in sorted(self.violations)]
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci  def PrintResults(self):
156cb93a386Sopenharmony_ci    for result in self.GetResults():
157cb93a386Sopenharmony_ci      print(result)
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ciclass CountViolationsFormatter(ResultsFormatter):
161cb93a386Sopenharmony_ci  """A results formatter that produces a number, the count of #include
162cb93a386Sopenharmony_ci  statements that are in violation of the dependency rules.
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci  Note that you normally want to instantiate DepsChecker with
165cb93a386Sopenharmony_ci  ignore_temp_rules=True when you use this formatter.
166cb93a386Sopenharmony_ci  """
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_ci  def __init__(self):
169cb93a386Sopenharmony_ci    self.count = 0
170cb93a386Sopenharmony_ci
171cb93a386Sopenharmony_ci  def AddError(self, dependee_status):
172cb93a386Sopenharmony_ci    self.count += len(dependee_status.violations)
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_ci  def GetResults(self):
175cb93a386Sopenharmony_ci    return '%d' % self.count
176cb93a386Sopenharmony_ci
177cb93a386Sopenharmony_ci  def PrintResults(self):
178cb93a386Sopenharmony_ci    print(self.count)
179