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 ovs_checkpatch_parse(text
):
168 global print_file_name
175 scissors
= re
.compile(r
'^[\w]*---[\w]*')
176 hunks
= re
.compile('^(---|\+\+\+) (\S+)')
177 hunk_differences
= re
.compile(
178 r
'^@@ ([0-9-+]+),([0-9-+]+) ([0-9-+]+),([0-9-+]+) @@')
179 is_signature
= re
.compile(r
'((\s*Signed-off-by: )(.*))$',
181 is_co_author
= re
.compile(r
'(\s*(Co-authored-by: )(.*))$',
183 skip_line_length_check
= False
185 for line
in text
.split('\n'):
186 if current_file
!= previous_file
:
187 previous_file
= current_file
188 if any([fmt
in current_file
for fmt
in line_length_blacklist
]):
189 skip_line_length_check
= True
191 skip_line_length_check
= False
201 match
= hunks
.match(line
)
204 current_file
= match
.group(2)
205 print_file_name
= current_file
208 if scissors
.match(line
):
210 if not skip_signoff_check
:
211 if len(signatures
) == 0:
212 print_error("No signatures found.")
213 elif len(signatures
) != 1 + len(co_authors
):
214 print_error("Too many signoffs; "
215 "are you missing Co-authored-by lines?")
216 if not set(co_authors
) <= set(signatures
):
217 print_error("Co-authored-by/Signed-off-by corruption")
218 elif is_signature
.match(line
):
219 m
= is_signature
.match(line
)
220 signatures
.append(m
.group(3))
221 elif is_co_author
.match(line
):
222 m
= is_co_author
.match(line
)
223 co_authors
.append(m
.group(3))
226 newfile
= hunks
.match(line
)
228 current_file
= newfile
.group(2)
229 print_file_name
= current_file
231 reset_line_number
= hunk_differences
.match(line
)
232 if reset_line_number
:
233 lineno
= int(reset_line_number
.group(3))
237 if is_subtracted_line(line
):
239 if not is_added_line(line
):
242 cmp_line
= added_line(line
)
244 # Skip files which have /datapath in them, since they are
245 # linux or windows coding standards
246 if '/datapath' in current_file
:
248 if (not current_file
.endswith('.mk') and
249 not leading_whitespace_is_spaces(cmp_line
)):
251 print_warning("Line has non-spaces leading whitespace",
253 if trailing_whitespace_or_crlf(cmp_line
):
255 print_warning("Line has trailing whitespace", lineno
)
256 if len(cmp_line
) > 79 and not skip_line_length_check
:
258 print_warning("Line is greater than 79-characters long",
260 if not if_and_for_whitespace_checks(cmp_line
):
262 print_error("Improper whitespace around control block",
264 if not if_and_for_end_with_bracket_check(cmp_line
):
266 print_error("Inappropriate bracing around statement", lineno
)
267 if pointer_whitespace_check(cmp_line
):
269 print_error("Inappropriate spacing in pointer declaration",
272 print("\n%s\n" % line
)
273 if __errors
or __warnings
:
279 print("Open vSwitch checkpatch.py")
280 print("Checks a patch for trivial mistakes.")
282 print("%s [options] [patch file]" % sys
.argv
[0])
284 print("-h|--help\t\t\t\tThis help message")
285 print("-b|--skip-block-whitespace\t"
286 "Skips the if/while/for whitespace tests")
287 print("-f|--check-file\t\t\tCheck a file instead of a patchfile.")
288 print("-l|--skip-leading-whitespace\t"
289 "Skips the leading whitespace test")
290 print("-s|--skip-signoff-lines\t"
291 "Do not emit an error if no Signed-off-by line is present")
292 print("-t|--skip-trailing-whitespace\t"
293 "Skips the trailing whitespace test")
296 def ovs_checkpatch_file(filename
):
297 global __warnings
, __errors
, checking_file
299 mail
= email
.message_from_file(open(filename
, 'r'))
301 print_error("Unable to parse file '%s'. Is it a patch?" % filename
)
304 for part
in mail
.walk():
305 if part
.get_content_maintype() == 'multipart':
307 result
= ovs_checkpatch_parse(part
.get_payload(decode
=True))
309 print("Warnings: %d, Errors: %d" % (__warnings
, __errors
))
313 if __name__
== '__main__':
315 optlist
, args
= getopt
.getopt(sys
.argv
[1:], 'bhlstf',
318 "skip-block-whitespace",
319 "skip-leading-whitespace",
320 "skip-signoff-lines",
321 "skip-trailing-whitespace"])
323 print("Unknown option encountered. Please rerun with -h for help.")
327 if o
in ("-h", "--help"):
330 elif o
in ("-b", "--skip-block-whitespace"):
331 skip_block_whitespace_check
= True
332 elif o
in ("-l", "--skip-leading-whitespace"):
333 skip_leading_whitespace_check
= True
334 elif o
in ("-s", "--skip-signoff-lines"):
335 skip_signoff_check
= True
336 elif o
in ("-t", "--skip-trailing-whitespace"):
337 skip_trailing_whitespace_check
= True
338 elif o
in ("-f", "--check-file"):
341 print("Unknown option '%s'" % o
)
346 if sys
.stdin
.isatty():
349 sys
.exit(ovs_checkpatch_parse(sys
.stdin
.read()))
350 sys
.exit(ovs_checkpatch_file(filename
))