OLD | NEW |
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Generic presubmit checks that can be reused by other presubmit checks.""" | 5 """Generic presubmit checks that can be reused by other presubmit checks.""" |
6 | 6 |
7 | 7 |
8 ### Description checks | 8 ### Description checks |
9 | 9 |
10 def CheckChangeHasTestField(input_api, output_api): | 10 def CheckChangeHasTestField(input_api, output_api): |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 if input_api.is_committing: | 60 if input_api.is_committing: |
61 return [output_api.PresubmitError('Add a description.')] | 61 return [output_api.PresubmitError('Add a description.')] |
62 else: | 62 else: |
63 return [output_api.PresubmitNotifyResult('Add a description.')] | 63 return [output_api.PresubmitNotifyResult('Add a description.')] |
64 return [] | 64 return [] |
65 | 65 |
66 ### Content checks | 66 ### Content checks |
67 | 67 |
68 def CheckDoNotSubmitInFiles(input_api, output_api): | 68 def CheckDoNotSubmitInFiles(input_api, output_api): |
69 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to any files.""" | 69 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to any files.""" |
70 # We want to check every text file, not just source files. | |
71 file_filter = lambda x : x | |
72 keyword = 'DO NOT ' + 'SUBMIT' | 70 keyword = 'DO NOT ' + 'SUBMIT' |
73 errors = _FindNewViolationsOfRule(lambda line : keyword not in line, | 71 # We want to check every text files, not just source files. |
74 input_api, file_filter) | 72 for f, line_num, line in input_api.RightHandSideLines(lambda x: x): |
75 text = '\n'.join('Found %s in %s' % (keyword, loc) for loc in errors) | 73 if keyword in line: |
76 if text: | 74 text = 'Found ' + keyword + ' in %s, line %s' % (f.LocalPath(), line_num) |
77 return [output_api.PresubmitError(text)] | 75 return [output_api.PresubmitError(text)] |
78 return [] | 76 return [] |
79 | 77 |
80 | 78 |
81 def CheckChangeLintsClean(input_api, output_api, source_file_filter=None): | 79 def CheckChangeLintsClean(input_api, output_api, source_file_filter=None): |
82 """Checks that all '.cc' and '.h' files pass cpplint.py.""" | 80 """Checks that all '.cc' and '.h' files pass cpplint.py.""" |
83 _RE_IS_TEST = input_api.re.compile(r'.*tests?.(cc|h)$') | 81 _RE_IS_TEST = input_api.re.compile(r'.*tests?.(cc|h)$') |
84 result = [] | 82 result = [] |
85 | 83 |
86 # Initialize cpplint. | 84 # Initialize cpplint. |
87 import cpplint | 85 import cpplint |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 if cr_files: | 206 if cr_files: |
209 outputs.append(output_api.PresubmitPromptWarning( | 207 outputs.append(output_api.PresubmitPromptWarning( |
210 'Found a CR character in these files:', items=cr_files)) | 208 'Found a CR character in these files:', items=cr_files)) |
211 if eof_files: | 209 if eof_files: |
212 outputs.append(output_api.PresubmitPromptWarning( | 210 outputs.append(output_api.PresubmitPromptWarning( |
213 'These files should end in one (and only one) newline character:', | 211 'These files should end in one (and only one) newline character:', |
214 items=eof_files)) | 212 items=eof_files)) |
215 return outputs | 213 return outputs |
216 | 214 |
217 | 215 |
218 def _ReportErrorFileAndLine(filename, line_num, line): | |
219 """Default error formatter for _FindNewViolationsOfRule.""" | |
220 return '%s, line %s' % (filename, line_num) | |
221 | |
222 | |
223 def _FindNewViolationsOfRule(callable_rule, input_api, source_file_filter=None, | |
224 error_formatter=_ReportErrorFileAndLine): | |
225 """Find all newly introduced violations of a per-line rule (a callable). | |
226 | |
227 Arguments: | |
228 callable_rule: a callable taking a line of input and returning True | |
229 if the rule is satisfied and False if there was a problem. | |
230 input_api: object to enumerate the affected files. | |
231 source_file_filter: a filter to be passed to the input api. | |
232 error_formatter: a callable taking (filename, line_number, line) and | |
233 returning a formatted error string. | |
234 | |
235 Returns: | |
236 A list of the newly-introduced violations reported by the rule. | |
237 """ | |
238 errors = [] | |
239 for f in input_api.AffectedFiles(source_file_filter): | |
240 # For speed, we do two passes, checking first the full file. Shelling out | |
241 # to the SCM to determine the changed region can be quite expensive on | |
242 # Win32. Assuming that most files will be kept problem-free, we can | |
243 # skip the SCM operations most of the time. | |
244 for line in f.NewContents(): | |
245 if not callable_rule(line): | |
246 # Violation found in full text; re-run on just the changed contents. | |
247 for line_num, line in f.ChangedContents(): | |
248 if not callable_rule(line): | |
249 errors.append(error_formatter(f.LocalPath(), line_num, line)) | |
250 return errors | |
251 | |
252 | |
253 def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None): | 216 def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None): |
254 """Checks that there are no tab characters in any of the text files to be | 217 """Checks that there are no tab characters in any of the text files to be |
255 submitted. | 218 submitted. |
256 """ | 219 """ |
257 # In addition to the filter, make sure that makefiles are blacklisted. | 220 # In addition to the filter, make sure that makefiles are blacklisted. |
258 if not source_file_filter: | 221 if not source_file_filter: |
259 # It's the default filter. | 222 # It's the default filter. |
260 source_file_filter = input_api.FilterSourceFile | 223 source_file_filter = input_api.FilterSourceFile |
261 def filter_more(affected_file): | 224 def filter_more(affected_file): |
262 return (not input_api.os_path.basename(affected_file.LocalPath()) in | 225 return (not input_api.os_path.basename(affected_file.LocalPath()) in |
263 ('Makefile', 'makefile') and | 226 ('Makefile', 'makefile') and |
264 source_file_filter(affected_file)) | 227 source_file_filter(affected_file)) |
265 | 228 tabs = [] |
266 tabs = _FindNewViolationsOfRule(lambda line : '\t' not in line, | 229 for f, line_num, line in input_api.RightHandSideLines(filter_more): |
267 input_api, filter_more) | 230 if '\t' in line: |
268 | 231 tabs.append('%s, line %s' % (f.LocalPath(), line_num)) |
269 if tabs: | 232 if tabs: |
270 return [output_api.PresubmitPromptWarning('Found a tab character in:', | 233 return [output_api.PresubmitPromptWarning('Found a tab character in:', |
271 long_text='\n'.join(tabs))] | 234 long_text='\n'.join(tabs))] |
272 return [] | 235 return [] |
273 | 236 |
274 | 237 |
275 def CheckChangeTodoHasOwner(input_api, output_api, source_file_filter=None): | 238 def CheckChangeTodoHasOwner(input_api, output_api, source_file_filter=None): |
276 """Checks that the user didn't add TODO(name) without an owner.""" | 239 """Checks that the user didn't add TODO(name) without an owner.""" |
277 | 240 |
278 unowned_todo = input_api.re.compile('TO' + 'DO[^(]') | 241 unowned_todo = input_api.re.compile('TO' + 'DO[^(]') |
279 errors = _FindNewViolationsOfRule(lambda x : not unowned_todo.search(x), | 242 for f, line_num, line in input_api.RightHandSideLines(source_file_filter): |
280 input_api, source_file_filter) | 243 if unowned_todo.search(line): |
281 errors = ['Found TO' + 'DO with no owner in ' + x for x in errors] | 244 text = ('Found TO' + 'DO with no owner in %s, line %s' % |
282 if errors: | 245 (f.LocalPath(), line_num)) |
283 return [output_api.PresubmitPromptWarning('\n'.join(errors))] | 246 return [output_api.PresubmitPromptWarning(text)] |
284 return [] | 247 return [] |
285 | 248 |
286 | 249 |
287 def CheckChangeHasNoStrayWhitespace(input_api, output_api, | 250 def CheckChangeHasNoStrayWhitespace(input_api, output_api, |
288 source_file_filter=None): | 251 source_file_filter=None): |
289 """Checks that there is no stray whitespace at source lines end.""" | 252 """Checks that there is no stray whitespace at source lines end.""" |
290 errors = _FindNewViolationsOfRule(lambda line : line.rstrip() == line, | 253 errors = [] |
291 input_api, source_file_filter) | 254 for f, line_num, line in input_api.RightHandSideLines(source_file_filter): |
| 255 if line.rstrip() != line: |
| 256 errors.append('%s, line %s' % (f.LocalPath(), line_num)) |
292 if errors: | 257 if errors: |
293 return [output_api.PresubmitPromptWarning( | 258 return [output_api.PresubmitPromptWarning( |
294 'Found line ending with white spaces in:', | 259 'Found line ending with white spaces in:', |
295 long_text='\n'.join(errors))] | 260 long_text='\n'.join(errors))] |
296 return [] | 261 return [] |
297 | 262 |
298 | 263 |
299 def CheckLongLines(input_api, output_api, maxlen=80, source_file_filter=None): | 264 def CheckLongLines(input_api, output_api, maxlen=80, source_file_filter=None): |
300 """Checks that there aren't any lines longer than maxlen characters in any of | 265 """Checks that there aren't any lines longer than maxlen characters in any of |
301 the text files to be submitted. | 266 the text files to be submitted. |
302 """ | 267 """ |
303 def no_long_lines(line): | 268 bad = [] |
| 269 for f, line_num, line in input_api.RightHandSideLines(source_file_filter): |
304 # Allow lines with http://, https:// and #define/#pragma/#include/#if/#endif | 270 # Allow lines with http://, https:// and #define/#pragma/#include/#if/#endif |
305 # to exceed the maxlen rule. | 271 # to exceed the maxlen rule. |
306 return (len(line) <= maxlen or | 272 if (len(line) > maxlen and |
307 any((url in line) for url in ('http://', 'https://')) or | 273 not 'http://' in line and |
308 line.startswith(('#define', '#include', '#import', '#pragma', | 274 not 'https://' in line and |
309 '#if', '#endif'))) | 275 not line.startswith('#define') and |
| 276 not line.startswith('#include') and |
| 277 not line.startswith('#import') and |
| 278 not line.startswith('#pragma') and |
| 279 not line.startswith('#if') and |
| 280 not line.startswith('#endif')): |
| 281 bad.append( |
| 282 '%s, line %s, %s chars' % |
| 283 (f.LocalPath(), line_num, len(line))) |
| 284 if len(bad) == 5: # Just show the first 5 errors. |
| 285 break |
310 | 286 |
311 def format_error(filename, line_num, line): | 287 if bad: |
312 return '%s, line %s, %s chars' % (filename, line_num, len(line)) | |
313 | |
314 errors = _FindNewViolationsOfRule(no_long_lines, input_api, | |
315 source_file_filter, | |
316 error_formatter=format_error) | |
317 if errors: | |
318 msg = 'Found lines longer than %s characters (first 5 shown).' % maxlen | 288 msg = 'Found lines longer than %s characters (first 5 shown).' % maxlen |
319 return [output_api.PresubmitPromptWarning(msg, items=errors[:5])] | 289 return [output_api.PresubmitPromptWarning(msg, items=bad)] |
320 else: | 290 else: |
321 return [] | 291 return [] |
322 | 292 |
323 | 293 |
324 def CheckLicense(input_api, output_api, license_re, source_file_filter=None, | 294 def CheckLicense(input_api, output_api, license_re, source_file_filter=None, |
325 accept_empty_files=True): | 295 accept_empty_files=True): |
326 """Verifies the license header. | 296 """Verifies the license header. |
327 """ | 297 """ |
328 license_re = input_api.re.compile(license_re, input_api.re.MULTILINE) | 298 license_re = input_api.re.compile(license_re, input_api.re.MULTILINE) |
329 bad_files = [] | 299 bad_files = [] |
(...skipping 573 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
903 # This code loads the default black list (e.g. third_party, experimental, etc) | 873 # This code loads the default black list (e.g. third_party, experimental, etc) |
904 # and add our black list (breakpad, skia and v8 are still not following | 874 # and add our black list (breakpad, skia and v8 are still not following |
905 # google style and are not really living this repository). | 875 # google style and are not really living this repository). |
906 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage. | 876 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage. |
907 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths | 877 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths |
908 white_list = input_api.DEFAULT_WHITE_LIST + text_files | 878 white_list = input_api.DEFAULT_WHITE_LIST + text_files |
909 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list) | 879 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list) |
910 text_files = lambda x: input_api.FilterSourceFile(x, black_list=black_list, | 880 text_files = lambda x: input_api.FilterSourceFile(x, black_list=black_list, |
911 white_list=white_list) | 881 white_list=white_list) |
912 | 882 |
913 | |
914 snapshot_memory = [] | |
915 def snapshot(msg): | |
916 """Measures & prints performance warning if a rule is running slow.""" | |
917 dt2 = input_api.time.clock() | |
918 if snapshot_memory: | |
919 delta_ms = int(1000*(dt2 - snapshot_memory[0])) | |
920 if delta_ms > 500: | |
921 print " %s took a long time: %dms" % (snapshot_memory[1], delta_ms) | |
922 snapshot_memory[:] = (dt2, msg) | |
923 | |
924 if owners_check: | 883 if owners_check: |
925 snapshot("checking owners") | |
926 results.extend(input_api.canned_checks.CheckOwners( | 884 results.extend(input_api.canned_checks.CheckOwners( |
927 input_api, output_api, source_file_filter=sources)) | 885 input_api, output_api, source_file_filter=sources)) |
928 | 886 |
929 snapshot("checking long lines") | |
930 results.extend(input_api.canned_checks.CheckLongLines( | 887 results.extend(input_api.canned_checks.CheckLongLines( |
931 input_api, output_api, source_file_filter=sources)) | 888 input_api, output_api, source_file_filter=sources)) |
932 snapshot( "checking tabs") | |
933 results.extend(input_api.canned_checks.CheckChangeHasNoTabs( | 889 results.extend(input_api.canned_checks.CheckChangeHasNoTabs( |
934 input_api, output_api, source_file_filter=sources)) | 890 input_api, output_api, source_file_filter=sources)) |
935 snapshot( "checking stray whitespace") | |
936 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace( | 891 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace( |
937 input_api, output_api, source_file_filter=sources)) | 892 input_api, output_api, source_file_filter=sources)) |
938 snapshot("checking eol style") | |
939 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle( | 893 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle( |
940 input_api, output_api, source_file_filter=text_files)) | 894 input_api, output_api, source_file_filter=text_files)) |
941 snapshot("checking svn mime types") | |
942 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes( | 895 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes( |
943 input_api, output_api)) | 896 input_api, output_api)) |
944 snapshot("checking license") | |
945 results.extend(input_api.canned_checks.CheckLicense( | 897 results.extend(input_api.canned_checks.CheckLicense( |
946 input_api, output_api, license_header, source_file_filter=sources)) | 898 input_api, output_api, license_header, source_file_filter=sources)) |
947 snapshot("checking nsobjects") | |
948 results.extend(_CheckConstNSObject( | 899 results.extend(_CheckConstNSObject( |
949 input_api, output_api, source_file_filter=sources)) | 900 input_api, output_api, source_file_filter=sources)) |
950 snapshot("checking singletons") | |
951 results.extend(_CheckSingletonInHeaders( | 901 results.extend(_CheckSingletonInHeaders( |
952 input_api, output_api, source_file_filter=sources)) | 902 input_api, output_api, source_file_filter=sources)) |
953 snapshot("done") | |
954 return results | 903 return results |
OLD | NEW |