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