| # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 |  | 
 | """Results object and results formatters for checkdeps tool.""" | 
 |  | 
 |  | 
 | import json | 
 |  | 
 |  | 
 | class DependencyViolation(object): | 
 |   """A single dependency violation.""" | 
 |  | 
 |   def __init__(self, include_path, violated_rule, rules): | 
 |     # The include or import path that is in violation of a rule. | 
 |     self.include_path = include_path | 
 |  | 
 |     # The violated rule. | 
 |     self.violated_rule = violated_rule | 
 |  | 
 |     # The set of rules containing self.violated_rule. | 
 |     self.rules = rules | 
 |  | 
 |  | 
 | class DependeeStatus(object): | 
 |   """Results object for a dependee file.""" | 
 |  | 
 |   def __init__(self, dependee_path): | 
 |     # Path of the file whose nonconforming dependencies are listed in | 
 |     # self.violations. | 
 |     self.dependee_path = dependee_path | 
 |  | 
 |     # List of DependencyViolation objects that apply to the dependee | 
 |     # file.  May be empty. | 
 |     self.violations = [] | 
 |  | 
 |   def AddViolation(self, violation): | 
 |     """Adds a violation.""" | 
 |     self.violations.append(violation) | 
 |  | 
 |   def HasViolations(self): | 
 |     """Returns True if this dependee is violating one or more rules.""" | 
 |     return not not self.violations | 
 |  | 
 |  | 
 | class ResultsFormatter(object): | 
 |   """Base class for results formatters.""" | 
 |  | 
 |   def AddError(self, dependee_status): | 
 |     """Add a formatted result to |self.results| for |dependee_status|, | 
 |     which is guaranteed to return True for | 
 |     |dependee_status.HasViolations|. | 
 |     """ | 
 |     raise NotImplementedError() | 
 |  | 
 |   def GetResults(self): | 
 |     """Returns the results.  May be overridden e.g. to process the | 
 |     results that have been accumulated. | 
 |     """ | 
 |     raise NotImplementedError() | 
 |  | 
 |   def PrintResults(self): | 
 |     """Prints the results to stdout.""" | 
 |     raise NotImplementedError() | 
 |  | 
 |  | 
 | class NormalResultsFormatter(ResultsFormatter): | 
 |   """A results formatting object that produces the classical, | 
 |   detailed, human-readable output of the checkdeps tool. | 
 |   """ | 
 |  | 
 |   def __init__(self, verbose): | 
 |     self.results = [] | 
 |     self.verbose = verbose | 
 |  | 
 |   def AddError(self, dependee_status): | 
 |     lines = [] | 
 |     lines.append('\nERROR in %s' % dependee_status.dependee_path) | 
 |     for violation in dependee_status.violations: | 
 |       lines.append(self.FormatViolation(violation, self.verbose)) | 
 |     self.results.append('\n'.join(lines)) | 
 |  | 
 |   @staticmethod | 
 |   def FormatViolation(violation, verbose=False): | 
 |     lines = [] | 
 |     if verbose: | 
 |       lines.append('  For %s' % violation.rules) | 
 |     lines.append( | 
 |         '  Illegal include: "%s"\n    Because of %s' % | 
 |         (violation.include_path, str(violation.violated_rule))) | 
 |     return '\n'.join(lines) | 
 |  | 
 |   def GetResults(self): | 
 |     return self.results | 
 |  | 
 |   def PrintResults(self): | 
 |     for result in self.results: | 
 |       print result | 
 |     if self.results: | 
 |       print '\nFAILED\n' | 
 |  | 
 |  | 
 | class JSONResultsFormatter(ResultsFormatter): | 
 |   """A results formatter that outputs results to a file as JSON.""" | 
 |  | 
 |   def __init__(self, output_path, wrapped_formatter=None): | 
 |     self.output_path = output_path | 
 |     self.wrapped_formatter = wrapped_formatter | 
 |  | 
 |     self.results = [] | 
 |  | 
 |   def AddError(self, dependee_status): | 
 |     self.results.append({ | 
 |         'dependee_path': dependee_status.dependee_path, | 
 |         'violations': [{ | 
 |             'include_path': violation.include_path, | 
 |             'violated_rule': violation.violated_rule.AsDependencyTuple(), | 
 |         } for violation in dependee_status.violations] | 
 |     }) | 
 |  | 
 |     if self.wrapped_formatter: | 
 |       self.wrapped_formatter.AddError(dependee_status) | 
 |  | 
 |   def GetResults(self): | 
 |     with open(self.output_path, 'w') as f: | 
 |       f.write(json.dumps(self.results)) | 
 |  | 
 |     return self.results | 
 |  | 
 |   def PrintResults(self): | 
 |     if self.wrapped_formatter: | 
 |       self.wrapped_formatter.PrintResults() | 
 |       return | 
 |  | 
 |     print self.results | 
 |  | 
 |  | 
 | class TemporaryRulesFormatter(ResultsFormatter): | 
 |   """A results formatter that produces a single line per nonconforming | 
 |   include. The combined output is suitable for directly pasting into a | 
 |   DEPS file as a list of temporary-allow rules. | 
 |   """ | 
 |  | 
 |   def __init__(self): | 
 |     self.violations = set() | 
 |  | 
 |   def AddError(self, dependee_status): | 
 |     for violation in dependee_status.violations: | 
 |       self.violations.add(violation.include_path) | 
 |  | 
 |   def GetResults(self): | 
 |     return ['  "!%s",' % path for path in sorted(self.violations)] | 
 |  | 
 |   def PrintResults(self): | 
 |     for result in self.GetResults(): | 
 |       print result | 
 |  | 
 |  | 
 | class CountViolationsFormatter(ResultsFormatter): | 
 |   """A results formatter that produces a number, the count of #include | 
 |   statements that are in violation of the dependency rules. | 
 |  | 
 |   Note that you normally want to instantiate DepsChecker with | 
 |   ignore_temp_rules=True when you use this formatter. | 
 |   """ | 
 |  | 
 |   def __init__(self): | 
 |     self.count = 0 | 
 |  | 
 |   def AddError(self, dependee_status): | 
 |     self.count += len(dependee_status.violations) | 
 |  | 
 |   def GetResults(self): | 
 |     return '%d' % self.count | 
 |  | 
 |   def PrintResults(self): | 
 |     print self.count |