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\.,\?\*/+-]*)?$')
72 skip_leading_whitespace_check
= False
73 skip_trailing_whitespace_check
= False
74 skip_block_whitespace_check
= False
75 skip_signoff_check
= False
77 # Don't enforce character limit on files that include these characters in their
78 # name, as they may have legitimate reasons to have longer lines.
80 # Python isn't checked as flake8 performs these checks during build.
81 line_length_blacklist
= ['.am', '.at', 'etc', '.in', '.m4', '.mk', '.patch',
85 def is_subtracted_line(line
):
86 """Returns TRUE if the line in question has been removed."""
87 return __regex_subtracted_line
.search(line
) is not None
90 def is_added_line(line
):
91 """Returns TRUE if the line in question is an added line.
94 return __regex_added_line
.search(line
) is not None or checking_file
98 """Returns the line formatted properly by removing diff syntax"""
100 if not checking_file
:
105 def leading_whitespace_is_spaces(line
):
106 """Returns TRUE if the leading whitespace in added lines is spaces
108 if skip_leading_whitespace_check
:
110 if (__regex_leading_with_whitespace_at_all
.search(line
) is not None and
111 __regex_single_line_feed
.search(line
) is None):
112 return __regex_leading_with_spaces
.search(line
) is not None
117 def trailing_whitespace_or_crlf(line
):
118 """Returns TRUE if the trailing characters is whitespace
120 if skip_trailing_whitespace_check
:
122 return (__regex_trailing_whitespace
.search(line
) is not None and
123 __regex_single_line_feed
.search(line
) is None)
126 def if_and_for_whitespace_checks(line
):
127 """Return TRUE if there is appropriate whitespace after if, for, while
129 if skip_block_whitespace_check
:
131 if (__regex_for_if_missing_whitespace
.search(line
) is not None or
132 __regex_for_if_too_much_whitespace
.search(line
) is not None or
133 __regex_for_if_parens_whitespace
.search(line
)):
138 def if_and_for_end_with_bracket_check(line
):
139 """Return TRUE if there is not a bracket at the end of an if, for, while
140 block which fits on a single line ie: 'if (foo)'"""
142 def balanced_parens(line
):
143 """This is a rather naive counter - it won't deal with quotes"""
152 if __regex_is_for_if_single_line_bracket
.search(line
) is not None:
153 if not balanced_parens(line
):
155 if __regex_ends_with_bracket
.search(line
) is None:
160 def ovs_checkpatch_parse(text
):
161 global print_file_name
168 scissors
= re
.compile(r
'^[\w]*---[\w]*')
169 hunks
= re
.compile('^(---|\+\+\+) (\S+)')
170 hunk_differences
= re
.compile(
171 r
'^@@ ([0-9-+]+),([0-9-+]+) ([0-9-+]+),([0-9-+]+) @@')
172 is_signature
= re
.compile(r
'((\s*Signed-off-by: )(.*))$',
174 is_co_author
= re
.compile(r
'(\s*(Co-authored-by: )(.*))$',
176 skip_line_length_check
= False
178 for line
in text
.split('\n'):
179 if current_file
!= previous_file
:
180 previous_file
= current_file
181 if any([fmt
in current_file
for fmt
in line_length_blacklist
]):
182 skip_line_length_check
= True
184 skip_line_length_check
= False
194 match
= hunks
.match(line
)
197 current_file
= match
.group(2)
198 print_file_name
= current_file
201 if scissors
.match(line
):
203 if not skip_signoff_check
:
204 if len(signatures
) == 0:
205 print_error("No signatures found.")
206 elif len(signatures
) != 1 + len(co_authors
):
207 print_error("Too many signoffs; "
208 "are you missing Co-authored-by lines?")
209 if not set(co_authors
) <= set(signatures
):
210 print_error("Co-authored-by/Signed-off-by corruption")
211 elif is_signature
.match(line
):
212 m
= is_signature
.match(line
)
213 signatures
.append(m
.group(3))
214 elif is_co_author
.match(line
):
215 m
= is_co_author
.match(line
)
216 co_authors
.append(m
.group(3))
219 newfile
= hunks
.match(line
)
221 current_file
= newfile
.group(2)
222 print_file_name
= current_file
224 reset_line_number
= hunk_differences
.match(line
)
225 if reset_line_number
:
226 lineno
= int(reset_line_number
.group(3))
230 if is_subtracted_line(line
):
232 if not is_added_line(line
):
235 cmp_line
= added_line(line
)
237 # Skip files which have /datapath in them, since they are
238 # linux or windows coding standards
239 if '/datapath' in current_file
:
241 if (not current_file
.endswith('.mk') and
242 not leading_whitespace_is_spaces(cmp_line
)):
244 print_warning("Line has non-spaces leading whitespace",
246 if trailing_whitespace_or_crlf(cmp_line
):
248 print_warning("Line has trailing whitespace", lineno
)
249 if len(cmp_line
) > 79 and not skip_line_length_check
:
251 print_warning("Line is greater than 79-characters long",
253 if not if_and_for_whitespace_checks(cmp_line
):
255 print_error("Improper whitespace around control block",
257 if not if_and_for_end_with_bracket_check(cmp_line
):
259 print_error("Inappropriate bracing around statement", lineno
)
261 print("\n%s\n" % line
)
262 if __errors
or __warnings
:
268 print("Open vSwitch checkpatch.py")
269 print("Checks a patch for trivial mistakes.")
271 print("%s [options] [patch file]" % sys
.argv
[0])
273 print("-h|--help\t\t\t\tThis help message")
274 print("-b|--skip-block-whitespace\t"
275 "Skips the if/while/for whitespace tests")
276 print("-f|--check-file\t\t\tCheck a file instead of a patchfile.")
277 print("-l|--skip-leading-whitespace\t"
278 "Skips the leading whitespace test")
279 print("-s|--skip-signoff-lines\t"
280 "Do not emit an error if no Signed-off-by line is present")
281 print("-t|--skip-trailing-whitespace\t"
282 "Skips the trailing whitespace test")
285 def ovs_checkpatch_file(filename
):
286 global __warnings
, __errors
, checking_file
288 mail
= email
.message_from_file(open(filename
, 'r'))
290 print_error("Unable to parse file '%s'. Is it a patch?" % filename
)
293 for part
in mail
.walk():
294 if part
.get_content_maintype() == 'multipart':
296 result
= ovs_checkpatch_parse(part
.get_payload(decode
=True))
298 print("Warnings: %d, Errors: %d" % (__warnings
, __errors
))
302 if __name__
== '__main__':
304 optlist
, args
= getopt
.getopt(sys
.argv
[1:], 'bhlstf',
307 "skip-block-whitespace",
308 "skip-leading-whitespace",
309 "skip-signoff-lines",
310 "skip-trailing-whitespace"])
312 print("Unknown option encountered. Please rerun with -h for help.")
316 if o
in ("-h", "--help"):
319 elif o
in ("-b", "--skip-block-whitespace"):
320 skip_block_whitespace_check
= True
321 elif o
in ("-l", "--skip-leading-whitespace"):
322 skip_leading_whitespace_check
= True
323 elif o
in ("-s", "--skip-signoff-lines"):
324 skip_signoff_check
= True
325 elif o
in ("-t", "--skip-trailing-whitespace"):
326 skip_trailing_whitespace_check
= True
327 elif o
in ("-f", "--check-file"):
330 print("Unknown option '%s'" % o
)
335 if sys
.stdin
.isatty():
338 sys
.exit(ovs_checkpatch_parse(sys
.stdin
.read()))
339 sys
.exit(ovs_checkpatch_file(filename
))