checkpatch.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. # SPDX-License-Identifier: GPL-2.0+
  2. # Copyright (c) 2011 The Chromium OS Authors.
  3. #
  4. import collections
  5. import os
  6. import re
  7. import sys
  8. from patman import command
  9. from patman import gitutil
  10. from patman import terminal
  11. from patman import tools
  12. def FindCheckPatch():
  13. top_level = gitutil.GetTopLevel()
  14. try_list = [
  15. os.getcwd(),
  16. os.path.join(os.getcwd(), '..', '..'),
  17. os.path.join(top_level, 'tools'),
  18. os.path.join(top_level, 'scripts'),
  19. '%s/bin' % os.getenv('HOME'),
  20. ]
  21. # Look in current dir
  22. for path in try_list:
  23. fname = os.path.join(path, 'checkpatch.pl')
  24. if os.path.isfile(fname):
  25. return fname
  26. # Look upwwards for a Chrome OS tree
  27. while not os.path.ismount(path):
  28. fname = os.path.join(path, 'src', 'third_party', 'kernel', 'files',
  29. 'scripts', 'checkpatch.pl')
  30. if os.path.isfile(fname):
  31. return fname
  32. path = os.path.dirname(path)
  33. sys.exit('Cannot find checkpatch.pl - please put it in your ' +
  34. '~/bin directory or use --no-check')
  35. def CheckPatch(fname, verbose=False):
  36. """Run checkpatch.pl on a file.
  37. Returns:
  38. namedtuple containing:
  39. ok: False=failure, True=ok
  40. problems: List of problems, each a dict:
  41. 'type'; error or warning
  42. 'msg': text message
  43. 'file' : filename
  44. 'line': line number
  45. errors: Number of errors
  46. warnings: Number of warnings
  47. checks: Number of checks
  48. lines: Number of lines
  49. stdout: Full output of checkpatch
  50. """
  51. fields = ['ok', 'problems', 'errors', 'warnings', 'checks', 'lines',
  52. 'stdout']
  53. result = collections.namedtuple('CheckPatchResult', fields)
  54. result.ok = False
  55. result.errors, result.warning, result.checks = 0, 0, 0
  56. result.lines = 0
  57. result.problems = []
  58. chk = FindCheckPatch()
  59. item = {}
  60. result.stdout = command.Output(chk, '--no-tree', fname,
  61. raise_on_error=False)
  62. #pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
  63. #stdout, stderr = pipe.communicate()
  64. # total: 0 errors, 0 warnings, 159 lines checked
  65. # or:
  66. # total: 0 errors, 2 warnings, 7 checks, 473 lines checked
  67. re_stats = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)')
  68. re_stats_full = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)'
  69. ' checks, (\d+)')
  70. re_ok = re.compile('.*has no obvious style problems')
  71. re_bad = re.compile('.*has style problems, please review')
  72. re_error = re.compile('ERROR: (.*)')
  73. re_warning = re.compile('WARNING: (.*)')
  74. re_check = re.compile('CHECK: (.*)')
  75. re_file = re.compile('#\d+: FILE: ([^:]*):(\d+):')
  76. for line in result.stdout.splitlines():
  77. if verbose:
  78. print(line)
  79. # A blank line indicates the end of a message
  80. if not line and item:
  81. result.problems.append(item)
  82. item = {}
  83. match = re_stats_full.match(line)
  84. if not match:
  85. match = re_stats.match(line)
  86. if match:
  87. result.errors = int(match.group(1))
  88. result.warnings = int(match.group(2))
  89. if len(match.groups()) == 4:
  90. result.checks = int(match.group(3))
  91. result.lines = int(match.group(4))
  92. else:
  93. result.lines = int(match.group(3))
  94. elif re_ok.match(line):
  95. result.ok = True
  96. elif re_bad.match(line):
  97. result.ok = False
  98. err_match = re_error.match(line)
  99. warn_match = re_warning.match(line)
  100. file_match = re_file.match(line)
  101. check_match = re_check.match(line)
  102. if err_match:
  103. item['msg'] = err_match.group(1)
  104. item['type'] = 'error'
  105. elif warn_match:
  106. item['msg'] = warn_match.group(1)
  107. item['type'] = 'warning'
  108. elif check_match:
  109. item['msg'] = check_match.group(1)
  110. item['type'] = 'check'
  111. elif file_match:
  112. item['file'] = file_match.group(1)
  113. item['line'] = int(file_match.group(2))
  114. return result
  115. def GetWarningMsg(col, msg_type, fname, line, msg):
  116. '''Create a message for a given file/line
  117. Args:
  118. msg_type: Message type ('error' or 'warning')
  119. fname: Filename which reports the problem
  120. line: Line number where it was noticed
  121. msg: Message to report
  122. '''
  123. if msg_type == 'warning':
  124. msg_type = col.Color(col.YELLOW, msg_type)
  125. elif msg_type == 'error':
  126. msg_type = col.Color(col.RED, msg_type)
  127. elif msg_type == 'check':
  128. msg_type = col.Color(col.MAGENTA, msg_type)
  129. return '%s:%d: %s: %s\n' % (fname, line, msg_type, msg)
  130. def CheckPatches(verbose, args):
  131. '''Run the checkpatch.pl script on each patch'''
  132. error_count, warning_count, check_count = 0, 0, 0
  133. col = terminal.Color()
  134. for fname in args:
  135. result = CheckPatch(fname, verbose)
  136. if not result.ok:
  137. error_count += result.errors
  138. warning_count += result.warnings
  139. check_count += result.checks
  140. print('%d errors, %d warnings, %d checks for %s:' % (result.errors,
  141. result.warnings, result.checks, col.Color(col.BLUE, fname)))
  142. if (len(result.problems) != result.errors + result.warnings +
  143. result.checks):
  144. print("Internal error: some problems lost")
  145. for item in result.problems:
  146. sys.stderr.write(
  147. GetWarningMsg(col, item.get('type', '<unknown>'),
  148. item.get('file', '<unknown>'),
  149. item.get('line', 0), item.get('msg', 'message')))
  150. print
  151. #print(stdout)
  152. if error_count or warning_count or check_count:
  153. str = 'checkpatch.pl found %d error(s), %d warning(s), %d checks(s)'
  154. color = col.GREEN
  155. if warning_count:
  156. color = col.YELLOW
  157. if error_count:
  158. color = col.RED
  159. print(col.Color(color, str % (error_count, warning_count, check_count)))
  160. return False
  161. return True