| # Copyright 2012 The Chromium Authors |
| # 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) |