results.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. """Results object and results formatters for checkdeps tool."""
  5. import json
  6. class DependencyViolation(object):
  7. """A single dependency violation."""
  8. def __init__(self, include_path, violated_rule, rules):
  9. # The include or import path that is in violation of a rule.
  10. self.include_path = include_path
  11. # The violated rule.
  12. self.violated_rule = violated_rule
  13. # The set of rules containing self.violated_rule.
  14. self.rules = rules
  15. class DependeeStatus(object):
  16. """Results object for a dependee file."""
  17. def __init__(self, dependee_path):
  18. # Path of the file whose nonconforming dependencies are listed in
  19. # self.violations.
  20. self.dependee_path = dependee_path
  21. # List of DependencyViolation objects that apply to the dependee
  22. # file. May be empty.
  23. self.violations = []
  24. def AddViolation(self, violation):
  25. """Adds a violation."""
  26. self.violations.append(violation)
  27. def HasViolations(self):
  28. """Returns True if this dependee is violating one or more rules."""
  29. return not not self.violations
  30. class ResultsFormatter(object):
  31. """Base class for results formatters."""
  32. def AddError(self, dependee_status):
  33. """Add a formatted result to |self.results| for |dependee_status|,
  34. which is guaranteed to return True for
  35. |dependee_status.HasViolations|.
  36. """
  37. raise NotImplementedError()
  38. def GetResults(self):
  39. """Returns the results. May be overridden e.g. to process the
  40. results that have been accumulated.
  41. """
  42. raise NotImplementedError()
  43. def PrintResults(self):
  44. """Prints the results to stdout."""
  45. raise NotImplementedError()
  46. class NormalResultsFormatter(ResultsFormatter):
  47. """A results formatting object that produces the classical,
  48. detailed, human-readable output of the checkdeps tool.
  49. """
  50. def __init__(self, verbose):
  51. self.results = []
  52. self.verbose = verbose
  53. def AddError(self, dependee_status):
  54. lines = []
  55. lines.append('\nERROR in %s' % dependee_status.dependee_path)
  56. for violation in dependee_status.violations:
  57. lines.append(self.FormatViolation(violation, self.verbose))
  58. self.results.append('\n'.join(lines))
  59. @staticmethod
  60. def FormatViolation(violation, verbose=False):
  61. lines = []
  62. if verbose:
  63. lines.append(' For %s' % violation.rules)
  64. lines.append(
  65. ' Illegal include: "%s"\n Because of %s' %
  66. (violation.include_path, str(violation.violated_rule)))
  67. return '\n'.join(lines)
  68. def GetResults(self):
  69. return self.results
  70. def PrintResults(self):
  71. for result in self.results:
  72. print(result)
  73. if self.results:
  74. print('\nFAILED\n')
  75. class JSONResultsFormatter(ResultsFormatter):
  76. """A results formatter that outputs results to a file as JSON."""
  77. def __init__(self, output_path, wrapped_formatter=None):
  78. self.output_path = output_path
  79. self.wrapped_formatter = wrapped_formatter
  80. self.results = []
  81. def AddError(self, dependee_status):
  82. self.results.append({
  83. 'dependee_path': dependee_status.dependee_path,
  84. 'violations': [{
  85. 'include_path': violation.include_path,
  86. 'violated_rule': violation.violated_rule.AsDependencyTuple(),
  87. } for violation in dependee_status.violations]
  88. })
  89. if self.wrapped_formatter:
  90. self.wrapped_formatter.AddError(dependee_status)
  91. def GetResults(self):
  92. with open(self.output_path, 'w') as f:
  93. f.write(json.dumps(self.results))
  94. return self.results
  95. def PrintResults(self):
  96. if self.wrapped_formatter:
  97. self.wrapped_formatter.PrintResults()
  98. return
  99. print(self.results)
  100. class TemporaryRulesFormatter(ResultsFormatter):
  101. """A results formatter that produces a single line per nonconforming
  102. include. The combined output is suitable for directly pasting into a
  103. DEPS file as a list of temporary-allow rules.
  104. """
  105. def __init__(self):
  106. self.violations = set()
  107. def AddError(self, dependee_status):
  108. for violation in dependee_status.violations:
  109. self.violations.add(violation.include_path)
  110. def GetResults(self):
  111. return [' "!%s",' % path for path in sorted(self.violations)]
  112. def PrintResults(self):
  113. for result in self.GetResults():
  114. print(result)
  115. class CountViolationsFormatter(ResultsFormatter):
  116. """A results formatter that produces a number, the count of #include
  117. statements that are in violation of the dependency rules.
  118. Note that you normally want to instantiate DepsChecker with
  119. ignore_temp_rules=True when you use this formatter.
  120. """
  121. def __init__(self):
  122. self.count = 0
  123. def AddError(self, dependee_status):
  124. self.count += len(dependee_status.violations)
  125. def GetResults(self):
  126. return '%d' % self.count
  127. def PrintResults(self):
  128. print(self.count)