blob: 0b79fc00be6206935f78b3c89aba2600762481e3 [file] [log] [blame]
dilmah@chromium.orgd22b9702011-08-26 13:53:49 +00001# Copyright (c) 2011 The Chromium Authors. All rights reserved.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +00002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Generic presubmit checks that can be reused by other presubmit checks."""
6
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00007
maruel@chromium.org3410d912009-06-09 20:56:16 +00008### Description checks
9
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000010def CheckChangeHasTestField(input_api, output_api):
11 """Requires that the changelist have a TEST= field."""
maruel@chromium.orge1a524f2009-05-27 14:43:46 +000012 if input_api.change.TEST:
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000013 return []
14 else:
15 return [output_api.PresubmitNotifyResult(
jam@chromium.org5c76de92011-01-24 18:19:21 +000016 'If this change requires manual test instructions to QA team, add '
17 'TEST=[instructions].')]
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000018
19
20def CheckChangeHasBugField(input_api, output_api):
21 """Requires that the changelist have a BUG= field."""
maruel@chromium.orge1a524f2009-05-27 14:43:46 +000022 if input_api.change.BUG:
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000023 return []
24 else:
25 return [output_api.PresubmitNotifyResult(
jam@chromium.org5c76de92011-01-24 18:19:21 +000026 'If this change has an associated bug, add BUG=[bug number].')]
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000027
28
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000029def CheckChangeHasTestedField(input_api, output_api):
30 """Requires that the changelist have a TESTED= field."""
maruel@chromium.orge1a524f2009-05-27 14:43:46 +000031 if input_api.change.TESTED:
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000032 return []
33 else:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000034 return [output_api.PresubmitError('Changelist must have a TESTED= field.')]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000035
36
37def CheckChangeHasQaField(input_api, output_api):
38 """Requires that the changelist have a QA= field."""
39 if input_api.change.QA:
40 return []
41 else:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000042 return [output_api.PresubmitError('Changelist must have a QA= field.')]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000043
44
45def CheckDoNotSubmitInDescription(input_api, output_api):
46 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to the CL description.
47 """
48 keyword = 'DO NOT ' + 'SUBMIT'
49 if keyword in input_api.change.DescriptionText():
50 return [output_api.PresubmitError(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000051 keyword + ' is present in the changelist description.')]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000052 else:
53 return []
54
55
maruel@chromium.orgbc50eb42009-06-10 18:22:47 +000056def CheckChangeHasDescription(input_api, output_api):
57 """Checks the CL description is not empty."""
58 text = input_api.change.DescriptionText()
59 if text.strip() == '':
60 if input_api.is_committing:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000061 return [output_api.PresubmitError('Add a description.')]
maruel@chromium.orgbc50eb42009-06-10 18:22:47 +000062 else:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000063 return [output_api.PresubmitNotifyResult('Add a description.')]
maruel@chromium.orgbc50eb42009-06-10 18:22:47 +000064 return []
65
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +000066
67def CheckChangeWasUploaded(input_api, output_api):
68 """Checks that the issue was uploaded before committing."""
maruel@chromium.orgd587f392011-07-26 00:41:18 +000069 if input_api.is_committing and not input_api.change.issue:
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +000070 return [output_api.PresubmitError(
71 'Issue wasn\'t uploaded. Please upload first.')]
72 return []
73
74
maruel@chromium.org3410d912009-06-09 20:56:16 +000075### Content checks
76
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000077def CheckDoNotSubmitInFiles(input_api, output_api):
78 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to any files."""
nick@chromium.orge06cb4e2011-04-23 01:20:38 +000079 # We want to check every text file, not just source files.
80 file_filter = lambda x : x
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000081 keyword = 'DO NOT ' + 'SUBMIT'
nick@chromium.orge06cb4e2011-04-23 01:20:38 +000082 errors = _FindNewViolationsOfRule(lambda line : keyword not in line,
83 input_api, file_filter)
84 text = '\n'.join('Found %s in %s' % (keyword, loc) for loc in errors)
85 if text:
86 return [output_api.PresubmitError(text)]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000087 return []
88
89
erg@google.com26970fa2009-11-17 18:07:32 +000090def CheckChangeLintsClean(input_api, output_api, source_file_filter=None):
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000091 """Checks that all '.cc' and '.h' files pass cpplint.py."""
erg@google.com26970fa2009-11-17 18:07:32 +000092 _RE_IS_TEST = input_api.re.compile(r'.*tests?.(cc|h)$')
93 result = []
94
95 # Initialize cpplint.
96 import cpplint
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +000097 # Access to a protected member _XX of a client class
98 # pylint: disable=W0212
erg@google.com26970fa2009-11-17 18:07:32 +000099 cpplint._cpplint_state.ResetErrorCounts()
100
101 # Justifications for each filter:
102 #
103 # - build/include : Too many; fix in the future.
104 # - build/include_order : Not happening; #ifdefed includes.
105 # - build/namespace : I'm surprised by how often we violate this rule.
106 # - readability/casting : Mistakes a whole bunch of function pointer.
107 # - runtime/int : Can be fixed long term; volume of errors too high
108 # - runtime/virtual : Broken now, but can be fixed in the future?
109 # - whitespace/braces : We have a lot of explicit scoping in chrome code.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000110 cpplint._SetFilters('-build/include,-build/include_order,-build/namespace,'
111 '-readability/casting,-runtime/int,-runtime/virtual,'
112 '-whitespace/braces')
erg@google.com26970fa2009-11-17 18:07:32 +0000113
114 # We currently are more strict with normal code than unit tests; 4 and 5 are
115 # the verbosity level that would normally be passed to cpplint.py through
116 # --verbose=#. Hopefully, in the future, we can be more verbose.
117 files = [f.AbsoluteLocalPath() for f in
118 input_api.AffectedSourceFiles(source_file_filter)]
119 for file_name in files:
120 if _RE_IS_TEST.match(file_name):
121 level = 5
122 else:
123 level = 4
124
125 cpplint.ProcessFile(file_name, level)
126
127 if cpplint._cpplint_state.error_count > 0:
128 if input_api.is_committing:
129 res_type = output_api.PresubmitError
130 else:
131 res_type = output_api.PresubmitPromptWarning
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000132 result = [res_type('Changelist failed cpplint.py check.')]
erg@google.com26970fa2009-11-17 18:07:32 +0000133
134 return result
135
136
maruel@chromium.org3410d912009-06-09 20:56:16 +0000137def CheckChangeHasNoCR(input_api, output_api, source_file_filter=None):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000138 """Checks no '\r' (CR) character is in any source files."""
139 cr_files = []
maruel@chromium.org3410d912009-06-09 20:56:16 +0000140 for f in input_api.AffectedSourceFiles(source_file_filter):
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000141 if '\r' in input_api.ReadFile(f, 'rb'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000142 cr_files.append(f.LocalPath())
143 if cr_files:
144 return [output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000145 'Found a CR character in these files:', items=cr_files)]
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000146 return []
147
148
thestig@chromium.orgda8cddd2009-08-13 00:25:55 +0000149def CheckSvnModifiedDirectories(input_api, output_api, source_file_filter=None):
150 """Checks for files in svn modified directories.
151
152 They will get submitted on accident because svn commits recursively by
153 default, and that's very dangerous.
154 """
155 if input_api.change.scm != 'svn':
156 return []
157
158 errors = []
159 current_cl_files = input_api.change.GetModifiedFiles()
160 all_modified_files = input_api.change.GetAllModifiedFiles()
161 # Filter out files in the current CL.
162 modified_files = [f for f in all_modified_files if f not in current_cl_files]
163 modified_abspaths = [input_api.os_path.abspath(f) for f in modified_files]
164
sail@chromium.org5538e022011-05-12 17:53:16 +0000165 for f in input_api.AffectedFiles(file_filter=source_file_filter):
thestig@chromium.orgda8cddd2009-08-13 00:25:55 +0000166 if f.Action() == 'M' and f.IsDirectory():
167 curpath = f.AbsoluteLocalPath()
168 bad_files = []
169 # Check if any of the modified files in other CLs are under curpath.
170 for i in xrange(len(modified_files)):
171 abspath = modified_abspaths[i]
172 if input_api.os_path.commonprefix([curpath, abspath]) == curpath:
173 bad_files.append(modified_files[i])
174 if bad_files:
175 if input_api.is_committing:
176 error_type = output_api.PresubmitPromptWarning
177 else:
178 error_type = output_api.PresubmitNotifyResult
179 errors.append(error_type(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000180 'Potential accidental commits in changelist %s:' % f.LocalPath(),
thestig@chromium.orgda8cddd2009-08-13 00:25:55 +0000181 items=bad_files))
182 return errors
183
184
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000185def CheckChangeHasOnlyOneEol(input_api, output_api, source_file_filter=None):
186 """Checks the files ends with one and only one \n (LF)."""
187 eof_files = []
188 for f in input_api.AffectedSourceFiles(source_file_filter):
189 contents = input_api.ReadFile(f, 'rb')
190 # Check that the file ends in one and only one newline character.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000191 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000192 eof_files.append(f.LocalPath())
193
194 if eof_files:
195 return [output_api.PresubmitPromptWarning(
196 'These files should end in one (and only one) newline character:',
197 items=eof_files)]
198 return []
199
200
201def CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api,
202 source_file_filter=None):
203 """Runs both CheckChangeHasNoCR and CheckChangeHasOnlyOneEOL in one pass.
204
205 It is faster because it is reading the file only once.
206 """
207 cr_files = []
208 eof_files = []
209 for f in input_api.AffectedSourceFiles(source_file_filter):
210 contents = input_api.ReadFile(f, 'rb')
211 if '\r' in contents:
212 cr_files.append(f.LocalPath())
213 # Check that the file ends in one and only one newline character.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000214 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000215 eof_files.append(f.LocalPath())
216 outputs = []
217 if cr_files:
218 outputs.append(output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000219 'Found a CR character in these files:', items=cr_files))
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000220 if eof_files:
221 outputs.append(output_api.PresubmitPromptWarning(
222 'These files should end in one (and only one) newline character:',
223 items=eof_files))
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000224 return outputs
225
226
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000227def _ReportErrorFileAndLine(filename, line_num, line):
228 """Default error formatter for _FindNewViolationsOfRule."""
229 return '%s, line %s' % (filename, line_num)
230
231
232def _FindNewViolationsOfRule(callable_rule, input_api, source_file_filter=None,
233 error_formatter=_ReportErrorFileAndLine):
234 """Find all newly introduced violations of a per-line rule (a callable).
235
236 Arguments:
237 callable_rule: a callable taking a line of input and returning True
238 if the rule is satisfied and False if there was a problem.
239 input_api: object to enumerate the affected files.
240 source_file_filter: a filter to be passed to the input api.
241 error_formatter: a callable taking (filename, line_number, line) and
242 returning a formatted error string.
243
244 Returns:
245 A list of the newly-introduced violations reported by the rule.
246 """
247 errors = []
sail@chromium.org5538e022011-05-12 17:53:16 +0000248 for f in input_api.AffectedFiles(include_deletes=False,
249 file_filter=source_file_filter):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000250 # For speed, we do two passes, checking first the full file. Shelling out
251 # to the SCM to determine the changed region can be quite expensive on
252 # Win32. Assuming that most files will be kept problem-free, we can
253 # skip the SCM operations most of the time.
254 if all(callable_rule(line) for line in f.NewContents()):
255 continue # No violation found in full text: can skip considering diff.
256
257 for line_num, line in f.ChangedContents():
258 if not callable_rule(line):
259 errors.append(error_formatter(f.LocalPath(), line_num, line))
260
261 return errors
262
263
maruel@chromium.org3410d912009-06-09 20:56:16 +0000264def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000265 """Checks that there are no tab characters in any of the text files to be
266 submitted.
267 """
maruel@chromium.org115ae6c2010-06-18 17:11:43 +0000268 # In addition to the filter, make sure that makefiles are blacklisted.
269 if not source_file_filter:
270 # It's the default filter.
271 source_file_filter = input_api.FilterSourceFile
272 def filter_more(affected_file):
273 return (not input_api.os_path.basename(affected_file.LocalPath()) in
274 ('Makefile', 'makefile') and
275 source_file_filter(affected_file))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000276
277 tabs = _FindNewViolationsOfRule(lambda line : '\t' not in line,
278 input_api, filter_more)
279
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000280 if tabs:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000281 return [output_api.PresubmitPromptWarning('Found a tab character in:',
282 long_text='\n'.join(tabs))]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000283 return []
284
285
estade@chromium.orgfdcc9f72011-02-07 22:25:07 +0000286def CheckChangeTodoHasOwner(input_api, output_api, source_file_filter=None):
287 """Checks that the user didn't add TODO(name) without an owner."""
288
maruel@chromium.org07ab60e2011-02-08 21:54:00 +0000289 unowned_todo = input_api.re.compile('TO' + 'DO[^(]')
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000290 errors = _FindNewViolationsOfRule(lambda x : not unowned_todo.search(x),
291 input_api, source_file_filter)
292 errors = ['Found TO' + 'DO with no owner in ' + x for x in errors]
293 if errors:
294 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
estade@chromium.orgfdcc9f72011-02-07 22:25:07 +0000295 return []
296
297
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000298def CheckChangeHasNoStrayWhitespace(input_api, output_api,
299 source_file_filter=None):
300 """Checks that there is no stray whitespace at source lines end."""
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000301 errors = _FindNewViolationsOfRule(lambda line : line.rstrip() == line,
302 input_api, source_file_filter)
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000303 if errors:
304 return [output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000305 'Found line ending with white spaces in:',
306 long_text='\n'.join(errors))]
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000307 return []
308
309
maruel@chromium.org3410d912009-06-09 20:56:16 +0000310def CheckLongLines(input_api, output_api, maxlen=80, source_file_filter=None):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000311 """Checks that there aren't any lines longer than maxlen characters in any of
312 the text files to be submitted.
313 """
maruel@chromium.orgeba64622011-06-20 18:26:48 +0000314 # Stupidly long symbols that needs to be worked around if takes 66% of line.
315 long_symbol = maxlen * 2 / 3
316 # Hard line length limit at 50% more.
317 extra_maxlen = maxlen * 3 / 2
318 # Note: these are C++ specific but processed on all languages. :(
319 MACROS = ('#define', '#include', '#import', '#pragma', '#if', '#endif')
320
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000321 def no_long_lines(line):
maruel@chromium.orgeba64622011-06-20 18:26:48 +0000322 if len(line) <= maxlen:
323 return True
324
325 if len(line) > extra_maxlen:
326 return False
327
328 return (
329 line.startswith(MACROS) or
330 any((url in line) for url in ('http://', 'https://')) or
331 input_api.re.match(
332 r'.*[A-Za-z][A-Za-z_0-9]{%d,}.*' % long_symbol, line))
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000333
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000334 def format_error(filename, line_num, line):
335 return '%s, line %s, %s chars' % (filename, line_num, len(line))
336
337 errors = _FindNewViolationsOfRule(no_long_lines, input_api,
338 source_file_filter,
339 error_formatter=format_error)
340 if errors:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000341 msg = 'Found lines longer than %s characters (first 5 shown).' % maxlen
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000342 return [output_api.PresubmitPromptWarning(msg, items=errors[:5])]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000343 else:
344 return []
345
346
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000347def CheckLicense(input_api, output_api, license_re, source_file_filter=None,
maruel@chromium.org71626852010-11-03 13:14:25 +0000348 accept_empty_files=True):
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000349 """Verifies the license header.
350 """
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000351 license_re = input_api.re.compile(license_re, input_api.re.MULTILINE)
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000352 bad_files = []
353 for f in input_api.AffectedSourceFiles(source_file_filter):
354 contents = input_api.ReadFile(f, 'rb')
maruel@chromium.org71626852010-11-03 13:14:25 +0000355 if accept_empty_files and not contents:
356 continue
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000357 if not license_re.search(contents):
358 bad_files.append(f.LocalPath())
359 if bad_files:
360 if input_api.is_committing:
361 res_type = output_api.PresubmitPromptWarning
362 else:
363 res_type = output_api.PresubmitNotifyResult
364 return [res_type(
bradnelson@google.com393460b2011-03-29 01:20:26 +0000365 'License must match:\n%s\n' % license_re.pattern +
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000366 'Found a bad license header in these files:', items=bad_files)]
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000367 return []
368
369
maruel@chromium.org1a0e3cb2009-06-10 18:03:04 +0000370def CheckChangeSvnEolStyle(input_api, output_api, source_file_filter=None):
maruel@chromium.orgb7d46902009-06-10 14:12:10 +0000371 """Checks that the source files have svn:eol-style=LF."""
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000372 return CheckSvnProperty(input_api, output_api,
373 'svn:eol-style', 'LF',
374 input_api.AffectedSourceFiles(source_file_filter))
375
376
377def CheckSvnForCommonMimeTypes(input_api, output_api):
378 """Checks that common binary file types have the correct svn:mime-type."""
379 output = []
380 files = input_api.AffectedFiles(include_deletes=False)
maruel@chromium.orge49187c2009-06-26 22:44:53 +0000381 def IsExts(x, exts):
382 path = x.LocalPath()
383 for extension in exts:
384 if path.endswith(extension):
385 return True
386 return False
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000387 def FilterFiles(extension):
maruel@chromium.orge49187c2009-06-26 22:44:53 +0000388 return filter(lambda x: IsExts(x, extension), files)
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000389 def RunCheck(mime_type, files):
390 output.extend(CheckSvnProperty(input_api, output_api, 'svn:mime-type',
391 mime_type, files))
maruel@chromium.orge49187c2009-06-26 22:44:53 +0000392 RunCheck('application/pdf', FilterFiles(['.pdf']))
393 RunCheck('image/bmp', FilterFiles(['.bmp']))
394 RunCheck('image/gif', FilterFiles(['.gif']))
395 RunCheck('image/png', FilterFiles(['.png']))
396 RunCheck('image/jpeg', FilterFiles(['.jpg', '.jpeg', '.jpe']))
397 RunCheck('image/vnd.microsoft.icon', FilterFiles(['.ico']))
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000398 return output
399
400
401def CheckSvnProperty(input_api, output_api, prop, expected, affected_files):
402 """Checks that affected_files files have prop=expected."""
thestig@chromium.orgda8cddd2009-08-13 00:25:55 +0000403 if input_api.change.scm != 'svn':
404 return []
405
406 bad = filter(lambda f: f.Property(prop) != expected, affected_files)
maruel@chromium.orgb7d46902009-06-10 14:12:10 +0000407 if bad:
maruel@chromium.org0874d472009-06-10 19:08:33 +0000408 if input_api.is_committing:
maruel@chromium.orge3608df2009-11-10 20:22:57 +0000409 res_type = output_api.PresubmitError
maruel@chromium.org0874d472009-06-10 19:08:33 +0000410 else:
maruel@chromium.orge3608df2009-11-10 20:22:57 +0000411 res_type = output_api.PresubmitNotifyResult
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000412 message = 'Run the command: svn pset %s %s \\' % (prop, expected)
maruel@chromium.orge3608df2009-11-10 20:22:57 +0000413 return [res_type(message, items=bad)]
maruel@chromium.orgb7d46902009-06-10 14:12:10 +0000414 return []
415
416
maruel@chromium.org3410d912009-06-09 20:56:16 +0000417### Other checks
418
419def CheckDoNotSubmit(input_api, output_api):
420 return (
421 CheckDoNotSubmitInDescription(input_api, output_api) +
422 CheckDoNotSubmitInFiles(input_api, output_api)
423 )
424
425
bradnelson@google.comc0b332a2010-08-26 00:30:37 +0000426def CheckTreeIsOpen(input_api, output_api,
427 url=None, closed=None, json_url=None):
428 """Check whether to allow commit without prompt.
429
430 Supports two styles:
431 1. Checks that an url's content doesn't match a regexp that would mean that
432 the tree is closed. (old)
433 2. Check the json_url to decide whether to allow commit without prompt.
434 Args:
435 input_api: input related apis.
436 output_api: output related apis.
437 url: url to use for regex based tree status.
438 closed: regex to match for closed status.
439 json_url: url to download json style status.
440 """
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000441 if not input_api.is_committing:
442 return []
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000443 try:
bradnelson@google.comc0b332a2010-08-26 00:30:37 +0000444 if json_url:
445 connection = input_api.urllib2.urlopen(json_url)
446 status = input_api.json.loads(connection.read())
447 connection.close()
448 if not status['can_commit_freely']:
449 short_text = 'Tree state is: ' + status['general_state']
450 long_text = status['message'] + '\n' + json_url
451 return [output_api.PresubmitError(short_text, long_text=long_text)]
452 else:
453 # TODO(bradnelson): drop this once all users are gone.
454 connection = input_api.urllib2.urlopen(url)
455 status = connection.read()
456 connection.close()
457 if input_api.re.match(closed, status):
458 long_text = status + '\n' + url
459 return [output_api.PresubmitError('The tree is closed.',
460 long_text=long_text)]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000461 except IOError:
462 pass
463 return []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000464
465
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000466def RunUnitTestsInDirectory(
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000467 input_api, output_api, directory, whitelist=None, blacklist=None):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000468 """Lists all files in a directory and runs them. Doesn't recurse.
469
470 It's mainly a wrapper for RunUnitTests. USe whitelist and blacklist to filter
471 tests accordingly.
472 """
473 unit_tests = []
474 test_path = input_api.os_path.abspath(
475 input_api.os_path.join(input_api.PresubmitLocalPath(), directory))
476
477 def check(filename, filters):
478 return any(True for i in filters if input_api.re.match(i, filename))
479
480 for filename in input_api.os_listdir(test_path):
481 fullpath = input_api.os_path.join(test_path, filename)
482 if not input_api.os_path.isfile(fullpath):
483 continue
484 if whitelist and not check(filename, whitelist):
485 continue
486 if blacklist and check(filename, blacklist):
487 continue
488 unit_tests.append(input_api.os_path.join(directory, filename))
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000489 return RunUnitTests(input_api, output_api, unit_tests)
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000490
491
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000492def RunUnitTests(input_api, output_api, unit_tests):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000493 """Runs all unit tests in a directory.
494
495 On Windows, sys.executable is used for unit tests ending with ".py".
496 """
497 # We don't want to hinder users from uploading incomplete patches.
498 if input_api.is_committing:
499 message_type = output_api.PresubmitError
500 else:
501 message_type = output_api.PresubmitPromptWarning
502
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000503 results = []
504 for unit_test in unit_tests:
505 cmd = []
506 if input_api.platform == 'win32' and unit_test.endswith('.py'):
507 # Windows needs some help.
508 cmd = [input_api.python_executable]
509 cmd.append(unit_test)
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000510 if input_api.verbose:
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000511 print('Running %s' % unit_test)
maruel@chromium.org6c7723e2011-04-12 19:04:55 +0000512 cmd.append('--verbose')
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000513 try:
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000514 if input_api.verbose:
maruel@chromium.org0e766052011-04-06 13:32:51 +0000515 input_api.subprocess.check_call(cmd, cwd=input_api.PresubmitLocalPath())
516 else:
517 input_api.subprocess.check_output(
518 cmd, cwd=input_api.PresubmitLocalPath())
519 except (OSError, input_api.subprocess.CalledProcessError), e:
520 results.append(message_type('%s failed!\n%s' % (unit_test, e)))
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000521 return results
522
523
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000524def RunPythonUnitTests(input_api, output_api, unit_tests):
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000525 """Run the unit tests out of process, capture the output and use the result
526 code to determine success.
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000527
528 DEPRECATED.
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000529 """
maruel@chromium.orgd7dccf52009-06-06 18:51:58 +0000530 # We don't want to hinder users from uploading incomplete patches.
531 if input_api.is_committing:
532 message_type = output_api.PresubmitError
533 else:
534 message_type = output_api.PresubmitNotifyResult
maruel@chromium.org0e766052011-04-06 13:32:51 +0000535 results = []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000536 for unit_test in unit_tests:
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000537 # Run the unit tests out of process. This is because some unit tests
538 # stub out base libraries and don't clean up their mess. It's too easy to
539 # get subtle bugs.
540 cwd = None
541 env = None
542 unit_test_name = unit_test
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000543 # 'python -m test.unit_test' doesn't work. We need to change to the right
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000544 # directory instead.
545 if '.' in unit_test:
546 # Tests imported in submodules (subdirectories) assume that the current
547 # directory is in the PYTHONPATH. Manually fix that.
548 unit_test = unit_test.replace('.', '/')
549 cwd = input_api.os_path.dirname(unit_test)
550 unit_test = input_api.os_path.basename(unit_test)
551 env = input_api.environ.copy()
kbr@google.comab318592009-09-04 00:54:55 +0000552 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH
553 backpath = [
554 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1))
555 ]
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000556 if env.get('PYTHONPATH'):
557 backpath.append(env.get('PYTHONPATH'))
ukai@chromium.orga301f1f2009-08-05 10:37:33 +0000558 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000559 cmd = [input_api.python_executable, '-m', '%s' % unit_test]
560 try:
561 input_api.subprocess.check_output(cmd, cwd=cwd, env=env)
562 except (OSError, input_api.subprocess.CalledProcessError), e:
563 results.append(message_type('%s failed!\n%s' % (unit_test_name, e)))
564 return results
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000565
566
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000567def _FetchAllFiles(input_api, white_list, black_list):
568 """Hack to fetch all files."""
569 # We cannot use AffectedFiles here because we want to test every python
570 # file on each single python change. It's because a change in a python file
571 # can break another unmodified file.
572 # Use code similar to InputApi.FilterSourceFile()
573 def Find(filepath, filters):
574 for item in filters:
575 if input_api.re.match(item, filepath):
576 return True
577 return False
578
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000579 files = []
580 path_len = len(input_api.PresubmitLocalPath())
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000581 for dirpath, dirnames, filenames in input_api.os_walk(
582 input_api.PresubmitLocalPath()):
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000583 # Passes dirnames in black list to speed up search.
584 for item in dirnames[:]:
585 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
586 if Find(filepath, black_list):
587 dirnames.remove(item)
588 for item in filenames:
589 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
590 if Find(filepath, white_list) and not Find(filepath, black_list):
591 files.append(filepath)
592 return files
593
594
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000595def RunPylint(input_api, output_api, white_list=None, black_list=None):
596 """Run pylint on python files.
597
598 The default white_list enforces looking only a *.py files.
599 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000600 white_list = tuple(white_list or ('.*\.py$',))
601 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000602 if input_api.is_committing:
603 error_type = output_api.PresubmitError
604 else:
605 error_type = output_api.PresubmitPromptWarning
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000606
607 # Only trigger if there is at least one python file affected.
608 src_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
609 if not input_api.AffectedSourceFiles(src_filter):
610 return []
611
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000612 # On certain pylint/python version combination, running pylint throws a lot of
613 # warning messages.
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000614 import warnings
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000615 warnings.filterwarnings('ignore', category=DeprecationWarning)
616 try:
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000617 files = _FetchAllFiles(input_api, white_list, black_list)
618 if not files:
619 return []
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000620 # Now that at least one python file was modified and all the python files
621 # were listed, try to run pylint.
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000622 try:
623 from pylint import lint
maruel@chromium.org6fba34d2011-06-02 13:45:12 +0000624 input_api.logging.debug(
625 'Using pylint v%s from %s' % (lint.version, lint.__file__))
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000626 except ImportError:
627 if input_api.platform == 'win32':
628 return [output_api.PresubmitNotifyResult(
629 'Warning: Can\'t run pylint because it is not installed. Please '
630 'install manually\n'
631 'Cannot do static analysis of python files.')]
632 return [output_api.PresubmitError(
633 'Please install pylint with "sudo apt-get install python-setuptools; '
634 'sudo easy_install pylint"\n'
maruel@chromium.org725f1c32011-04-01 20:24:54 +0000635 'or visit http://pypi.python.org/pypi/setuptools.\n'
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000636 'Cannot do static analysis of python files.')]
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000637
638 def run_lint(files):
639 try:
640 lint.Run(files)
641 assert False
642 except SystemExit, e:
643 # pylint has the bad habit of calling sys.exit(), trap it here.
644 return e.code
645
646 result = None
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000647 if not input_api.verbose:
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000648 result = run_lint(sorted(files))
649 else:
650 for filename in sorted(files):
651 print('Running pylint on %s' % filename)
652 out = run_lint([filename])
653 if out:
654 result = out
maruel@chromium.orgfca53392010-12-21 18:42:57 +0000655 if result:
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000656 return [error_type('Fix pylint errors first.')]
maruel@chromium.orgfca53392010-12-21 18:42:57 +0000657 return []
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000658 finally:
659 warnings.filterwarnings('default', category=DeprecationWarning)
660
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000661
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000662# TODO(dpranke): Get the host_url from the input_api instead
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000663def CheckRietveldTryJobExecution(input_api, output_api, host_url, platforms,
664 owner):
665 if not input_api.is_committing:
666 return []
667 if not input_api.change.issue or not input_api.change.patchset:
668 return []
669 url = '%s/%d/get_build_results/%d' % (
670 host_url, input_api.change.issue, input_api.change.patchset)
671 try:
672 connection = input_api.urllib2.urlopen(url)
673 # platform|status|url
674 values = [item.split('|', 2) for item in connection.read().splitlines()]
675 connection.close()
676 except input_api.urllib2.HTTPError, e:
677 if e.code == 404:
678 # Fallback to no try job.
679 return [output_api.PresubmitPromptWarning(
680 'You should try the patch first.')]
681 else:
682 # Another HTTP error happened, warn the user.
683 return [output_api.PresubmitPromptWarning(
684 'Got %s while looking for try job status.' % str(e))]
685
686 if not values:
687 # It returned an empty list. Probably a private review.
688 return []
689 # Reformat as an dict of platform: [status, url]
690 values = dict([[v[0], [v[1], v[2]]] for v in values if len(v) == 3])
691 if not values:
692 # It returned useless data.
693 return [output_api.PresubmitNotifyResult('Failed to parse try job results')]
694
695 for platform in platforms:
696 values.setdefault(platform, ['not started', ''])
697 message = None
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000698 non_success = [k.upper() for k, v in values.iteritems() if v[0] != 'success']
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000699 if 'failure' in [v[0] for v in values.itervalues()]:
700 message = 'Try job failures on %s!\n' % ', '.join(non_success)
701 elif non_success:
702 message = ('Unfinished (or not even started) try jobs on '
703 '%s.\n') % ', '.join(non_success)
704 if message:
705 message += (
706 'Is try server wrong or broken? Please notify %s. '
707 'Thanks.\n' % owner)
708 return [output_api.PresubmitPromptWarning(message=message)]
709 return []
710
711
712def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
713 ignored):
714 if not input_api.json:
715 return [output_api.PresubmitPromptWarning(
716 'Please install simplejson or upgrade to python 2.6+')]
717 try:
718 connection = input_api.urllib2.urlopen(url)
719 raw_data = connection.read()
720 connection.close()
721 except IOError:
722 return [output_api.PresubmitNotifyResult('%s is not accessible' % url)]
723
724 try:
725 data = input_api.json.loads(raw_data)
726 except ValueError:
727 return [output_api.PresubmitNotifyResult('Received malformed json while '
728 'looking up buildbot status')]
729
730 out = []
731 for (builder_name, builder) in data.iteritems():
732 if builder_name in ignored:
733 continue
maruel@chromium.orgcf1982c2010-10-04 15:08:28 +0000734 if builder.get('state', '') == 'offline':
735 continue
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000736 pending_builds_len = len(builder.get('pending_builds', []))
737 if pending_builds_len > max_pendings:
738 out.append('%s has %d build(s) pending' %
739 (builder_name, pending_builds_len))
740 if out:
741 return [output_api.PresubmitPromptWarning(
742 'Build(s) pending. It is suggested to wait that no more than %d '
743 'builds are pending.' % max_pendings,
744 long_text='\n'.join(out))]
745 return []
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000746
747
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000748def CheckOwners(input_api, output_api, source_file_filter=None):
749 if not input_api.is_committing:
750 return []
751 if input_api.tbr:
dpranke@chromium.org0a2bb372011-03-25 01:16:22 +0000752 return [output_api.PresubmitNotifyResult(
753 '--tbr was specified, skipping OWNERS check')]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000754 if not input_api.change.issue:
dpranke@chromium.org0a2bb372011-03-25 01:16:22 +0000755 return [output_api.PresubmitError(
756 "OWNERS check failed: this change has no Rietveld issue number, so "
757 "we can't check it for approvals.")]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000758
dpranke@chromium.orgadd5df42011-03-08 23:04:01 +0000759 affected_files = set([f.LocalPath() for f in
sail@chromium.org5538e022011-05-12 17:53:16 +0000760 input_api.change.AffectedFiles(file_filter=source_file_filter)])
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000761
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000762 owners_db = input_api.owners_db
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000763 owner_email, approvers = _RietveldOwnerAndApprovers(input_api,
764 owners_db.email_regexp)
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000765 if not owner_email:
766 return [output_api.PresubmitWarning(
767 'The issue was not uploaded so you have no OWNER approval.')]
768
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000769 approvers_plus_owner = approvers.union(set([owner_email]))
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000770
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000771 missing_files = owners_db.files_not_covered_by(affected_files,
772 approvers_plus_owner)
773 if missing_files:
774 return [output_api.PresubmitError('Missing LGTM from an OWNER for: %s' %
775 ','.join(missing_files))]
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000776
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000777 if not approvers:
778 return [output_api.PresubmitError('Missing LGTM from someone other than %s'
779 % owner_email)]
780 return []
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000781
782
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000783def _RietveldOwnerAndApprovers(input_api, email_regexp):
784 """Return the owner and approvers of a change, if any."""
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000785 if not input_api.change.issue:
786 return None, None
787
788 issue_props = input_api.rietveld.get_issue_properties(
789 int(input_api.change.issue), True)
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000790 owner_email = issue_props['owner_email']
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000791
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000792 def match_reviewer(r):
maruel@chromium.org80941c22011-05-30 20:14:18 +0000793 return email_regexp.match(r) and r != owner_email
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000794
maruel@chromium.org80941c22011-05-30 20:14:18 +0000795 messages = issue_props.get('messages', [])
796 approvers = set(
797 m['sender'] for m in messages
798 if m.get('approval') and match_reviewer(m['sender']))
799
800 return owner_email, approvers
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000801
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000802
803def _CheckConstNSObject(input_api, output_api, source_file_filter):
804 """Checks to make sure no objective-c files have |const NSSomeClass*|."""
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000805 pattern = input_api.re.compile(
806 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000807
808 def objective_c_filter(f):
809 return (source_file_filter(f) and
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000810 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm'))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000811
812 files = []
813 for f in input_api.AffectedSourceFiles(objective_c_filter):
814 contents = input_api.ReadFile(f)
815 if pattern.search(contents):
816 files.append(f)
817
818 if files:
819 if input_api.is_committing:
820 res_type = output_api.PresubmitPromptWarning
821 else:
822 res_type = output_api.PresubmitNotifyResult
823 return [ res_type('|const NSClass*| is wrong, see ' +
824 'http://dev.chromium.org/developers/clang-mac',
825 files) ]
826 return []
827
828
829def _CheckSingletonInHeaders(input_api, output_api, source_file_filter):
830 """Checks to make sure no header files have |Singleton<|."""
dilmah@chromium.orgd22b9702011-08-26 13:53:49 +0000831 pattern = input_api.re.compile(r'Singleton\s*<')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000832 files = []
833 for f in input_api.AffectedSourceFiles(source_file_filter):
834 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
835 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
836 contents = input_api.ReadFile(f)
dilmah@chromium.orgd22b9702011-08-26 13:53:49 +0000837 for line in contents.splitlines(False):
838 line = input_api.re.sub(r'//.*$', '', line) # Strip C++ comment.
839 if pattern.search(line):
840 files.append(f)
841 break
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000842
843 if files:
844 return [ output_api.PresubmitError(
845 'Found Singleton<T> in the following header files.\n' +
846 'Please move them to an appropriate source file so that the ' +
847 'template gets instantiated in a single compilation unit.',
848 files) ]
849 return []
850
851
852def PanProjectChecks(input_api, output_api,
853 excluded_paths=None, text_files=None,
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000854 license_header=None, project_name=None,
855 owners_check=True):
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000856 """Checks that ALL chromium orbit projects should use.
857
858 These are checks to be run on all Chromium orbit project, including:
859 Chromium
860 Native Client
861 V8
862 When you update this function, please take this broad scope into account.
863 Args:
864 input_api: Bag of input related interfaces.
865 output_api: Bag of output related interfaces.
866 excluded_paths: Don't include these paths in common checks.
867 text_files: Which file are to be treated as documentation text files.
868 license_header: What license header should be on files.
869 project_name: What is the name of the project as it appears in the license.
870 Returns:
871 A list of warning or error objects.
872 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000873 excluded_paths = tuple(excluded_paths or [])
874 text_files = tuple(text_files or (
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +0000875 r'.+\.txt$',
876 r'.+\.json$',
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000877 ))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000878 project_name = project_name or 'Chromium'
879 license_header = license_header or (
880 r'.*? Copyright \(c\) %(year)s The %(project)s Authors\. '
881 r'All rights reserved\.\n'
882 r'.*? Use of this source code is governed by a BSD-style license that '
883 r'can be\n'
884 r'.*? found in the LICENSE file\.\n'
885 ) % {
dpranke@chromium.org0d1bdea2011-03-24 22:54:38 +0000886 'year': input_api.time.strftime('%Y'),
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000887 'project': project_name,
888 }
889
890 results = []
891 # This code loads the default black list (e.g. third_party, experimental, etc)
892 # and add our black list (breakpad, skia and v8 are still not following
893 # google style and are not really living this repository).
894 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage.
895 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths
896 white_list = input_api.DEFAULT_WHITE_LIST + text_files
897 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list)
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +0000898 text_files = lambda x: input_api.FilterSourceFile(
899 x, black_list=black_list, white_list=white_list)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000900
901 snapshot_memory = []
902 def snapshot(msg):
903 """Measures & prints performance warning if a rule is running slow."""
904 dt2 = input_api.time.clock()
905 if snapshot_memory:
906 delta_ms = int(1000*(dt2 - snapshot_memory[0]))
907 if delta_ms > 500:
908 print " %s took a long time: %dms" % (snapshot_memory[1], delta_ms)
909 snapshot_memory[:] = (dt2, msg)
910
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000911 if owners_check:
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000912 snapshot("checking owners")
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000913 results.extend(input_api.canned_checks.CheckOwners(
dpranke@chromium.org751797a2011-06-07 18:46:27 +0000914 input_api, output_api, source_file_filter=None))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000915
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000916 snapshot("checking long lines")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000917 results.extend(input_api.canned_checks.CheckLongLines(
918 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000919 snapshot( "checking tabs")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000920 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
921 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000922 snapshot( "checking stray whitespace")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000923 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
924 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000925 snapshot("checking nsobjects")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000926 results.extend(_CheckConstNSObject(
927 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000928 snapshot("checking singletons")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000929 results.extend(_CheckSingletonInHeaders(
930 input_api, output_api, source_file_filter=sources))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +0000931
932 # The following checks are only done on commit, since the commit bot will
933 # auto-fix most of these.
934 if input_api.is_committing:
maruel@chromium.org8571dac2011-05-10 18:10:13 +0000935 snapshot("checking eol style")
936 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle(
937 input_api, output_api, source_file_filter=text_files))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +0000938 snapshot("checking svn mime types")
939 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes(
940 input_api, output_api))
941 snapshot("checking license")
942 results.extend(input_api.canned_checks.CheckLicense(
943 input_api, output_api, license_header, source_file_filter=sources))
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +0000944 snapshot("checking was uploaded")
945 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
946 input_api, output_api))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000947 snapshot("done")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000948 return results