15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Results object and results formatters for checkdeps tool."""
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import json
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class DependencyViolation(object):
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A single dependency violation."""
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, include_path, violated_rule, rules):
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The include or import path that is in violation of a rule.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.include_path = include_path
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The violated rule.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.violated_rule = violated_rule
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The set of rules containing self.violated_rule.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.rules = rules
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class DependeeStatus(object):
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Results object for a dependee file."""
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, dependee_path):
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Path of the file whose nonconforming dependencies are listed in
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # self.violations.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.dependee_path = dependee_path
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # List of DependencyViolation objects that apply to the dependee
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # file.  May be empty.
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.violations = []
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddViolation(self, violation):
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Adds a violation."""
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.violations.append(violation)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def HasViolations(self):
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns True if this dependee is violating one or more rules."""
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return not not self.violations
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ResultsFormatter(object):
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Base class for results formatters."""
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddError(self, dependee_status):
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Add a formatted result to |self.results| for |dependee_status|,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    which is guaranteed to return True for
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    |dependee_status.HasViolations|.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetResults(self):
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Returns the results.  May be overridden e.g. to process the
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    results that have been accumulated.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def PrintResults(self):
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Prints the results to stdout."""
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    raise NotImplementedError()
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NormalResultsFormatter(ResultsFormatter):
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A results formatting object that produces the classical,
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  detailed, human-readable output of the checkdeps tool.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, verbose):
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.results = []
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.verbose = verbose
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddError(self, dependee_status):
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines = []
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines.append('\nERROR in %s' % dependee_status.dependee_path)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for violation in dependee_status.violations:
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines.append(self.FormatViolation(violation, self.verbose))
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.results.append('\n'.join(lines))
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @staticmethod
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def FormatViolation(violation, verbose=False):
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines = []
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if verbose:
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines.append('  For %s' % violation.rules)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines.append(
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        '  Illegal include: "%s"\n    Because of %s' %
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (violation.include_path, str(violation.violated_rule)))
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '\n'.join(lines)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetResults(self):
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.results
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def PrintResults(self):
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for result in self.results:
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print result
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.results:
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print '\nFAILED\n'
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class JSONResultsFormatter(ResultsFormatter):
105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  """A results formatter that outputs results to a file as JSON."""
106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def __init__(self, output_path, wrapped_formatter=None):
108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.output_path = output_path
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.wrapped_formatter = wrapped_formatter
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.results = []
112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def AddError(self, dependee_status):
114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.results.append({
115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        'dependee_path': dependee_status.dependee_path,
116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        'violations': [{
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            'include_path': violation.include_path,
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            'violated_rule': violation.violated_rule.AsDependencyTuple(),
119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        } for violation in dependee_status.violations]
120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    })
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if self.wrapped_formatter:
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self.wrapped_formatter.AddError(dependee_status)
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def GetResults(self):
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    with open(self.output_path, 'w') as f:
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      f.write(json.dumps(self.results))
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return self.results
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def PrintResults(self):
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if self.wrapped_formatter:
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self.wrapped_formatter.PrintResults()
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    print self.results
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TemporaryRulesFormatter(ResultsFormatter):
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A results formatter that produces a single line per nonconforming
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  include. The combined output is suitable for directly pasting into a
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DEPS file as a list of temporary-allow rules.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.violations = set()
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddError(self, dependee_status):
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for violation in dependee_status.violations:
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.violations.add(violation.include_path)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetResults(self):
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ['  "!%s",' % path for path in sorted(self.violations)]
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def PrintResults(self):
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for result in self.GetResults():
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print result
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class CountViolationsFormatter(ResultsFormatter):
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A results formatter that produces a number, the count of #include
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statements that are in violation of the dependency rules.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Note that you normally want to instantiate DepsChecker with
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ignore_temp_rules=True when you use this formatter.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self):
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.count = 0
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddError(self, dependee_status):
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.count += len(dependee_status.violations)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetResults(self):
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '%d' % self.count
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def PrintResults(self):
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print self.count
179