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