|  | # 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 |