2 # Copyright (c) 2016 Red Hat, Inc.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 from __future__
import print_function
24 print_file_name
= None
29 global print_file_name
31 print("In file %s" % print_file_name
)
32 print_file_name
= None
35 def print_error(message
, lineno
=None):
38 if lineno
is not None:
39 print("E(%d): %s" % (lineno
, message
))
41 print("E: %s" % (message
))
43 __errors
= __errors
+ 1
46 def print_warning(message
, lineno
=None):
50 print("W(%d): %s" % (lineno
, message
))
52 print("W: %s" % (message
))
54 __warnings
= __warnings
+ 1
57 __regex_added_line
= re
.compile(r
'^\+{1,2}[^\+][\w\W]*')
58 __regex_subtracted_line
= re
.compile(r
'^\-{1,2}[^\-][\w\W]*')
59 __regex_leading_with_whitespace_at_all
= re
.compile(r
'^\s+')
60 __regex_leading_with_spaces
= re
.compile(r
'^ +[\S]+')
61 __regex_trailing_whitespace
= re
.compile(r
'[^\S]+$')
62 __regex_single_line_feed
= re
.compile(r
'^\f$')
63 __regex_for_if_missing_whitespace
= re
.compile(r
' +(if|for|while)[\(]')
64 __regex_for_if_too_much_whitespace
= re
.compile(r
' +(if|for|while) +[\(]')
65 __regex_for_if_parens_whitespace
= \
66 re
.compile(r
' +(if|for|while) \( +[\s\S]+\)')
67 __regex_is_for_if_single_line_bracket
= \
68 re
.compile(r
'^ +(if|for|while) \(.*\)')
69 __regex_ends_with_bracket
= \
70 re
.compile(r
'[^\s]\) {(\s+/\*[\s\Sa-zA-Z0-9\.,\?\*/+-]*)?$')
71 __regex_ptr_declaration_missing_whitespace
= re
.compile(r
'[a-zA-Z0-9]\*')
73 skip_leading_whitespace_check
= False
74 skip_trailing_whitespace_check
= False
75 skip_block_whitespace_check
= False
76 skip_signoff_check
= False
78 # Don't enforce character limit on files that include these characters in their
79 # name, as they may have legitimate reasons to have longer lines.
81 # Python isn't checked as flake8 performs these checks during build.
82 line_length_blacklist
= ['.am', '.at', 'etc', '.in', '.m4', '.mk', '.patch',
86 def is_subtracted_line(line
):
87 """Returns TRUE if the line in question has been removed."""
88 return __regex_subtracted_line
.search(line
) is not None
91 def is_added_line(line
):
92 """Returns TRUE if the line in question is an added line.
95 return __regex_added_line
.search(line
) is not None or checking_file
99 """Returns the line formatted properly by removing diff syntax"""
101 if not checking_file
:
106 def leading_whitespace_is_spaces(line
):
107 """Returns TRUE if the leading whitespace in added lines is spaces
109 if skip_leading_whitespace_check
:
111 if (__regex_leading_with_whitespace_at_all
.search(line
) is not None and
112 __regex_single_line_feed
.search(line
) is None):
113 return __regex_leading_with_spaces
.search(line
) is not None
118 def trailing_whitespace_or_crlf(line
):
119 """Returns TRUE if the trailing characters is whitespace
121 if skip_trailing_whitespace_check
:
123 return (__regex_trailing_whitespace
.search(line
) is not None and
124 __regex_single_line_feed
.search(line
) is None)
127 def if_and_for_whitespace_checks(line
):
128 """Return TRUE if there is appropriate whitespace after if, for, while
130 if skip_block_whitespace_check
:
132 if (__regex_for_if_missing_whitespace
.search(line
) is not None or
133 __regex_for_if_too_much_whitespace
.search(line
) is not None or
134 __regex_for_if_parens_whitespace
.search(line
)):
139 def if_and_for_end_with_bracket_check(line
):
140 """Return TRUE if there is not a bracket at the end of an if, for, while
141 block which fits on a single line ie: 'if (foo)'"""
143 def balanced_parens(line
):
144 """This is a rather naive counter - it won't deal with quotes"""
153 if __regex_is_for_if_single_line_bracket
.search(line
) is not None:
154 if not balanced_parens(line
):
156 if __regex_ends_with_bracket
.search(line
) is None:
161 def pointer_whitespace_check(line
):
162 """Return TRUE if there is no space between a pointer name and the
163 asterisk that denotes this is a apionter type, ie: 'struct foo*'"""
164 return __regex_ptr_declaration_missing_whitespace
.search(line
) is not None
167 def line_length_check(line
):
168 """Return TRUE if the line length is too long"""
177 lambda x
: not any([fmt
in x
for fmt
in line_length_blacklist
]),
178 'check': lambda x
: line_length_check(x
),
180 lambda x
: print_warning("Line is greater than 79-characters long", x
)}
184 def get_file_type_checks(filename
):
185 """Returns the list of checks for a file based on matching the filename
190 if check
['regex'] is None and check
['match_name'] is None:
191 checkList
.append(check
)
192 if check
['regex'] is not None and \
193 re
.compile(check
['regex']).search(filename
) is not None:
194 checkList
.append(check
)
195 elif check
['match_name'] is not None and check
['match_name'](filename
):
196 checkList
.append(check
)
200 def run_checks(current_file
, line
, lineno
):
201 """Runs the various checks for the particular line. This will take
202 filename into account."""
204 for check
in get_file_type_checks(current_file
):
205 if check
['check'](line
):
206 check
['print'](lineno
)
210 print("\n%s\n" % line
)
213 def ovs_checkpatch_parse(text
):
214 global print_file_name
221 scissors
= re
.compile(r
'^[\w]*---[\w]*')
222 hunks
= re
.compile('^(---|\+\+\+) (\S+)')
223 hunk_differences
= re
.compile(
224 r
'^@@ ([0-9-+]+),([0-9-+]+) ([0-9-+]+),([0-9-+]+) @@')
225 is_signature
= re
.compile(r
'((\s*Signed-off-by: )(.*))$',
227 is_co_author
= re
.compile(r
'(\s*(Co-authored-by: )(.*))$',
230 for line
in text
.decode().split('\n'):
231 if current_file
!= previous_file
:
232 previous_file
= current_file
242 match
= hunks
.match(line
)
245 current_file
= match
.group(2)
246 print_file_name
= current_file
249 if scissors
.match(line
):
251 if not skip_signoff_check
:
252 if len(signatures
) == 0:
253 print_error("No signatures found.")
254 elif len(signatures
) != 1 + len(co_authors
):
255 print_error("Too many signoffs; "
256 "are you missing Co-authored-by lines?")
257 if not set(co_authors
) <= set(signatures
):
258 print_error("Co-authored-by/Signed-off-by corruption")
259 elif is_signature
.match(line
):
260 m
= is_signature
.match(line
)
261 signatures
.append(m
.group(3))
262 elif is_co_author
.match(line
):
263 m
= is_co_author
.match(line
)
264 co_authors
.append(m
.group(3))
266 newfile
= hunks
.match(line
)
268 current_file
= newfile
.group(2)
269 print_file_name
= current_file
271 reset_line_number
= hunk_differences
.match(line
)
272 if reset_line_number
:
273 lineno
= int(reset_line_number
.group(3))
277 if is_subtracted_line(line
):
279 if not is_added_line(line
):
282 cmp_line
= added_line(line
)
284 # Skip files which have /datapath in them, since they are
285 # linux or windows coding standards
286 if '/datapath' in current_file
:
288 if (not current_file
.endswith('.mk') and
289 not leading_whitespace_is_spaces(cmp_line
)):
290 print_warning("Line has non-spaces leading whitespace",
292 run_checks(current_file
, cmp_line
, lineno
)
293 if trailing_whitespace_or_crlf(cmp_line
):
294 print_warning("Line has trailing whitespace", lineno
)
295 if not if_and_for_whitespace_checks(cmp_line
):
296 print_error("Improper whitespace around control block",
298 if not if_and_for_end_with_bracket_check(cmp_line
):
299 print_error("Inappropriate bracing around statement", lineno
)
300 if pointer_whitespace_check(cmp_line
):
301 print_error("Inappropriate spacing in pointer declaration",
303 if __errors
or __warnings
:
309 print("Open vSwitch checkpatch.py")
310 print("Checks a patch for trivial mistakes.")
312 print("%s [options] [patch file]" % sys
.argv
[0])
314 print("-h|--help\t\t\t\tThis help message")
315 print("-b|--skip-block-whitespace\t"
316 "Skips the if/while/for whitespace tests")
317 print("-f|--check-file\t\t\tCheck a file instead of a patchfile.")
318 print("-l|--skip-leading-whitespace\t"
319 "Skips the leading whitespace test")
320 print("-s|--skip-signoff-lines\t"
321 "Do not emit an error if no Signed-off-by line is present")
322 print("-t|--skip-trailing-whitespace\t"
323 "Skips the trailing whitespace test")
326 def ovs_checkpatch_file(filename
):
327 global __warnings
, __errors
, checking_file
329 mail
= email
.message_from_file(open(filename
, 'r'))
331 print_error("Unable to parse file '%s'. Is it a patch?" % filename
)
334 for part
in mail
.walk():
335 if part
.get_content_maintype() == 'multipart':
337 result
= ovs_checkpatch_parse(part
.get_payload(decode
=True))
339 print("Warnings: %d, Errors: %d" % (__warnings
, __errors
))
343 if __name__
== '__main__':
345 optlist
, args
= getopt
.getopt(sys
.argv
[1:], 'bhlstf',
348 "skip-block-whitespace",
349 "skip-leading-whitespace",
350 "skip-signoff-lines",
351 "skip-trailing-whitespace"])
353 print("Unknown option encountered. Please rerun with -h for help.")
357 if o
in ("-h", "--help"):
360 elif o
in ("-b", "--skip-block-whitespace"):
361 skip_block_whitespace_check
= True
362 elif o
in ("-l", "--skip-leading-whitespace"):
363 skip_leading_whitespace_check
= True
364 elif o
in ("-s", "--skip-signoff-lines"):
365 skip_signoff_check
= True
366 elif o
in ("-t", "--skip-trailing-whitespace"):
367 skip_trailing_whitespace_check
= True
368 elif o
in ("-f", "--check-file"):
371 print("Unknown option '%s'" % o
)
376 if sys
.stdin
.isatty():
379 sys
.exit(ovs_checkpatch_parse(sys
.stdin
.read()))
380 sys
.exit(ovs_checkpatch_file(filename
))