blob: 0a92e3ae03a8ee555fc4f63b77c1b282fe1c4943 [file] [log] [blame]
estade@chromium.orgae7af922012-01-27 14:51:13 +00001# Copyright (c) 2012 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)]
rohitrao@chromium.orgd490ee82012-02-06 19:31:33 +0000461 except IOError as e:
462 return [output_api.PresubmitError('Error fetching tree status.',
463 long_text=str(e))]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000464 return []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000465
466
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000467def RunUnitTestsInDirectory(
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000468 input_api, output_api, directory, whitelist=None, blacklist=None):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000469 """Lists all files in a directory and runs them. Doesn't recurse.
470
471 It's mainly a wrapper for RunUnitTests. USe whitelist and blacklist to filter
472 tests accordingly.
473 """
474 unit_tests = []
475 test_path = input_api.os_path.abspath(
476 input_api.os_path.join(input_api.PresubmitLocalPath(), directory))
477
478 def check(filename, filters):
479 return any(True for i in filters if input_api.re.match(i, filename))
480
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000481 to_run = found = 0
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000482 for filename in input_api.os_listdir(test_path):
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000483 found += 1
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000484 fullpath = input_api.os_path.join(test_path, filename)
485 if not input_api.os_path.isfile(fullpath):
486 continue
487 if whitelist and not check(filename, whitelist):
488 continue
489 if blacklist and check(filename, blacklist):
490 continue
491 unit_tests.append(input_api.os_path.join(directory, filename))
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000492 to_run += 1
493 input_api.logging.debug('Found %d files, running %d' % (found, to_run))
494 if not to_run:
495 return [
496 output_api.PresubmitPromptWarning(
497 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
498 % (found, whitelist, blacklist, directory))
499 ]
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000500 return RunUnitTests(input_api, output_api, unit_tests)
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000501
502
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000503def RunUnitTests(input_api, output_api, unit_tests):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000504 """Runs all unit tests in a directory.
505
506 On Windows, sys.executable is used for unit tests ending with ".py".
507 """
508 # We don't want to hinder users from uploading incomplete patches.
509 if input_api.is_committing:
510 message_type = output_api.PresubmitError
511 else:
512 message_type = output_api.PresubmitPromptWarning
513
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000514 results = []
515 for unit_test in unit_tests:
516 cmd = []
517 if input_api.platform == 'win32' and unit_test.endswith('.py'):
518 # Windows needs some help.
519 cmd = [input_api.python_executable]
520 cmd.append(unit_test)
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000521 if input_api.verbose:
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000522 print('Running %s' % unit_test)
maruel@chromium.org6c7723e2011-04-12 19:04:55 +0000523 cmd.append('--verbose')
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000524 try:
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000525 if input_api.verbose:
maruel@chromium.org0e766052011-04-06 13:32:51 +0000526 input_api.subprocess.check_call(cmd, cwd=input_api.PresubmitLocalPath())
527 else:
528 input_api.subprocess.check_output(
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000529 cmd,
530 stderr=input_api.subprocess.STDOUT,
531 cwd=input_api.PresubmitLocalPath())
maruel@chromium.org0e766052011-04-06 13:32:51 +0000532 except (OSError, input_api.subprocess.CalledProcessError), e:
533 results.append(message_type('%s failed!\n%s' % (unit_test, e)))
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000534 return results
535
536
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000537def RunPythonUnitTests(input_api, output_api, unit_tests):
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000538 """Run the unit tests out of process, capture the output and use the result
539 code to determine success.
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000540
541 DEPRECATED.
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000542 """
maruel@chromium.orgd7dccf52009-06-06 18:51:58 +0000543 # We don't want to hinder users from uploading incomplete patches.
544 if input_api.is_committing:
545 message_type = output_api.PresubmitError
546 else:
547 message_type = output_api.PresubmitNotifyResult
maruel@chromium.org0e766052011-04-06 13:32:51 +0000548 results = []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000549 for unit_test in unit_tests:
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000550 # Run the unit tests out of process. This is because some unit tests
551 # stub out base libraries and don't clean up their mess. It's too easy to
552 # get subtle bugs.
553 cwd = None
554 env = None
555 unit_test_name = unit_test
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000556 # 'python -m test.unit_test' doesn't work. We need to change to the right
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000557 # directory instead.
558 if '.' in unit_test:
559 # Tests imported in submodules (subdirectories) assume that the current
560 # directory is in the PYTHONPATH. Manually fix that.
561 unit_test = unit_test.replace('.', '/')
562 cwd = input_api.os_path.dirname(unit_test)
563 unit_test = input_api.os_path.basename(unit_test)
564 env = input_api.environ.copy()
kbr@google.comab318592009-09-04 00:54:55 +0000565 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH
566 backpath = [
567 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1))
568 ]
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000569 if env.get('PYTHONPATH'):
570 backpath.append(env.get('PYTHONPATH'))
ukai@chromium.orga301f1f2009-08-05 10:37:33 +0000571 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000572 cmd = [input_api.python_executable, '-m', '%s' % unit_test]
573 try:
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000574 input_api.subprocess.check_output(
575 cmd, stderr=input_api.subprocess.STDOUT, cwd=cwd, env=env)
maruel@chromium.org0e766052011-04-06 13:32:51 +0000576 except (OSError, input_api.subprocess.CalledProcessError), e:
577 results.append(message_type('%s failed!\n%s' % (unit_test_name, e)))
578 return results
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000579
580
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000581def _FetchAllFiles(input_api, white_list, black_list):
582 """Hack to fetch all files."""
583 # We cannot use AffectedFiles here because we want to test every python
584 # file on each single python change. It's because a change in a python file
585 # can break another unmodified file.
586 # Use code similar to InputApi.FilterSourceFile()
587 def Find(filepath, filters):
588 for item in filters:
589 if input_api.re.match(item, filepath):
590 return True
591 return False
592
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000593 files = []
594 path_len = len(input_api.PresubmitLocalPath())
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000595 for dirpath, dirnames, filenames in input_api.os_walk(
596 input_api.PresubmitLocalPath()):
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000597 # Passes dirnames in black list to speed up search.
598 for item in dirnames[:]:
599 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
600 if Find(filepath, black_list):
601 dirnames.remove(item)
602 for item in filenames:
603 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
604 if Find(filepath, white_list) and not Find(filepath, black_list):
605 files.append(filepath)
606 return files
607
608
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000609def RunPylint(input_api, output_api, white_list=None, black_list=None):
610 """Run pylint on python files.
611
612 The default white_list enforces looking only a *.py files.
613 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000614 white_list = tuple(white_list or ('.*\.py$',))
615 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000616 if input_api.is_committing:
617 error_type = output_api.PresubmitError
618 else:
619 error_type = output_api.PresubmitPromptWarning
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000620
621 # Only trigger if there is at least one python file affected.
622 src_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
623 if not input_api.AffectedSourceFiles(src_filter):
624 return []
625
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000626 # On certain pylint/python version combination, running pylint throws a lot of
627 # warning messages.
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000628 import warnings
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000629 warnings.filterwarnings('ignore', category=DeprecationWarning)
630 try:
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000631 files = _FetchAllFiles(input_api, white_list, black_list)
632 if not files:
633 return []
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000634 # Now that at least one python file was modified and all the python files
635 # were listed, try to run pylint.
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000636 try:
637 from pylint import lint
maruel@chromium.orgce71e672011-10-23 00:35:58 +0000638 from pylint.utils import UnknownMessage
maruel@chromium.org6fba34d2011-06-02 13:45:12 +0000639 input_api.logging.debug(
640 'Using pylint v%s from %s' % (lint.version, lint.__file__))
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000641 except ImportError:
642 if input_api.platform == 'win32':
643 return [output_api.PresubmitNotifyResult(
644 'Warning: Can\'t run pylint because it is not installed. Please '
645 'install manually\n'
646 'Cannot do static analysis of python files.')]
647 return [output_api.PresubmitError(
648 'Please install pylint with "sudo apt-get install python-setuptools; '
649 'sudo easy_install pylint"\n'
maruel@chromium.org725f1c32011-04-01 20:24:54 +0000650 'or visit http://pypi.python.org/pypi/setuptools.\n'
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000651 'Cannot do static analysis of python files.')]
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000652
653 def run_lint(files):
654 try:
655 lint.Run(files)
656 assert False
657 except SystemExit, e:
658 # pylint has the bad habit of calling sys.exit(), trap it here.
659 return e.code
maruel@chromium.orgce71e672011-10-23 00:35:58 +0000660 except UnknownMessage, e:
661 return 'Please upgrade pylint: %s' % e
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000662
663 result = None
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000664 if not input_api.verbose:
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000665 result = run_lint(sorted(files))
666 else:
667 for filename in sorted(files):
668 print('Running pylint on %s' % filename)
maruel@chromium.orgce71e672011-10-23 00:35:58 +0000669 result = run_lint([filename]) or result
670 if isinstance(result, basestring):
671 return [error_type(result)]
672 elif result:
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000673 return [error_type('Fix pylint errors first.')]
maruel@chromium.orgfca53392010-12-21 18:42:57 +0000674 return []
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000675 finally:
676 warnings.filterwarnings('default', category=DeprecationWarning)
677
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000678
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000679# TODO(dpranke): Get the host_url from the input_api instead
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000680def CheckRietveldTryJobExecution(input_api, output_api, host_url, platforms,
681 owner):
maruel@chromium.org85da74b2011-10-27 17:13:30 +0000682 # Temporarily 'fix' the check while the Rietveld API is being upgraded to
683 # something sensible.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000684 return []
685
686
687def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
688 ignored):
689 if not input_api.json:
690 return [output_api.PresubmitPromptWarning(
691 'Please install simplejson or upgrade to python 2.6+')]
692 try:
693 connection = input_api.urllib2.urlopen(url)
694 raw_data = connection.read()
695 connection.close()
696 except IOError:
697 return [output_api.PresubmitNotifyResult('%s is not accessible' % url)]
698
699 try:
700 data = input_api.json.loads(raw_data)
701 except ValueError:
702 return [output_api.PresubmitNotifyResult('Received malformed json while '
703 'looking up buildbot status')]
704
705 out = []
706 for (builder_name, builder) in data.iteritems():
707 if builder_name in ignored:
708 continue
maruel@chromium.orgcf1982c2010-10-04 15:08:28 +0000709 if builder.get('state', '') == 'offline':
710 continue
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000711 pending_builds_len = len(builder.get('pending_builds', []))
712 if pending_builds_len > max_pendings:
713 out.append('%s has %d build(s) pending' %
714 (builder_name, pending_builds_len))
715 if out:
716 return [output_api.PresubmitPromptWarning(
717 'Build(s) pending. It is suggested to wait that no more than %d '
718 'builds are pending.' % max_pendings,
719 long_text='\n'.join(out))]
720 return []
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000721
722
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000723def CheckOwners(input_api, output_api, source_file_filter=None):
724 if not input_api.is_committing:
725 return []
726 if input_api.tbr:
dpranke@chromium.org0a2bb372011-03-25 01:16:22 +0000727 return [output_api.PresubmitNotifyResult(
728 '--tbr was specified, skipping OWNERS check')]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000729 if not input_api.change.issue:
dpranke@chromium.org0a2bb372011-03-25 01:16:22 +0000730 return [output_api.PresubmitError(
731 "OWNERS check failed: this change has no Rietveld issue number, so "
732 "we can't check it for approvals.")]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000733
dpranke@chromium.orgadd5df42011-03-08 23:04:01 +0000734 affected_files = set([f.LocalPath() for f in
sail@chromium.org5538e022011-05-12 17:53:16 +0000735 input_api.change.AffectedFiles(file_filter=source_file_filter)])
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000736
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000737 owners_db = input_api.owners_db
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000738 owner_email, approvers = _RietveldOwnerAndApprovers(input_api,
739 owners_db.email_regexp)
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000740 if not owner_email:
741 return [output_api.PresubmitWarning(
742 'The issue was not uploaded so you have no OWNER approval.')]
743
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000744 approvers_plus_owner = approvers.union(set([owner_email]))
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000745
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000746 missing_files = owners_db.files_not_covered_by(affected_files,
747 approvers_plus_owner)
748 if missing_files:
749 return [output_api.PresubmitError('Missing LGTM from an OWNER for: %s' %
750 ','.join(missing_files))]
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000751
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000752 if not approvers:
753 return [output_api.PresubmitError('Missing LGTM from someone other than %s'
754 % owner_email)]
755 return []
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000756
757
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000758def _RietveldOwnerAndApprovers(input_api, email_regexp):
759 """Return the owner and approvers of a change, if any."""
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000760 if not input_api.change.issue:
761 return None, None
762
763 issue_props = input_api.rietveld.get_issue_properties(
764 int(input_api.change.issue), True)
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000765 owner_email = issue_props['owner_email']
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000766
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000767 def match_reviewer(r):
maruel@chromium.org80941c22011-05-30 20:14:18 +0000768 return email_regexp.match(r) and r != owner_email
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000769
maruel@chromium.org80941c22011-05-30 20:14:18 +0000770 messages = issue_props.get('messages', [])
771 approvers = set(
772 m['sender'] for m in messages
773 if m.get('approval') and match_reviewer(m['sender']))
774
775 return owner_email, approvers
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000776
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000777
778def _CheckConstNSObject(input_api, output_api, source_file_filter):
779 """Checks to make sure no objective-c files have |const NSSomeClass*|."""
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000780 pattern = input_api.re.compile(
781 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000782
783 def objective_c_filter(f):
784 return (source_file_filter(f) and
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000785 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm'))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000786
787 files = []
788 for f in input_api.AffectedSourceFiles(objective_c_filter):
789 contents = input_api.ReadFile(f)
790 if pattern.search(contents):
791 files.append(f)
792
793 if files:
794 if input_api.is_committing:
795 res_type = output_api.PresubmitPromptWarning
796 else:
797 res_type = output_api.PresubmitNotifyResult
798 return [ res_type('|const NSClass*| is wrong, see ' +
799 'http://dev.chromium.org/developers/clang-mac',
800 files) ]
801 return []
802
803
804def _CheckSingletonInHeaders(input_api, output_api, source_file_filter):
805 """Checks to make sure no header files have |Singleton<|."""
dilmah@chromium.orgd22b9702011-08-26 13:53:49 +0000806 pattern = input_api.re.compile(r'Singleton\s*<')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000807 files = []
808 for f in input_api.AffectedSourceFiles(source_file_filter):
809 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
810 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
811 contents = input_api.ReadFile(f)
dilmah@chromium.orgd22b9702011-08-26 13:53:49 +0000812 for line in contents.splitlines(False):
813 line = input_api.re.sub(r'//.*$', '', line) # Strip C++ comment.
814 if pattern.search(line):
815 files.append(f)
816 break
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000817
818 if files:
819 return [ output_api.PresubmitError(
820 'Found Singleton<T> in the following header files.\n' +
821 'Please move them to an appropriate source file so that the ' +
822 'template gets instantiated in a single compilation unit.',
823 files) ]
824 return []
825
826
827def PanProjectChecks(input_api, output_api,
828 excluded_paths=None, text_files=None,
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000829 license_header=None, project_name=None,
830 owners_check=True):
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000831 """Checks that ALL chromium orbit projects should use.
832
833 These are checks to be run on all Chromium orbit project, including:
834 Chromium
835 Native Client
836 V8
837 When you update this function, please take this broad scope into account.
838 Args:
839 input_api: Bag of input related interfaces.
840 output_api: Bag of output related interfaces.
841 excluded_paths: Don't include these paths in common checks.
842 text_files: Which file are to be treated as documentation text files.
843 license_header: What license header should be on files.
844 project_name: What is the name of the project as it appears in the license.
845 Returns:
846 A list of warning or error objects.
847 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000848 excluded_paths = tuple(excluded_paths or [])
849 text_files = tuple(text_files or (
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +0000850 r'.+\.txt$',
851 r'.+\.json$',
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000852 ))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000853 project_name = project_name or 'Chromium'
854 license_header = license_header or (
855 r'.*? Copyright \(c\) %(year)s The %(project)s Authors\. '
856 r'All rights reserved\.\n'
857 r'.*? Use of this source code is governed by a BSD-style license that '
858 r'can be\n'
dbeam@chromium.orgb2312102012-02-15 02:01:55 +0000859 r'.*? found in the LICENSE file\.(?: \*/)?\n'
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000860 ) % {
dpranke@chromium.org0d1bdea2011-03-24 22:54:38 +0000861 'year': input_api.time.strftime('%Y'),
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000862 'project': project_name,
863 }
864
865 results = []
866 # This code loads the default black list (e.g. third_party, experimental, etc)
867 # and add our black list (breakpad, skia and v8 are still not following
868 # google style and are not really living this repository).
869 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage.
870 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths
871 white_list = input_api.DEFAULT_WHITE_LIST + text_files
872 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list)
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +0000873 text_files = lambda x: input_api.FilterSourceFile(
874 x, black_list=black_list, white_list=white_list)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000875
876 snapshot_memory = []
877 def snapshot(msg):
878 """Measures & prints performance warning if a rule is running slow."""
879 dt2 = input_api.time.clock()
880 if snapshot_memory:
881 delta_ms = int(1000*(dt2 - snapshot_memory[0]))
882 if delta_ms > 500:
883 print " %s took a long time: %dms" % (snapshot_memory[1], delta_ms)
884 snapshot_memory[:] = (dt2, msg)
885
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000886 if owners_check:
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000887 snapshot("checking owners")
bradnelson@google.comd57771d2011-03-31 19:18:32 +0000888 results.extend(input_api.canned_checks.CheckOwners(
dpranke@chromium.org751797a2011-06-07 18:46:27 +0000889 input_api, output_api, source_file_filter=None))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000890
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000891 snapshot("checking long lines")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000892 results.extend(input_api.canned_checks.CheckLongLines(
893 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000894 snapshot( "checking tabs")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000895 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
896 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000897 snapshot( "checking stray whitespace")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000898 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
899 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000900 snapshot("checking nsobjects")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000901 results.extend(_CheckConstNSObject(
902 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000903 snapshot("checking singletons")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000904 results.extend(_CheckSingletonInHeaders(
905 input_api, output_api, source_file_filter=sources))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +0000906
907 # The following checks are only done on commit, since the commit bot will
908 # auto-fix most of these.
909 if input_api.is_committing:
maruel@chromium.org8571dac2011-05-10 18:10:13 +0000910 snapshot("checking eol style")
911 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle(
912 input_api, output_api, source_file_filter=text_files))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +0000913 snapshot("checking svn mime types")
914 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes(
915 input_api, output_api))
916 snapshot("checking license")
917 results.extend(input_api.canned_checks.CheckLicense(
918 input_api, output_api, license_header, source_file_filter=sources))
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +0000919 snapshot("checking was uploaded")
920 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
921 input_api, output_api))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000922 snapshot("done")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000923 return results