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"""Results object and results formatters for checkdeps tool.""" 6 7 8 9import json 10 11 12class 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 26class 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 47class 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 68class 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 104class 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 139class 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 160class 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