dpif-netlink: add GENEVE creation support
[cascardo/ovs.git] / utilities / checkpatch.py
1 #!/usr/bin/env python
2 # Copyright (c) 2016 Red Hat, Inc.
3 #
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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
16
17 import email
18 import getopt
19 import re
20 import sys
21
22 __errors = 0
23 __warnings = 0
24
25
26 def print_error(message, lineno=None):
27     global __errors
28     if lineno is not None:
29         print("E(%d): %s" % (lineno, message))
30     else:
31         print("E: %s" % (message))
32
33     __errors = __errors + 1
34
35
36 def print_warning(message, lineno=None):
37     global __warnings
38     if lineno:
39         print("W(%d): %s" % (lineno, message))
40     else:
41         print("W: %s" % (message))
42
43     __warnings = __warnings + 1
44
45
46 __regex_added_line = re.compile(r'^\+{1,2}[^\+][\w\W]*')
47 __regex_leading_with_whitespace_at_all = re.compile(r'^\s+')
48 __regex_leading_with_spaces = re.compile(r'^ +[\S]+')
49 __regex_trailing_whitespace = re.compile(r'[^\S]+$')
50 __regex_single_line_feed = re.compile(r'^\f$')
51 __regex_for_if_missing_whitespace = re.compile(r'(if|for|while)[\(]')
52 __regex_for_if_too_much_whitespace = re.compile(r'(if|for|while)  +[\(]')
53 __regex_for_if_parens_whitespace = re.compile(r'(if|for|while) \( +[\s\S]+\)')
54 __regex_is_for_if_single_line_bracket = \
55     re.compile(r'^ +(if|for|while) \(.*\)')
56
57 __regex_ends_with_bracket = re.compile(r'[^\s]\) {$')
58
59 skip_leading_whitespace_check = False
60 skip_trailing_whitespace_check = False
61 skip_block_whitespace_check = False
62 skip_signoff_check = False
63
64 # Don't enforce character limit on files that include these characters in their
65 # name, as they may have legitimate reasons to have longer lines.
66 #
67 # Python isn't checked as flake8 performs these checks during build.
68 line_length_blacklist = ['.am', '.at', 'etc', '.in', '.m4', '.mk', '.patch',
69                          '.py']
70
71
72 def is_added_line(line):
73     """Returns TRUE if the line in question is an added line.
74     """
75     return __regex_added_line.search(line) is not None
76
77
78 def leading_whitespace_is_spaces(line):
79     """Returns TRUE if the leading whitespace in added lines is spaces
80     """
81     if skip_leading_whitespace_check:
82         return True
83     if (__regex_leading_with_whitespace_at_all.search(line) is not None and
84             __regex_single_line_feed.search(line) is None):
85         return __regex_leading_with_spaces.search(line) is not None
86
87     return True
88
89
90 def trailing_whitespace_or_crlf(line):
91     """Returns TRUE if the trailing characters is whitespace
92     """
93     if skip_trailing_whitespace_check:
94         return False
95     return (__regex_trailing_whitespace.search(line) is not None and
96             __regex_single_line_feed.search(line) is None)
97
98
99 def if_and_for_whitespace_checks(line):
100     """Return TRUE if there is appropriate whitespace after if, for, while
101     """
102     if skip_block_whitespace_check:
103         return True
104     if (__regex_for_if_missing_whitespace.search(line) is not None or
105             __regex_for_if_too_much_whitespace.search(line) is not None or
106             __regex_for_if_parens_whitespace.search(line)):
107         return False
108     return True
109
110
111 def if_and_for_end_with_bracket_check(line):
112     """Return TRUE if there is not a bracket at the end of an if, for, while
113        block which fits on a single line ie: 'if (foo)'"""
114
115     def balanced_parens(line):
116         """This is a rather naive counter - it won't deal with quotes"""
117         balance = 0
118         for letter in line:
119             if letter is '(':
120                 balance += 1
121             elif letter is ')':
122                 balance -= 1
123         return balance is 0
124
125     if __regex_is_for_if_single_line_bracket.search(line) is not None:
126         if not balanced_parens(line):
127             return True
128         if __regex_ends_with_bracket.search(line) is None:
129             return False
130     return True
131
132
133 def ovs_checkpatch_parse(text):
134     lineno = 0
135     signatures = []
136     co_authors = []
137     parse = 0
138     current_file = ''
139     previous_file = ''
140     scissors = re.compile(r'^[\w]*---[\w]*')
141     hunks = re.compile('^(---|\+\+\+) (\S+)')
142     is_signature = re.compile(r'((\s*Signed-off-by: )(.*))$',
143                               re.I | re.M | re.S)
144     is_co_author = re.compile(r'(\s*(Co-authored-by: )(.*))$',
145                               re.I | re.M | re.S)
146     skip_line_length_check = False
147
148     for line in text.split('\n'):
149         if current_file != previous_file:
150             previous_file = current_file
151             if any([fmt in current_file for fmt in line_length_blacklist]):
152                 skip_line_length_check = True
153             else:
154                 skip_line_length_check = False
155
156         lineno = lineno + 1
157         if len(line) <= 0:
158             continue
159
160         if parse == 1:
161             match = hunks.match(line)
162             if match:
163                 parse = parse + 1
164                 current_file = match.group(2)
165             continue
166         elif parse == 0:
167             if scissors.match(line):
168                 parse = parse + 1
169                 if not skip_signoff_check:
170                     if len(signatures) == 0:
171                         print_error("No signatures found.")
172                     if len(signatures) != 1 + len(co_authors):
173                         print_error("Too many signoffs; "
174                                     "are you missing Co-authored-by lines?")
175                     if not set(co_authors) <= set(signatures):
176                         print_error("Co-authored-by/Signed-off-by corruption")
177             elif is_signature.match(line):
178                 m = is_signature.match(line)
179                 signatures.append(m.group(3))
180             elif is_co_author.match(line):
181                 m = is_co_author.match(line)
182                 co_authors.append(m.group(3))
183         elif parse == 2:
184             print_line = False
185             newfile = hunks.match(line)
186             if newfile:
187                 current_file = newfile.group(2)
188                 continue
189             if not is_added_line(line):
190                 continue
191             # Skip files which have /datapath in them, since they are
192             # linux or windows coding standards
193             if '/datapath' in current_file:
194                 continue
195             if (not current_file.endswith('.mk') and
196                     not leading_whitespace_is_spaces(line[1:])):
197                 print_line = True
198                 print_warning("Line has non-spaces leading whitespace",
199                               lineno)
200             if trailing_whitespace_or_crlf(line[1:]):
201                 print_line = True
202                 print_warning("Line has trailing whitespace", lineno)
203             if len(line[1:]) > 79 and not skip_line_length_check:
204                 print_line = True
205                 print_warning("Line is greater than 79-characters long",
206                               lineno)
207             if not if_and_for_whitespace_checks(line[1:]):
208                 print_line = True
209                 print_warning("Improper whitespace around control block",
210                               lineno)
211             if not if_and_for_end_with_bracket_check(line[1:]):
212                 print_line = True
213                 print_warning("Inappropriate bracing around statement",
214                               lineno)
215             if print_line:
216                 print(line)
217     if __errors or __warnings:
218         return -1
219     return 0
220
221
222 def usage():
223     print("Open vSwitch checkpatch.py")
224     print("Checks a patch for trivial mistakes.")
225     print("usage:")
226     print("%s [options] [patch file]" % sys.argv[0])
227     print("options:")
228     print("-h|--help\t\t\t\tThis help message")
229     print("-b|--skip-block-whitespace\t"
230           "Skips the if/while/for whitespace tests")
231     print("-l|--skip-leading-whitespace\t"
232           "Skips the leading whitespace test")
233     print("-s|--skip-signoff-lines\t"
234           "Do not emit an error if no Signed-off-by line is present")
235     print("-t|--skip-trailing-whitespace\t"
236           "Skips the trailing whitespace test")
237
238
239 def ovs_checkpatch_file(filename):
240     try:
241         mail = email.message_from_file(open(filename, 'r'))
242     except:
243         print_error("Unable to parse file '%s'. Is it a patch?" % filename)
244         return -1
245
246     for part in mail.walk():
247         if part.get_content_maintype() == 'multipart':
248             continue
249         return ovs_checkpatch_parse(part.get_payload(decode=True))
250
251 if __name__ == '__main__':
252     try:
253         optlist, args = getopt.getopt(sys.argv[1:], 'bhlst',
254                                       ["help",
255                                        "skip-block-whitespace",
256                                        "skip-leading-whitespace",
257                                        "skip-signoff-lines",
258                                        "skip-trailing-whitespace"])
259     except:
260         print("Unknown option encountered. Please rerun with -h for help.")
261         sys.exit(-1)
262
263     for o, a in optlist:
264         if o in ("-h", "--help"):
265             usage()
266             sys.exit(0)
267         elif o in ("-b", "--skip-block-whitespace"):
268             skip_block_whitespace_check = True
269         elif o in ("-l", "--skip-leading-whitespace"):
270             skip_leading_whitespace_check = True
271         elif o in ("-s", "--skip-signoff-lines"):
272             skip_signoff_check = True
273         elif o in ("-t", "--skip-trailing-whitespace"):
274             skip_trailing_whitespace_check = True
275         else:
276             print("Unknown option '%s'" % o)
277             sys.exit(-1)
278     try:
279         filename = args[0]
280     except:
281         if sys.stdin.isatty():
282             usage()
283             sys.exit(-1)
284         sys.exit(ovs_checkpatch_parse(sys.stdin.read()))
285     sys.exit(ovs_checkpatch_file(filename))