Move lib/ofp-msgs.h to include/openvswitch directory
[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_for_if_missing_whitespace = re.compile(r'(if|for|while)[\(]')
51 __regex_for_if_too_much_whitespace = re.compile(r'(if|for|while)  +[\(]')
52 __regex_for_if_parens_whitespace = re.compile(r'(if|for|while) \( +[\s\S]+\)')
53
54 skip_leading_whitespace_check = False
55 skip_trailing_whitespace_check = False
56 skip_block_whitespace_check = False
57 skip_signoff_check = False
58
59 # Don't enforce character limit on files that include these characters in their
60 # name, as they may have legitimate reasons to have longer lines.
61 #
62 # Python isn't checked as flake8 performs these checks during build.
63 line_length_blacklist = ['.am', '.at', 'etc', '.in', '.m4', '.mk', '.patch',
64                          '.py']
65
66
67 def is_added_line(line):
68     """Returns TRUE if the line in question is an added line.
69     """
70     return __regex_added_line.search(line) is not None
71
72
73 def leading_whitespace_is_spaces(line):
74     """Returns TRUE if the leading whitespace in added lines is spaces
75     """
76     if skip_leading_whitespace_check:
77         return True
78     if __regex_leading_with_whitespace_at_all.search(line) is not None:
79         return __regex_leading_with_spaces.search(line) is not None
80     return True
81
82
83 def trailing_whitespace_or_crlf(line):
84     """Returns TRUE if the trailing characters is whitespace
85     """
86     if skip_trailing_whitespace_check:
87         return False
88     return __regex_trailing_whitespace.search(line) is not None
89
90
91 def if_and_for_whitespace_checks(line):
92     """Return TRUE if there is appropriate whitespace after if, for, while
93     """
94     if skip_block_whitespace_check:
95         return True
96     if (__regex_for_if_missing_whitespace.search(line) is not None or
97             __regex_for_if_too_much_whitespace.search(line) is not None or
98             __regex_for_if_parens_whitespace.search(line)):
99         return False
100     return True
101
102
103 def ovs_checkpatch_parse(text):
104     lineno = 0
105     signatures = []
106     co_authors = []
107     parse = 0
108     current_file = ''
109     previous_file = ''
110     scissors = re.compile(r'^[\w]*---[\w]*')
111     hunks = re.compile('^(---|\+\+\+) (\S+)')
112     is_signature = re.compile(r'((\s*Signed-off-by: )(.*))$',
113                               re.I | re.M | re.S)
114     is_co_author = re.compile(r'(\s*(Co-authored-by: )(.*))$',
115                               re.I | re.M | re.S)
116     skip_line_length_check = False
117
118     for line in text.split('\n'):
119         if current_file != previous_file:
120             previous_file = current_file
121             if any([fmt in current_file for fmt in line_length_blacklist]):
122                 skip_line_length_check = True
123             else:
124                 skip_line_length_check = False
125
126         lineno = lineno + 1
127         if len(line) <= 0:
128             continue
129
130         if parse == 1:
131             match = hunks.match(line)
132             if match:
133                 parse = parse + 1
134                 current_file = match.group(2)
135             continue
136         elif parse == 0:
137             if scissors.match(line):
138                 parse = parse + 1
139                 if not skip_signoff_check:
140                     if len(signatures) == 0:
141                         print_error("No signatures found.")
142                     if len(signatures) != 1 + len(co_authors):
143                         print_error("Too many signoffs; "
144                                     "are you missing Co-authored-by lines?")
145                     if not set(co_authors) <= set(signatures):
146                         print_error("Co-authored-by/Signed-off-by corruption")
147             elif is_signature.match(line):
148                 m = is_signature.match(line)
149                 signatures.append(m.group(3))
150             elif is_co_author.match(line):
151                 m = is_co_author.match(line)
152                 co_authors.append(m.group(3))
153         elif parse == 2:
154             print_line = False
155             newfile = hunks.match(line)
156             if newfile:
157                 current_file = newfile.group(2)
158                 continue
159             if not is_added_line(line):
160                 continue
161             # Skip files which have /datapath in them, since they are
162             # linux or windows coding standards
163             if '/datapath' in current_file:
164                 continue
165             if (not current_file.endswith('.mk') and
166                     not leading_whitespace_is_spaces(line[1:])):
167                 print_line = True
168                 print_warning("Line has non-spaces leading whitespace",
169                               lineno)
170             if trailing_whitespace_or_crlf(line[1:]):
171                 print_line = True
172                 print_warning("Line has trailing whitespace", lineno)
173             if len(line[1:]) > 79 and not skip_line_length_check:
174                 print_line = True
175                 print_warning("Line is greater than 79-characters long",
176                               lineno)
177             if not if_and_for_whitespace_checks(line[1:]):
178                 print_line = True
179                 print_warning("Improper whitespace around control block",
180                               lineno)
181             if print_line:
182                 print(line)
183     if __errors or __warnings:
184         return -1
185     return 0
186
187
188 def usage():
189     print("Open vSwitch checkpatch.py")
190     print("Checks a patch for trivial mistakes.")
191     print("usage:")
192     print("%s [options] [patch file]" % sys.argv[0])
193     print("options:")
194     print("-h|--help\t\t\t\tThis help message")
195     print("-b|--skip-block-whitespace\t"
196           "Skips the if/while/for whitespace tests")
197     print("-l|--skip-leading-whitespace\t"
198           "Skips the leading whitespace test")
199     print("-s|--skip-signoff-lines\t"
200           "Do not emit an error if no Signed-off-by line is present")
201     print("-t|--skip-trailing-whitespace\t"
202           "Skips the trailing whitespace test")
203
204
205 def ovs_checkpatch_file(filename):
206     try:
207         mail = email.message_from_file(open(filename, 'r'))
208     except:
209         print_error("Unable to parse file '%s'. Is it a patch?" % filename)
210         return -1
211
212     for part in mail.walk():
213         if part.get_content_maintype() == 'multipart':
214             continue
215         return ovs_checkpatch_parse(part.get_payload(decode=True))
216
217 if __name__ == '__main__':
218     try:
219         optlist, args = getopt.getopt(sys.argv[1:], 'bhlst',
220                                       ["help",
221                                        "skip-block-whitespace",
222                                        "skip-leading-whitespace",
223                                        "skip-signoff-lines",
224                                        "skip-trailing-whitespace"])
225     except:
226         print("Unknown option encountered. Please rerun with -h for help.")
227         sys.exit(-1)
228
229     for o, a in optlist:
230         if o in ("-h", "--help"):
231             usage()
232             sys.exit(0)
233         elif o in ("-b", "--skip-block-whitespace"):
234             skip_block_whitespace_check = True
235         elif o in ("-l", "--skip-leading-whitespace"):
236             skip_leading_whitespace_check = True
237         elif o in ("-s", "--skip-signoff-lines"):
238             skip_signoff_check = True
239         elif o in ("-t", "--skip-trailing-whitespace"):
240             skip_trailing_whitespace_check = True
241         else:
242             print("Unknown option '%s'" % o)
243             sys.exit(-1)
244     try:
245         filename = args[0]
246     except:
247         if sys.stdin.isatty():
248             usage()
249             sys.exit(-1)
250         sys.exit(ovs_checkpatch_parse(sys.stdin.read()))
251     sys.exit(ovs_checkpatch_file(filename))