blob: 6f7c4bafd5d2fd6e7bc26a3f8537ac75b4876fa1 [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
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000480 to_run = found = 0
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000481 for filename in input_api.os_listdir(test_path):
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000482 found += 1
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000483 fullpath = input_api.os_path.join(test_path, filename)
484 if not input_api.os_path.isfile(fullpath):
485 continue
486 if whitelist and not check(filename, whitelist):
487 continue
488 if blacklist and check(filename, blacklist):
489 continue
490 unit_tests.append(input_api.os_path.join(directory, filename))
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000491 to_run += 1
492 input_api.logging.debug('Found %d files, running %d' % (found, to_run))
493 if not to_run:
494 return [
495 output_api.PresubmitPromptWarning(
496 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
497 % (found, whitelist, blacklist, directory))
498 ]
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000499 return RunUnitTests(input_api, output_api, unit_tests)
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000500
501
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000502def RunUnitTests(input_api, output_api, unit_tests):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000503 """Runs all unit tests in a directory.
504
505 On Windows, sys.executable is used for unit tests ending with ".py".
506 """
507 # We don't want to hinder users from uploading incomplete patches.
508 if input_api.is_committing:
509 message_type = output_api.PresubmitError
510 else:
511 message_type = output_api.PresubmitPromptWarning
512
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000513 results = []
514 for unit_test in unit_tests:
515 cmd = []
516 if input_api.platform == 'win32' and unit_test.endswith('.py'):
517 # Windows needs some help.
518 cmd = [input_api.python_executable]
519 cmd.append(unit_test)
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000520 if input_api.verbose:
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000521 print('Running %s' % unit_test)
maruel@chromium.org6c7723e2011-04-12 19:04:55 +0000522 cmd.append('--verbose')
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000523 try:
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000524 if input_api.verbose:
maruel@chromium.org0e766052011-04-06 13:32:51 +0000525 input_api.subprocess.check_call(cmd, cwd=input_api.PresubmitLocalPath())
526 else:
527 input_api.subprocess.check_output(
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000528 cmd,
529 stderr=input_api.subprocess.STDOUT,
530 cwd=input_api.PresubmitLocalPath())
maruel@chromium.org0e766052011-04-06 13:32:51 +0000531 except (OSError, input_api.subprocess.CalledProcessError), e:
532 results.append(message_type('%s failed!\n%s' % (unit_test, e)))
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000533 return results
534
535
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000536def RunPythonUnitTests(input_api, output_api, unit_tests):
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000537 """Run the unit tests out of process, capture the output and use the result
538 code to determine success.
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000539
540 DEPRECATED.
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000541 """
maruel@chromium.orgd7dccf52009-06-06 18:51:58 +0000542 # We don't want to hinder users from uploading incomplete patches.
543 if input_api.is_committing:
544 message_type = output_api.PresubmitError
545 else:
546 message_type = output_api.PresubmitNotifyResult
maruel@chromium.org0e766052011-04-06 13:32:51 +0000547 results = []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000548 for unit_test in unit_tests:
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000549 # Run the unit tests out of process. This is because some unit tests
550 # stub out base libraries and don't clean up their mess. It's too easy to
551 # get subtle bugs.
552 cwd = None
553 env = None
554 unit_test_name = unit_test
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000555 # 'python -m test.unit_test' doesn't work. We need to change to the right
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000556 # directory instead.
557 if '.' in unit_test:
558 # Tests imported in submodules (subdirectories) assume that the current
559 # directory is in the PYTHONPATH. Manually fix that.
560 unit_test = unit_test.replace('.', '/')
561 cwd = input_api.os_path.dirname(unit_test)
562 unit_test = input_api.os_path.basename(unit_test)
563 env = input_api.environ.copy()
kbr@google.comab318592009-09-04 00:54:55 +0000564 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH
565 backpath = [
566 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1))
567 ]
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000568 if env.get('PYTHONPATH'):
569 backpath.append(env.get('PYTHONPATH'))
ukai@chromium.orga301f1f2009-08-05 10:37:33 +0000570 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000571 cmd = [input_api.python_executable, '-m', '%s' % unit_test]
572 try:
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000573 input_api.subprocess.check_output(
574 cmd, stderr=input_api.subprocess.STDOUT, cwd=cwd, env=env)
maruel@chromium.org0e766052011-04-06 13:32:51 +0000575 except (OSError, input_api.subprocess.CalledProcessError), e:
576 results.append(message_type('%s failed!\n%s' % (unit_test_name, e)))
577 return results
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000578
579
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000580def _FetchAllFiles(input_api, white_list, black_list):
581 """Hack to fetch all files."""
582 # We cannot use AffectedFiles here because we want to test every python
583 # file on each single python change. It's because a change in a python file
584 # can break another unmodified file.
585 # Use code similar to InputApi.FilterSourceFile()
586 def Find(filepath, filters):
587 for item in filters:
588 if input_api.re.match(item, filepath):
589 return True
590 return False
591
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000592 files = []
593 path_len = len(input_api.PresubmitLocalPath())
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000594 for dirpath, dirnames, filenames in input_api.os_walk(
595 input_api.PresubmitLocalPath()):
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000596 # Passes dirnames in black list to speed up search.
597 for item in dirnames[:]:
598 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
599 if Find(filepath, black_list):
600 dirnames.remove(item)
601 for item in filenames:
602 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
603 if Find(filepath, white_list) and not Find(filepath, black_list):
604 files.append(filepath)
605 return files
606
607
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000608def RunPylint(input_api, output_api, white_list=None, black_list=None):
609 """Run pylint on python files.
610
611 The default white_list enforces looking only a *.py files.
612 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000613 white_list = tuple(white_list or ('.*\.py$',))
614 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000615 if input_api.is_committing:
616 error_type = output_api.PresubmitError
617 else:
618 error_type = output_api.PresubmitPromptWarning
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000619
620 # Only trigger if there is at least one python file affected.
621 src_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
622 if not input_api.AffectedSourceFiles(src_filter):
623 return []
624
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000625 # On certain pylint/python version combination, running pylint throws a lot of
626 # warning messages.
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000627 import warnings
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000628 warnings.filterwarnings('ignore', category=DeprecationWarning)
629 try:
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000630 files = _FetchAllFiles(input_api, white_list, black_list)
631 if not files:
632 return []
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000633 # Now that at least one python file was modified and all the python files
634 # were listed, try to run pylint.
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000635 try:
636 from pylint import lint
maruel@chromium.orgce71e672011-10-23 00:35:58 +0000637 from pylint.utils import UnknownMessage
maruel@chromium.org6fba34d2011-06-02 13:45:12 +0000638 input_api.logging.debug(
639 'Using pylint v%s from %s' % (lint.version, lint.__file__))
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000640 except ImportError:
641 if input_api.platform == 'win32':
642 return [output_api.PresubmitNotifyResult(
643 'Warning: Can\'t run pylint because it is not installed. Please '
644 'install manually\n'
645 'Cannot do static analysis of python files.')]
646 return [output_api.PresubmitError(
647 'Please install pylint with "sudo apt-get install python-setuptools; '
648 'sudo easy_install pylint"\n'
maruel@chromium.org725f1c32011-04-01 20:24:54 +0000649 'or visit http://pypi.python.org/pypi/setuptools.\n'
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000650 'Cannot do static analysis of python files.')]
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000651
652 def run_lint(files):
653 try:
654 lint.Run(files)
655 assert False
656 except SystemExit, e:
657 # pylint has the bad habit of calling sys.exit(), trap it here.
658 return e.code
maruel@chromium.orgce71e672011-10-23 00:35:58 +0000659 except UnknownMessage, e:
660 return 'Please upgrade pylint: %s' % e
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000661
662 result = None
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000663 if not input_api.verbose:
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000664 result = run_lint(sorted(files))
665 else:
666 for filename in sorted(files):
667 print('Running pylint on %s' % filename)
maruel@chromium.orgce71e672011-10-23 00:35:58 +0000668 result = run_lint([filename]) or result
669 if isinstance(result, basestring):
670 return [error_type(result)]
671 elif result:
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000672 return [error_type('Fix pylint errors first.')]
maruel@chromium.orgfca53392010-12-21 18:42:57 +0000673 return []
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000674 finally:
675 warnings.filterwarnings('default', category=DeprecationWarning)
676
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000677
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000678# TODO(dpranke): Get the host_url from the input_api instead
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000679def CheckRietveldTryJobExecution(input_api, output_api, host_url, platforms,
680 owner):
maruel@chromium.org85da74b2011-10-27 17:13:30 +0000681 # Temporarily 'fix' the check while the Rietveld API is being upgraded to
682 # something sensible.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000683 return []
684
685
686def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
687 ignored):
688 if not input_api.json:
689 return [output_api.PresubmitPromptWarning(
690 'Please install simplejson or upgrade to python 2.6+')]
691 try:
692 connection = input_api.urllib2.urlopen(url)
693 raw_data = connection.read()
694 connection.close()
695 except IOError:
696 return [output_api.PresubmitNotifyResult('%s is not accessible' % url)]
697
698 try:
699 data = input_api.json.loads(raw_data)
700 except ValueError:
701 return [output_api.PresubmitNotifyResult('Received malformed json while '
702 'looking up buildbot status')]
703
704 out = []
705 for (builder_name, builder) in data.iteritems():
706 if builder_name in ignored:
707 continue
maruel@chromium.orgcf1982c2010-10-04 15:08:28 +0000708 if builder.get('state', '') == 'offline':
709 continue
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000710 pending_builds_len = len(builder.get('pending_builds', []))
711 if pending_builds_len > max_pendings:
712 out.append('%s has %d build(s) pending' %
713 (builder_name, pending_builds_len))
714 if out:
715 return [output_api.PresubmitPromptWarning(
716 'Build(s) pending. It is suggested to wait that no more than %d '
717 'builds are pending.' % max_pendings,
718 long_text='\n'.join(out))]
719 return []
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000720
721
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000722def CheckOwners(input_api, output_api, source_file_filter=None):
723 if not input_api.is_committing:
724 return []
725 if input_api.tbr:
dpranke@chromium.org0a2bb372011-03-25 01:16:22 +0000726 return [output_api.PresubmitNotifyResult(
727 '--tbr was specified, skipping OWNERS check')]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000728 if not input_api.change.issue:
dpranke@chromium.org0a2bb372011-03-25 01:16:22 +0000729 return [output_api.PresubmitError(
730 "OWNERS check failed: this change has no Rietveld issue number, so "
731 "we can't check it for approvals.")]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000732
dpranke@chromium.orgadd5df42011-03-08 23:04:01 +0000733 affected_files = set([f.LocalPath() for f in
sail@chromium.org5538e022011-05-12 17:53:16 +0000734 input_api.change.AffectedFiles(file_filter=source_file_filter)])
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000735
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000736 owners_db = input_api.owners_db
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000737 owner_email, approvers = _RietveldOwnerAndApprovers(input_api,
738 owners_db.email_regexp)
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000739 if not owner_email:
740 return [output_api.PresubmitWarning(
741 'The issue was not uploaded so you have no OWNER approval.')]
742
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000743 approvers_plus_owner = approvers.union(set([owner_email]))
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000744
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000745 missing_files = owners_db.files_not_covered_by(affected_files,
746 approvers_plus_owner)
747 if missing_files:
748 return [output_api.PresubmitError('Missing LGTM from an OWNER for: %s' %
749 ','.join(missing_files))]
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000750
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000751 if not approvers:
752 return [output_api.PresubmitError('Missing LGTM from someone other than %s'
753 % owner_email)]
754 return []
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000755
756
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000757def _RietveldOwnerAndApprovers(input_api, email_regexp):
758 """Return the owner and approvers of a change, if any."""
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000759 if not input_api.change.issue:
760 return None, None
761
762 issue_props = input_api.rietveld.get_issue_properties(
763 int(input_api.change.issue), True)
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000764 owner_email = issue_props['owner_email']
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000765
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000766 def match_reviewer(r):
maruel@chromium.org80941c22011-05-30 20:14:18 +0000767 return email_regexp.match(r) and r != owner_email
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000768
maruel@chromium.org80941c22011-05-30 20:14:18 +0000769 messages = issue_props.get('messages', [])
770 approvers = set(
771 m['sender'] for m in messages
772 if m.get('approval') and match_reviewer(m['sender']))
773
774 return owner_email, approvers
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000775
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000776
777def _CheckConstNSObject(input_api, output_api, source_file_filter):
778 """Checks to make sure no objective-c files have |const NSSomeClass*|."""
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000779 pattern = input_api.re.compile(
780 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000781
782 def objective_c_filter(f):
783 return (source_file_filter(f) and
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000784 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm'))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000785
786 files = []
787 for f in input_api.AffectedSourceFiles(objective_c_filter):
788 contents = input_api.ReadFile(f)
789 if pattern.search(contents):
790 files.append(f)
791
792 if files:
793 if input_api.is_committing:
794 res_type = output_api.PresubmitPromptWarning
795 else:
796 res_type = output_api.PresubmitNotifyResult
797 return [ res_type('|const NSClass*| is wrong, see ' +
798 'http://dev.chromium.org/developers/clang-mac',
799 files) ]
800 return []
801
802
803def _CheckSingletonInHeaders(input_api, output_api, source_file_filter):
804 """Checks to make sure no header files have |Singleton<|."""
dilmah@chromium.orgd22b9702011-08-26 13:53:49 +0000805 pattern = input_api.re.compile(r'Singleton\s*<')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000806 files = []
807 for f in input_api.AffectedSourceFiles(source_file_filter):
808 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
809 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
810 contents = input_api.ReadFile(f)
dilmah@chromium.orgd22b9702011-08-26 13:53:49 +0000811 for line in contents.splitlines(False):
812 line = input_api.re.sub(r'//.*$', '', line) # Strip C++ comment.
813 if pattern.search(line):
814 files.append(f)
815 break
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000816
817 if files:
818 return [ output_api.PresubmitError(
819 'Found Singleton<T> in the following header files.\n' +
820 'Please move them to an appropriate source file so that the ' +
821 'template gets instantiated in a single compilation unit.',
822 files) ]
823 return []
824
825
826def PanProjectChecks(input_api, output_api,
827 excluded_paths=None, text_files=None,
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000828 license_header=None, project_name=None,
829 owners_check=True):
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000830 """Checks that ALL chromium orbit projects should use.
831
832 These are checks to be run on all Chromium orbit project, including:
833 Chromium
834 Native Client
835 V8
836 When you update this function, please take this broad scope into account.
837 Args:
838 input_api: Bag of input related interfaces.
839 output_api: Bag of output related interfaces.
840 excluded_paths: Don't include these paths in common checks.
841 text_files: Which file are to be treated as documentation text files.
842 license_header: What license header should be on files.
843 project_name: What is the name of the project as it appears in the license.
844 Returns:
845 A list of warning or error objects.
846 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000847 excluded_paths = tuple(excluded_paths or [])
848 text_files = tuple(text_files or (
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +0000849 r'.+\.txt$',
850 r'.+\.json$',
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000851 ))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000852 project_name = project_name or 'Chromium'
853 license_header = license_header or (
854 r'.*? Copyright \(c\) %(year)s The %(project)s Authors\. '
855 r'All rights reserved\.\n'
856 r'.*? Use of this source code is governed by a BSD-style license that '
857 r'can be\n'
858 r'.*? found in the LICENSE file\.\n'
859 ) % {
dpranke@chromium.org0d1bdea2011-03-24 22:54:38 +0000860 'year': input_api.time.strftime('%Y'),
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000861 'project': project_name,
862 }
863
864 results = []
865 # This code loads the default black list (e.g. third_party, experimental, etc)
866 # and add our black list (breakpad, skia and v8 are still not following
867 # google style and are not really living this repository).
868 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage.
869 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths
870 white_list = input_api.DEFAULT_WHITE_LIST + text_files
871 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list)
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +0000872 text_files = lambda x: input_api.FilterSourceFile(
873 x, black_list=black_list, white_list=white_list)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000874
875 snapshot_memory = []
876 def snapshot(msg):
877 """Measures & prints performance warning if a rule is running slow."""
878 dt2 = input_api.time.clock()
879 if snapshot_memory:
880 delta_ms = int(1000*(dt2 - snapshot_memory[0]))
881 if delta_ms > 500:
882 print " %s took a long time: %dms" % (snapshot_memory[1], delta_ms)
883 snapshot_memory[:] = (dt2, msg)
884
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000885 if owners_check:
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000886 snapshot("checking owners")
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000887 results.extend(input_api.canned_checks.CheckOwners(
dpranke@chromium.org751797a2011-06-07 18:46:27 +0000888 input_api, output_api, source_file_filter=None))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000889
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000890 snapshot("checking long lines")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000891 results.extend(input_api.canned_checks.CheckLongLines(
892 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000893 snapshot( "checking tabs")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000894 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
895 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000896 snapshot( "checking stray whitespace")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000897 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
898 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000899 snapshot("checking nsobjects")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000900 results.extend(_CheckConstNSObject(
901 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000902 snapshot("checking singletons")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000903 results.extend(_CheckSingletonInHeaders(
904 input_api, output_api, source_file_filter=sources))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +0000905
906 # The following checks are only done on commit, since the commit bot will
907 # auto-fix most of these.
908 if input_api.is_committing:
maruel@chromium.org8571dac2011-05-10 18:10:13 +0000909 snapshot("checking eol style")
910 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle(
911 input_api, output_api, source_file_filter=text_files))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +0000912 snapshot("checking svn mime types")
913 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes(
914 input_api, output_api))
915 snapshot("checking license")
916 results.extend(input_api.canned_checks.CheckLicense(
917 input_api, output_api, license_header, source_file_filter=sources))
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +0000918 snapshot("checking was uploaded")
919 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
920 input_api, output_api))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000921 snapshot("done")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000922 return results