blob: 11f18b1f5ef8f316e62a92eaf21bacaf38519731 [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
maruel@chromium.orgff9a2172012-04-24 16:55:32 +00007import os as _os
8_HERE = _os.path.dirname(_os.path.abspath(__file__))
9
tfarina@chromium.orgb6795642014-12-12 00:03:49 +000010# Justifications for each filter:
11#
12# - build/include : Too many; fix in the future.
13# - build/include_order : Not happening; #ifdefed includes.
14# - build/namespace : I'm surprised by how often we violate this rule.
15# - readability/casting : Mistakes a whole bunch of function pointer.
16# - runtime/int : Can be fixed long term; volume of errors too high
17# - runtime/virtual : Broken now, but can be fixed in the future?
18# - whitespace/braces : We have a lot of explicit scoping in chrome code.
tfarina@chromium.orgb6795642014-12-12 00:03:49 +000019DEFAULT_LINT_FILTERS = [
20 '-build/include',
21 '-build/include_order',
22 '-build/namespace',
23 '-readability/casting',
24 '-runtime/int',
25 '-runtime/virtual',
26 '-whitespace/braces',
tfarina@chromium.orgb6795642014-12-12 00:03:49 +000027]
bradnelson@google.com56e48bc2011-03-24 20:51:21 +000028
danakj@chromium.org0ae71222016-01-11 19:37:11 +000029# These filters will always be removed, even if the caller specifies a filter
30# set, as they are problematic or broken in some way.
31#
32# Justifications for each filter:
33# - build/c++11 : Rvalue ref checks are unreliable (false positives),
34# include file and feature blacklists are
35# google3-specific.
36BLACKLIST_LINT_FILTERS = [
37 '-build/c++11',
38]
39
maruel@chromium.org3410d912009-06-09 20:56:16 +000040### Description checks
41
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000042def CheckChangeHasTestField(input_api, output_api):
43 """Requires that the changelist have a TEST= field."""
maruel@chromium.orge1a524f2009-05-27 14:43:46 +000044 if input_api.change.TEST:
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000045 return []
46 else:
47 return [output_api.PresubmitNotifyResult(
jam@chromium.org5c76de92011-01-24 18:19:21 +000048 'If this change requires manual test instructions to QA team, add '
49 'TEST=[instructions].')]
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000050
51
52def CheckChangeHasBugField(input_api, output_api):
53 """Requires that the changelist have a BUG= field."""
maruel@chromium.orge1a524f2009-05-27 14:43:46 +000054 if input_api.change.BUG:
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000055 return []
56 else:
57 return [output_api.PresubmitNotifyResult(
jam@chromium.org5c76de92011-01-24 18:19:21 +000058 'If this change has an associated bug, add BUG=[bug number].')]
kuchhal@chromium.org00c41e42009-05-12 21:43:13 +000059
60
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000061def CheckChangeHasTestedField(input_api, output_api):
62 """Requires that the changelist have a TESTED= field."""
maruel@chromium.orge1a524f2009-05-27 14:43:46 +000063 if input_api.change.TESTED:
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000064 return []
65 else:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000066 return [output_api.PresubmitError('Changelist must have a TESTED= field.')]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000067
68
69def CheckChangeHasQaField(input_api, output_api):
70 """Requires that the changelist have a QA= field."""
71 if input_api.change.QA:
72 return []
73 else:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000074 return [output_api.PresubmitError('Changelist must have a QA= field.')]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000075
76
77def CheckDoNotSubmitInDescription(input_api, output_api):
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +000078 """Checks that the user didn't add 'DO NOT ''SUBMIT' to the CL description.
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000079 """
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +000080 keyword = 'DO NOT ''SUBMIT'
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000081 if keyword in input_api.change.DescriptionText():
82 return [output_api.PresubmitError(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +000083 keyword + ' is present in the changelist description.')]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +000084 else:
85 return []
86
87
maruel@chromium.orgbc50eb42009-06-10 18:22:47 +000088def CheckChangeHasDescription(input_api, output_api):
89 """Checks the CL description is not empty."""
90 text = input_api.change.DescriptionText()
91 if text.strip() == '':
92 if input_api.is_committing:
pgervais@chromium.orgbc3b3b52014-06-03 00:53:48 +000093 return [output_api.PresubmitError('Add a description to the CL.')]
maruel@chromium.orgbc50eb42009-06-10 18:22:47 +000094 else:
pgervais@chromium.orgbc3b3b52014-06-03 00:53:48 +000095 return [output_api.PresubmitNotifyResult('Add a description to the CL.')]
maruel@chromium.orgbc50eb42009-06-10 18:22:47 +000096 return []
97
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +000098
99def CheckChangeWasUploaded(input_api, output_api):
100 """Checks that the issue was uploaded before committing."""
maruel@chromium.orgd587f392011-07-26 00:41:18 +0000101 if input_api.is_committing and not input_api.change.issue:
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +0000102 return [output_api.PresubmitError(
103 'Issue wasn\'t uploaded. Please upload first.')]
104 return []
105
106
maruel@chromium.org3410d912009-06-09 20:56:16 +0000107### Content checks
108
Michael Achenbachc850b962016-12-05 15:40:17 +0100109def CheckAuthorizedAuthor(input_api, output_api):
110 """For non-googler/chromites committers, verify the author's email address is
111 in AUTHORS.
112 """
113 author = input_api.change.author_email
114 if not author:
115 input_api.logging.info('No author, skipping AUTHOR check')
116 return []
117 authors_path = input_api.os_path.join(
118 input_api.PresubmitLocalPath(), 'AUTHORS')
119 valid_authors = (
120 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
121 for line in open(authors_path))
122 valid_authors = [item.group(1).lower() for item in valid_authors if item]
123 if not any(input_api.fnmatch.fnmatch(author.lower(), valid)
124 for valid in valid_authors):
125 input_api.logging.info('Valid authors are %s', ', '.join(valid_authors))
126 return [output_api.PresubmitPromptWarning(
127 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
128 '\n'
129 'http://www.chromium.org/developers/contributing-code and read the '
130 '"Legal" section\n'
131 'If you are a chromite, verify the contributor signed the CLA.') %
132 author)]
133 return []
134
135
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000136def CheckDoNotSubmitInFiles(input_api, output_api):
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000137 """Checks that the user didn't add 'DO NOT ''SUBMIT' to any files."""
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000138 # We want to check every text file, not just source files.
139 file_filter = lambda x : x
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000140 keyword = 'DO NOT ''SUBMIT'
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000141 errors = _FindNewViolationsOfRule(lambda _, line : keyword not in line,
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000142 input_api, file_filter)
143 text = '\n'.join('Found %s in %s' % (keyword, loc) for loc in errors)
144 if text:
145 return [output_api.PresubmitError(text)]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000146 return []
147
148
tfarina@chromium.orgb6795642014-12-12 00:03:49 +0000149def CheckChangeLintsClean(input_api, output_api, source_file_filter=None,
tfarina@chromium.orga62208f2015-02-25 03:23:11 +0000150 lint_filters=None, verbose_level=None):
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000151 """Checks that all '.cc' and '.h' files pass cpplint.py."""
erg@google.com26970fa2009-11-17 18:07:32 +0000152 _RE_IS_TEST = input_api.re.compile(r'.*tests?.(cc|h)$')
153 result = []
154
enne@chromium.orge72c5f52013-04-16 00:36:40 +0000155 cpplint = input_api.cpplint
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000156 # Access to a protected member _XX of a client class
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800157 # pylint: disable=protected-access
erg@google.com26970fa2009-11-17 18:07:32 +0000158 cpplint._cpplint_state.ResetErrorCounts()
159
tfarina@chromium.orgb6795642014-12-12 00:03:49 +0000160 lint_filters = lint_filters or DEFAULT_LINT_FILTERS
danakj@chromium.org0ae71222016-01-11 19:37:11 +0000161 lint_filters.extend(BLACKLIST_LINT_FILTERS)
tfarina@chromium.orgb6795642014-12-12 00:03:49 +0000162 cpplint._SetFilters(','.join(lint_filters))
erg@google.com26970fa2009-11-17 18:07:32 +0000163
164 # We currently are more strict with normal code than unit tests; 4 and 5 are
165 # the verbosity level that would normally be passed to cpplint.py through
166 # --verbose=#. Hopefully, in the future, we can be more verbose.
167 files = [f.AbsoluteLocalPath() for f in
168 input_api.AffectedSourceFiles(source_file_filter)]
169 for file_name in files:
170 if _RE_IS_TEST.match(file_name):
171 level = 5
172 else:
173 level = 4
174
tfarina@chromium.orga62208f2015-02-25 03:23:11 +0000175 verbose_level = verbose_level or level
176 cpplint.ProcessFile(file_name, verbose_level)
erg@google.com26970fa2009-11-17 18:07:32 +0000177
178 if cpplint._cpplint_state.error_count > 0:
179 if input_api.is_committing:
180 res_type = output_api.PresubmitError
181 else:
182 res_type = output_api.PresubmitPromptWarning
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000183 result = [res_type('Changelist failed cpplint.py check.')]
erg@google.com26970fa2009-11-17 18:07:32 +0000184
185 return result
186
187
maruel@chromium.org3410d912009-06-09 20:56:16 +0000188def CheckChangeHasNoCR(input_api, output_api, source_file_filter=None):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000189 """Checks no '\r' (CR) character is in any source files."""
190 cr_files = []
maruel@chromium.org3410d912009-06-09 20:56:16 +0000191 for f in input_api.AffectedSourceFiles(source_file_filter):
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000192 if '\r' in input_api.ReadFile(f, 'rb'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000193 cr_files.append(f.LocalPath())
194 if cr_files:
195 return [output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000196 'Found a CR character in these files:', items=cr_files)]
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000197 return []
198
199
200def CheckChangeHasOnlyOneEol(input_api, output_api, source_file_filter=None):
201 """Checks the files ends with one and only one \n (LF)."""
202 eof_files = []
203 for f in input_api.AffectedSourceFiles(source_file_filter):
204 contents = input_api.ReadFile(f, 'rb')
205 # Check that the file ends in one and only one newline character.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000206 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000207 eof_files.append(f.LocalPath())
208
209 if eof_files:
210 return [output_api.PresubmitPromptWarning(
211 'These files should end in one (and only one) newline character:',
212 items=eof_files)]
213 return []
214
215
216def CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api,
217 source_file_filter=None):
218 """Runs both CheckChangeHasNoCR and CheckChangeHasOnlyOneEOL in one pass.
219
220 It is faster because it is reading the file only once.
221 """
222 cr_files = []
223 eof_files = []
224 for f in input_api.AffectedSourceFiles(source_file_filter):
225 contents = input_api.ReadFile(f, 'rb')
226 if '\r' in contents:
227 cr_files.append(f.LocalPath())
228 # Check that the file ends in one and only one newline character.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000229 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000230 eof_files.append(f.LocalPath())
231 outputs = []
232 if cr_files:
233 outputs.append(output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000234 'Found a CR character in these files:', items=cr_files))
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000235 if eof_files:
236 outputs.append(output_api.PresubmitPromptWarning(
237 'These files should end in one (and only one) newline character:',
238 items=eof_files))
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000239 return outputs
240
seanmccullough0b670442016-06-07 10:45:58 -0700241def CheckGenderNeutral(input_api, output_api, source_file_filter=None):
242 """Checks that there are no gendered pronouns in any of the text files to be
243 submitted.
244 """
245 gendered_re = input_api.re.compile(
246 '(^|\s|\(|\[)([Hh]e|[Hh]is|[Hh]ers?|[Hh]im|[Ss]he|[Gg]uys?)\\b')
247
248 errors = []
249 for f in input_api.AffectedFiles(include_deletes=False,
250 file_filter=source_file_filter):
251 for line_num, line in f.ChangedContents():
252 if gendered_re.search(line):
253 errors.append('%s (%d): %s' % (f.LocalPath(), line_num, line))
254
255 if len(errors):
256 return [output_api.PresubmitPromptWarning('Found a gendered pronoun in:',
257 long_text='\n'.join(errors))]
258 return []
259
260
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000261
chrisha@google.com267d6592012-06-19 19:23:31 +0000262def _ReportErrorFileAndLine(filename, line_num, dummy_line):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000263 """Default error formatter for _FindNewViolationsOfRule."""
danakj@chromium.orgc5965ba2013-08-14 00:27:24 +0000264 return '%s:%s' % (filename, line_num)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000265
266
267def _FindNewViolationsOfRule(callable_rule, input_api, source_file_filter=None,
268 error_formatter=_ReportErrorFileAndLine):
269 """Find all newly introduced violations of a per-line rule (a callable).
270
271 Arguments:
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000272 callable_rule: a callable taking a file extension and line of input and
273 returning True if the rule is satisfied and False if there was a problem.
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000274 input_api: object to enumerate the affected files.
275 source_file_filter: a filter to be passed to the input api.
276 error_formatter: a callable taking (filename, line_number, line) and
277 returning a formatted error string.
278
279 Returns:
280 A list of the newly-introduced violations reported by the rule.
281 """
282 errors = []
sail@chromium.org5538e022011-05-12 17:53:16 +0000283 for f in input_api.AffectedFiles(include_deletes=False,
284 file_filter=source_file_filter):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000285 # For speed, we do two passes, checking first the full file. Shelling out
286 # to the SCM to determine the changed region can be quite expensive on
287 # Win32. Assuming that most files will be kept problem-free, we can
288 # skip the SCM operations most of the time.
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000289 extension = str(f.LocalPath()).rsplit('.', 1)[-1]
290 if all(callable_rule(extension, line) for line in f.NewContents()):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000291 continue # No violation found in full text: can skip considering diff.
292
293 for line_num, line in f.ChangedContents():
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000294 if not callable_rule(extension, line):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000295 errors.append(error_formatter(f.LocalPath(), line_num, line))
296
297 return errors
298
299
maruel@chromium.org3410d912009-06-09 20:56:16 +0000300def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000301 """Checks that there are no tab characters in any of the text files to be
302 submitted.
303 """
maruel@chromium.org115ae6c2010-06-18 17:11:43 +0000304 # In addition to the filter, make sure that makefiles are blacklisted.
305 if not source_file_filter:
306 # It's the default filter.
307 source_file_filter = input_api.FilterSourceFile
308 def filter_more(affected_file):
cmp@chromium.orgcb5e57c2012-04-06 19:50:15 +0000309 basename = input_api.os_path.basename(affected_file.LocalPath())
310 return (not (basename in ('Makefile', 'makefile') or
311 basename.endswith('.mk')) and
maruel@chromium.org115ae6c2010-06-18 17:11:43 +0000312 source_file_filter(affected_file))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000313
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000314 tabs = _FindNewViolationsOfRule(lambda _, line : '\t' not in line,
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000315 input_api, filter_more)
316
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000317 if tabs:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000318 return [output_api.PresubmitPromptWarning('Found a tab character in:',
319 long_text='\n'.join(tabs))]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000320 return []
321
322
estade@chromium.orgfdcc9f72011-02-07 22:25:07 +0000323def CheckChangeTodoHasOwner(input_api, output_api, source_file_filter=None):
324 """Checks that the user didn't add TODO(name) without an owner."""
325
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000326 unowned_todo = input_api.re.compile('TO''DO[^(]')
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000327 errors = _FindNewViolationsOfRule(lambda _, x : not unowned_todo.search(x),
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000328 input_api, source_file_filter)
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000329 errors = ['Found TO''DO with no owner in ' + x for x in errors]
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000330 if errors:
331 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
estade@chromium.orgfdcc9f72011-02-07 22:25:07 +0000332 return []
333
334
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000335def CheckChangeHasNoStrayWhitespace(input_api, output_api,
336 source_file_filter=None):
337 """Checks that there is no stray whitespace at source lines end."""
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000338 errors = _FindNewViolationsOfRule(lambda _, line : line.rstrip() == line,
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000339 input_api, source_file_filter)
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000340 if errors:
341 return [output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000342 'Found line ending with white spaces in:',
343 long_text='\n'.join(errors))]
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000344 return []
345
346
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +0000347def CheckLongLines(input_api, output_api, maxlen, source_file_filter=None):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000348 """Checks that there aren't any lines longer than maxlen characters in any of
349 the text files to be submitted.
350 """
dpranke@chromium.orge3b1c3d2012-10-20 22:28:14 +0000351 maxlens = {
352 'java': 100,
torne@chromium.org0ecad9c2013-02-15 16:35:16 +0000353 # This is specifically for Android's handwritten makefiles (Android.mk).
354 'mk': 200,
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000355 '': maxlen,
356 }
maruel@chromium.orgeba64622011-06-20 18:26:48 +0000357
erikchen@google.com12816082013-12-03 02:04:20 +0000358 # Language specific exceptions to max line length.
359 # '.h' is considered an obj-c file extension, since OBJC_EXCEPTIONS are a
360 # superset of CPP_EXCEPTIONS.
361 CPP_FILE_EXTS = ('c', 'cc')
362 CPP_EXCEPTIONS = ('#define', '#endif', '#if', '#include', '#pragma')
Dave Schuyler6eea7c22017-03-03 18:46:25 -0800363 HTML_FILE_EXTS = ('html',)
364 HTML_EXCEPTIONS = ('<link',)
erikchen@google.com12816082013-12-03 02:04:20 +0000365 JAVA_FILE_EXTS = ('java',)
366 JAVA_EXCEPTIONS = ('import ', 'package ')
dbeam@chromium.org5a2af6d2015-10-08 17:52:29 +0000367 JS_FILE_EXTS = ('js',)
368 JS_EXCEPTIONS = ("GEN('#include",)
erikchen@google.com12816082013-12-03 02:04:20 +0000369 OBJC_FILE_EXTS = ('h', 'm', 'mm')
370 OBJC_EXCEPTIONS = ('#define', '#endif', '#if', '#import', '#include',
371 '#pragma')
dbeam@chromium.org5a2af6d2015-10-08 17:52:29 +0000372 PY_FILE_EXTS = ('py',)
aiolos@chromium.org84033cc2015-07-16 01:27:15 +0000373 PY_EXCEPTIONS = ('import', 'from')
erikchen@google.com12816082013-12-03 02:04:20 +0000374
375 LANGUAGE_EXCEPTIONS = [
376 (CPP_FILE_EXTS, CPP_EXCEPTIONS),
Dave Schuyler6eea7c22017-03-03 18:46:25 -0800377 (HTML_FILE_EXTS, HTML_EXCEPTIONS),
erikchen@google.com12816082013-12-03 02:04:20 +0000378 (JAVA_FILE_EXTS, JAVA_EXCEPTIONS),
dbeam@chromium.org5a2af6d2015-10-08 17:52:29 +0000379 (JS_FILE_EXTS, JS_EXCEPTIONS),
erikchen@google.com12816082013-12-03 02:04:20 +0000380 (OBJC_FILE_EXTS, OBJC_EXCEPTIONS),
aiolos@chromium.org84033cc2015-07-16 01:27:15 +0000381 (PY_FILE_EXTS, PY_EXCEPTIONS),
erikchen@google.com12816082013-12-03 02:04:20 +0000382 ]
sivachandra@chromium.org270db5c2012-10-09 22:38:44 +0000383
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000384 def no_long_lines(file_extension, line):
erikchen@google.com12816082013-12-03 02:04:20 +0000385 # Check for language specific exceptions.
386 if any(file_extension in exts and line.startswith(exceptions)
387 for exts, exceptions in LANGUAGE_EXCEPTIONS):
sivachandra@chromium.org270db5c2012-10-09 22:38:44 +0000388 return True
389
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000390 file_maxlen = maxlens.get(file_extension, maxlens[''])
391 # Stupidly long symbols that needs to be worked around if takes 66% of line.
392 long_symbol = file_maxlen * 2 / 3
393 # Hard line length limit at 50% more.
394 extra_maxlen = file_maxlen * 3 / 2
395
396 line_len = len(line)
397 if line_len <= file_maxlen:
maruel@chromium.orgeba64622011-06-20 18:26:48 +0000398 return True
399
dtu@chromium.org03f0c532015-03-27 22:22:07 +0000400 # Allow long URLs of any length.
treib@chromium.orgde3ee902014-07-28 14:23:11 +0000401 if any((url in line) for url in ('file://', 'http://', 'https://')):
402 return True
403
qyearsley12fa6ff2016-08-24 09:18:40 -0700404 # If 'line-too-long' is explicitly suppressed for the line, any length is
nednguyen@google.com6df90f32015-12-01 20:14:33 +0000405 # acceptable.
406 if 'pylint: disable=line-too-long' in line and file_extension == 'py':
407 return True
408
dtu@chromium.org03f0c532015-03-27 22:22:07 +0000409 if line_len > extra_maxlen:
410 return False
411
treib@chromium.orgde3ee902014-07-28 14:23:11 +0000412 if 'url(' in line and file_extension == 'css':
413 return True
414
dbeam@chromium.orgb5ccc9b2014-09-23 00:42:22 +0000415 if '<include' in line and file_extension in ('css', 'html', 'js'):
416 return True
417
treib@chromium.orgde3ee902014-07-28 14:23:11 +0000418 return input_api.re.match(
419 r'.*[A-Za-z][A-Za-z_0-9]{%d,}.*' % long_symbol, line)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000420
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000421 def format_error(filename, line_num, line):
422 return '%s, line %s, %s chars' % (filename, line_num, len(line))
423
424 errors = _FindNewViolationsOfRule(no_long_lines, input_api,
425 source_file_filter,
426 error_formatter=format_error)
427 if errors:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000428 msg = 'Found lines longer than %s characters (first 5 shown).' % maxlen
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000429 return [output_api.PresubmitPromptWarning(msg, items=errors[:5])]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000430 else:
431 return []
432
433
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000434def CheckLicense(input_api, output_api, license_re, source_file_filter=None,
maruel@chromium.org71626852010-11-03 13:14:25 +0000435 accept_empty_files=True):
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000436 """Verifies the license header.
437 """
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000438 license_re = input_api.re.compile(license_re, input_api.re.MULTILINE)
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000439 bad_files = []
440 for f in input_api.AffectedSourceFiles(source_file_filter):
441 contents = input_api.ReadFile(f, 'rb')
maruel@chromium.org71626852010-11-03 13:14:25 +0000442 if accept_empty_files and not contents:
443 continue
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000444 if not license_re.search(contents):
445 bad_files.append(f.LocalPath())
446 if bad_files:
phajdan.jr@chromium.orge27eb7e2015-11-16 12:47:53 +0000447 return [output_api.PresubmitPromptWarning(
bradnelson@google.com393460b2011-03-29 01:20:26 +0000448 'License must match:\n%s\n' % license_re.pattern +
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000449 'Found a bad license header in these files:', items=bad_files)]
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000450 return []
451
452
maruel@chromium.org3410d912009-06-09 20:56:16 +0000453### Other checks
454
455def CheckDoNotSubmit(input_api, output_api):
456 return (
457 CheckDoNotSubmitInDescription(input_api, output_api) +
458 CheckDoNotSubmitInFiles(input_api, output_api)
459 )
460
461
bradnelson@google.comc0b332a2010-08-26 00:30:37 +0000462def CheckTreeIsOpen(input_api, output_api,
463 url=None, closed=None, json_url=None):
464 """Check whether to allow commit without prompt.
465
466 Supports two styles:
467 1. Checks that an url's content doesn't match a regexp that would mean that
468 the tree is closed. (old)
469 2. Check the json_url to decide whether to allow commit without prompt.
470 Args:
471 input_api: input related apis.
472 output_api: output related apis.
473 url: url to use for regex based tree status.
474 closed: regex to match for closed status.
475 json_url: url to download json style status.
476 """
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000477 if not input_api.is_committing:
478 return []
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000479 try:
bradnelson@google.comc0b332a2010-08-26 00:30:37 +0000480 if json_url:
481 connection = input_api.urllib2.urlopen(json_url)
482 status = input_api.json.loads(connection.read())
483 connection.close()
484 if not status['can_commit_freely']:
485 short_text = 'Tree state is: ' + status['general_state']
486 long_text = status['message'] + '\n' + json_url
487 return [output_api.PresubmitError(short_text, long_text=long_text)]
488 else:
489 # TODO(bradnelson): drop this once all users are gone.
490 connection = input_api.urllib2.urlopen(url)
491 status = connection.read()
492 connection.close()
493 if input_api.re.match(closed, status):
494 long_text = status + '\n' + url
495 return [output_api.PresubmitError('The tree is closed.',
496 long_text=long_text)]
rohitrao@chromium.orgd490ee82012-02-06 19:31:33 +0000497 except IOError as e:
498 return [output_api.PresubmitError('Error fetching tree status.',
499 long_text=str(e))]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000500 return []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000501
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000502def GetUnitTestsInDirectory(
smut@google.comac296202014-04-24 21:47:17 +0000503 input_api, output_api, directory, whitelist=None, blacklist=None, env=None):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000504 """Lists all files in a directory and runs them. Doesn't recurse.
505
nick@chromium.orgff526192013-06-10 19:30:26 +0000506 It's mainly a wrapper for RunUnitTests. Use whitelist and blacklist to filter
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000507 tests accordingly.
508 """
509 unit_tests = []
510 test_path = input_api.os_path.abspath(
511 input_api.os_path.join(input_api.PresubmitLocalPath(), directory))
512
513 def check(filename, filters):
514 return any(True for i in filters if input_api.re.match(i, filename))
515
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000516 to_run = found = 0
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000517 for filename in input_api.os_listdir(test_path):
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000518 found += 1
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000519 fullpath = input_api.os_path.join(test_path, filename)
520 if not input_api.os_path.isfile(fullpath):
521 continue
522 if whitelist and not check(filename, whitelist):
523 continue
524 if blacklist and check(filename, blacklist):
525 continue
526 unit_tests.append(input_api.os_path.join(directory, filename))
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000527 to_run += 1
anatoly techtonikbdcdc592017-03-29 17:00:13 +0300528 input_api.logging.debug('Found %d files, running %d unit tests'
529 % (found, to_run))
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000530 if not to_run:
531 return [
532 output_api.PresubmitPromptWarning(
533 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
534 % (found, whitelist, blacklist, directory))
535 ]
smut@google.comac296202014-04-24 21:47:17 +0000536 return GetUnitTests(input_api, output_api, unit_tests, env)
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000537
538
smut@google.comac296202014-04-24 21:47:17 +0000539def GetUnitTests(input_api, output_api, unit_tests, env=None):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000540 """Runs all unit tests in a directory.
541
542 On Windows, sys.executable is used for unit tests ending with ".py".
543 """
544 # We don't want to hinder users from uploading incomplete patches.
545 if input_api.is_committing:
546 message_type = output_api.PresubmitError
547 else:
548 message_type = output_api.PresubmitPromptWarning
549
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000550 results = []
551 for unit_test in unit_tests:
552 cmd = []
553 if input_api.platform == 'win32' and unit_test.endswith('.py'):
554 # Windows needs some help.
555 cmd = [input_api.python_executable]
556 cmd.append(unit_test)
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000557 if input_api.verbose:
maruel@chromium.org6c7723e2011-04-12 19:04:55 +0000558 cmd.append('--verbose')
smut@google.comac296202014-04-24 21:47:17 +0000559 kwargs = {'cwd': input_api.PresubmitLocalPath()}
560 if env:
561 kwargs['env'] = env
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000562 results.append(input_api.Command(
563 name=unit_test,
564 cmd=cmd,
smut@google.comac296202014-04-24 21:47:17 +0000565 kwargs=kwargs,
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000566 message=message_type))
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000567 return results
568
nick@chromium.orgff526192013-06-10 19:30:26 +0000569
agable@chromium.org40a3d0b2014-05-15 01:59:16 +0000570def GetUnitTestsRecursively(input_api, output_api, directory,
571 whitelist, blacklist):
572 """Gets all files in the directory tree (git repo) that match the whitelist.
573
574 Restricts itself to only find files within the Change's source repo, not
575 dependencies.
576 """
577 def check(filename):
578 return (any(input_api.re.match(f, filename) for f in whitelist) and
579 not any(input_api.re.match(f, filename) for f in blacklist))
580
581 tests = []
582
583 to_run = found = 0
584 for filepath in input_api.change.AllFiles(directory):
585 found += 1
586 if check(filepath):
587 to_run += 1
588 tests.append(filepath)
589 input_api.logging.debug('Found %d files, running %d' % (found, to_run))
590 if not to_run:
591 return [
592 output_api.PresubmitPromptWarning(
593 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
594 % (found, whitelist, blacklist, directory))
595 ]
596
597 return GetUnitTests(input_api, output_api, tests)
598
599
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000600def GetPythonUnitTests(input_api, output_api, unit_tests):
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000601 """Run the unit tests out of process, capture the output and use the result
602 code to determine success.
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000603
604 DEPRECATED.
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000605 """
maruel@chromium.orgd7dccf52009-06-06 18:51:58 +0000606 # We don't want to hinder users from uploading incomplete patches.
607 if input_api.is_committing:
608 message_type = output_api.PresubmitError
609 else:
610 message_type = output_api.PresubmitNotifyResult
maruel@chromium.org0e766052011-04-06 13:32:51 +0000611 results = []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000612 for unit_test in unit_tests:
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000613 # Run the unit tests out of process. This is because some unit tests
614 # stub out base libraries and don't clean up their mess. It's too easy to
615 # get subtle bugs.
616 cwd = None
617 env = None
618 unit_test_name = unit_test
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000619 # 'python -m test.unit_test' doesn't work. We need to change to the right
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000620 # directory instead.
621 if '.' in unit_test:
622 # Tests imported in submodules (subdirectories) assume that the current
623 # directory is in the PYTHONPATH. Manually fix that.
624 unit_test = unit_test.replace('.', '/')
625 cwd = input_api.os_path.dirname(unit_test)
626 unit_test = input_api.os_path.basename(unit_test)
627 env = input_api.environ.copy()
kbr@google.comab318592009-09-04 00:54:55 +0000628 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH
629 backpath = [
630 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1))
631 ]
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000632 if env.get('PYTHONPATH'):
633 backpath.append(env.get('PYTHONPATH'))
ukai@chromium.orga301f1f2009-08-05 10:37:33 +0000634 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000635 cmd = [input_api.python_executable, '-m', '%s' % unit_test]
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000636 results.append(input_api.Command(
637 name=unit_test_name,
638 cmd=cmd,
639 kwargs={'env': env, 'cwd': cwd},
640 message=message_type))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000641 return results
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000642
643
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000644def RunUnitTestsInDirectory(input_api, *args, **kwargs):
645 """Run tests in a directory serially.
646
647 For better performance, use GetUnitTestsInDirectory and then
648 pass to input_api.RunTests.
649 """
650 return input_api.RunTests(
651 GetUnitTestsInDirectory(input_api, *args, **kwargs), False)
652
653
654def RunUnitTests(input_api, *args, **kwargs):
655 """Run tests serially.
656
657 For better performance, use GetUnitTests and then pass to
658 input_api.RunTests.
659 """
660 return input_api.RunTests(GetUnitTests(input_api, *args, **kwargs), False)
661
662
663def RunPythonUnitTests(input_api, *args, **kwargs):
664 """Run python tests in a directory serially.
665
666 DEPRECATED
667 """
668 return input_api.RunTests(
669 GetPythonUnitTests(input_api, *args, **kwargs), False)
670
671
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000672def _FetchAllFiles(input_api, white_list, black_list):
673 """Hack to fetch all files."""
674 # We cannot use AffectedFiles here because we want to test every python
675 # file on each single python change. It's because a change in a python file
676 # can break another unmodified file.
677 # Use code similar to InputApi.FilterSourceFile()
678 def Find(filepath, filters):
anatoly techtonikbdcdc592017-03-29 17:00:13 +0300679 if input_api.platform == 'win32':
680 filepath = filepath.replace('\\', '/')
681
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000682 for item in filters:
683 if input_api.re.match(item, filepath):
684 return True
685 return False
686
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000687 files = []
688 path_len = len(input_api.PresubmitLocalPath())
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000689 for dirpath, dirnames, filenames in input_api.os_walk(
690 input_api.PresubmitLocalPath()):
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000691 # Passes dirnames in black list to speed up search.
692 for item in dirnames[:]:
693 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
694 if Find(filepath, black_list):
695 dirnames.remove(item)
696 for item in filenames:
697 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
698 if Find(filepath, white_list) and not Find(filepath, black_list):
699 files.append(filepath)
700 return files
701
702
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000703def GetPylint(input_api, output_api, white_list=None, black_list=None,
dtu@chromium.orgf23524d2016-01-27 20:08:49 +0000704 disabled_warnings=None, extra_paths_list=None, pylintrc=None):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000705 """Run pylint on python files.
706
chrisha@google.com267d6592012-06-19 19:23:31 +0000707 The default white_list enforces looking only at *.py files.
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000708 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000709 white_list = tuple(white_list or ('.*\.py$',))
710 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000711 extra_paths_list = extra_paths_list or []
712
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000713 if input_api.is_committing:
714 error_type = output_api.PresubmitError
715 else:
716 error_type = output_api.PresubmitPromptWarning
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000717
718 # Only trigger if there is at least one python file affected.
ilevy@chromium.org36576332013-01-08 03:16:15 +0000719 def rel_path(regex):
720 """Modifies a regex for a subject to accept paths relative to root."""
chrisha@chromium.org40e5d3d2013-01-18 17:46:57 +0000721 def samefile(a, b):
722 # Default implementation for platforms lacking os.path.samefile
723 # (like Windows).
724 return input_api.os_path.abspath(a) == input_api.os_path.abspath(b)
725 samefile = getattr(input_api.os_path, 'samefile', samefile)
726 if samefile(input_api.PresubmitLocalPath(),
727 input_api.change.RepositoryRoot()):
ilevy@chromium.orgdd4e9312013-01-15 03:22:04 +0000728 return regex
chrisha@chromium.org40e5d3d2013-01-18 17:46:57 +0000729
ilevy@chromium.org36576332013-01-08 03:16:15 +0000730 prefix = input_api.os_path.join(input_api.os_path.relpath(
731 input_api.PresubmitLocalPath(), input_api.change.RepositoryRoot()), '')
732 return input_api.re.escape(prefix) + regex
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000733 src_filter = lambda x: input_api.FilterSourceFile(
734 x, map(rel_path, white_list), map(rel_path, black_list))
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000735 if not input_api.AffectedSourceFiles(src_filter):
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000736 input_api.logging.info('Skipping pylint: no matching changes.')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000737 return []
738
agable@chromium.org327d72b2015-04-21 20:22:50 +0000739 if pylintrc is not None:
740 pylintrc = input_api.os_path.join(input_api.PresubmitLocalPath(), pylintrc)
741 else:
742 pylintrc = input_api.os_path.join(_HERE, 'pylintrc')
dtu@chromium.orgf23524d2016-01-27 20:08:49 +0000743 extra_args = ['--rcfile=%s' % pylintrc]
maruel@chromium.orgff9a2172012-04-24 16:55:32 +0000744 if disabled_warnings:
745 extra_args.extend(['-d', ','.join(disabled_warnings)])
746
chrisha@google.com267d6592012-06-19 19:23:31 +0000747 files = _FetchAllFiles(input_api, white_list, black_list)
748 if not files:
maruel@chromium.orgfca53392010-12-21 18:42:57 +0000749 return []
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000750 files.sort()
chrisha@google.com267d6592012-06-19 19:23:31 +0000751
csharp@chromium.org40395342013-02-21 14:57:23 +0000752 input_api.logging.info('Running pylint on %d files', len(files))
753 input_api.logging.debug('Running pylint on: %s', files)
chrisha@google.com267d6592012-06-19 19:23:31 +0000754 # Copy the system path to the environment so pylint can find the right
755 # imports.
756 env = input_api.environ.copy()
757 import sys
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000758 env['PYTHONPATH'] = input_api.os_path.pathsep.join(
robertshield@chromium.orga2873932013-02-20 18:08:46 +0000759 extra_paths_list + sys.path).encode('utf8')
chrisha@google.com267d6592012-06-19 19:23:31 +0000760
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000761 def GetPylintCmd(flist, extra, parallel):
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000762 # Windows needs help running python files so we explicitly specify
763 # the interpreter to use. It also has limitations on the size of
764 # the command-line, so we pass arguments via a pipe.
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000765 cmd = [input_api.python_executable,
766 input_api.os_path.join(_HERE, 'third_party', 'pylint.py'),
767 '--args-on-stdin']
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000768 if len(flist) == 1:
769 description = flist[0]
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000770 else:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000771 description = '%s files' % len(flist)
chrisha@chromium.org1b129e52012-06-22 17:08:11 +0000772
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000773 args = extra_args[:]
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000774 if extra:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000775 args.extend(extra)
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000776 description += ' using %s' % (extra,)
777 if parallel:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000778 args.append('--jobs=%s' % input_api.cpu_count)
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000779 description += ' on %d cores' % input_api.cpu_count
780
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000781 return input_api.Command(
782 name='Pylint (%s)' % description,
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000783 cmd=cmd,
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000784 kwargs={'env': env, 'stdin': '\n'.join(args + flist)},
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000785 message=error_type)
chrisha@chromium.org1b129e52012-06-22 17:08:11 +0000786
sbc@chromium.org7fc75ca2012-09-21 20:14:21 +0000787 # Always run pylint and pass it all the py files at once.
788 # Passing py files one at time is slower and can produce
789 # different results. input_api.verbose used to be used
790 # to enable this behaviour but differing behaviour in
791 # verbose mode is not desirable.
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000792 # Leave this unreachable code in here so users can make
793 # a quick local edit to diagnose pylint issues more
794 # easily.
sbc@chromium.org7fc75ca2012-09-21 20:14:21 +0000795 if True:
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000796 # pylint's cycle detection doesn't work in parallel, so spawn a second,
797 # single-threaded job for just that check.
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000798
799 # Some PRESUBMITs explicitly mention cycle detection.
800 if not any('R0401' in a or 'cyclic-import' in a for a in extra_args):
801 return [
802 GetPylintCmd(files, ["--disable=cyclic-import"], True),
803 GetPylintCmd(files, ["--disable=all", "--enable=cyclic-import"], False)
804 ]
805 else:
806 return [ GetPylintCmd(files, [], True) ]
807
chrisha@google.com267d6592012-06-19 19:23:31 +0000808 else:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000809 return map(lambda x: GetPylintCmd([x], [], 1), files)
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000810
811
812def RunPylint(input_api, *args, **kwargs):
813 """Legacy presubmit function.
814
815 For better performance, get all tests and then pass to
816 input_api.RunTests.
817 """
818 return input_api.RunTests(GetPylint(input_api, *args, **kwargs), False)
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000819
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000820
tandrii@chromium.org5f30f2f2016-04-27 13:19:57 +0000821def CheckRietveldTryJobExecution(dummy_input_api, output_api,
chrisha@google.com267d6592012-06-19 19:23:31 +0000822 dummy_host_url, dummy_platforms,
823 dummy_owner):
tandrii@chromium.org5f30f2f2016-04-27 13:19:57 +0000824 return [
825 output_api.PresubmitNotifyResult(
826 'CheckRietveldTryJobExecution is deprecated, please remove it.')
827 ]
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000828
829
830def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
831 ignored):
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000832 try:
833 connection = input_api.urllib2.urlopen(url)
834 raw_data = connection.read()
835 connection.close()
836 except IOError:
837 return [output_api.PresubmitNotifyResult('%s is not accessible' % url)]
838
839 try:
840 data = input_api.json.loads(raw_data)
841 except ValueError:
842 return [output_api.PresubmitNotifyResult('Received malformed json while '
843 'looking up buildbot status')]
844
845 out = []
846 for (builder_name, builder) in data.iteritems():
847 if builder_name in ignored:
848 continue
maruel@chromium.orgcf1982c2010-10-04 15:08:28 +0000849 if builder.get('state', '') == 'offline':
850 continue
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000851 pending_builds_len = len(builder.get('pending_builds', []))
852 if pending_builds_len > max_pendings:
853 out.append('%s has %d build(s) pending' %
854 (builder_name, pending_builds_len))
855 if out:
856 return [output_api.PresubmitPromptWarning(
857 'Build(s) pending. It is suggested to wait that no more than %d '
858 'builds are pending.' % max_pendings,
859 long_text='\n'.join(out))]
860 return []
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000861
862
dpranke@chromium.orgbce925d2015-01-16 22:38:38 +0000863def CheckOwners(input_api, output_api, source_file_filter=None):
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000864 if input_api.is_committing:
865 if input_api.tbr:
866 return [output_api.PresubmitNotifyResult(
867 '--tbr was specified, skipping OWNERS check')]
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100868 needed = 'LGTM from an OWNER'
869 output_fn = output_api.PresubmitError
rmistry@google.com5fc6c8c2015-04-16 16:38:43 +0000870 if input_api.change.issue:
tandrii@chromium.org9dea2ac2016-04-28 06:26:20 +0000871 if input_api.dry_run:
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100872 output_fn = lambda text: output_api.PresubmitNotifyResult(
873 'This is a dry run, but these failures would be reported on ' +
874 'commit:\n' + text)
rmistry@google.com5fc6c8c2015-04-16 16:38:43 +0000875 else:
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000876 return [output_api.PresubmitError("OWNERS check failed: this change has "
877 "no Rietveld issue number, so we can't check it for approvals.")]
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000878 else:
879 needed = 'OWNER reviewers'
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100880 output_fn = output_api.PresubmitNotifyResult
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000881
dpranke@chromium.orgadd5df42011-03-08 23:04:01 +0000882 affected_files = set([f.LocalPath() for f in
Dirk Pranke3c86cee2017-01-23 22:02:06 -0800883 input_api.change.AffectedFiles(file_filter=source_file_filter)])
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000884
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000885 owners_db = input_api.owners_db
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000886 owner_email, reviewers = GetCodereviewOwnerAndReviewers(
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000887 input_api,
888 owners_db.email_regexp,
889 approval_needed=input_api.is_committing)
890
dpranke@chromium.orgf6ddfa42013-03-05 21:06:03 +0000891 owner_email = owner_email or input_api.change.author_email
892
dpranke@chromium.orgbce925d2015-01-16 22:38:38 +0000893 if owner_email:
dpranke@chromium.org4c7ce992013-03-06 17:15:40 +0000894 reviewers_plus_owner = set([owner_email]).union(reviewers)
dpranke@chromium.orgdbf8b4e2013-02-28 19:24:16 +0000895 missing_files = owners_db.files_not_covered_by(affected_files,
dpranke@chromium.orgf6ddfa42013-03-05 21:06:03 +0000896 reviewers_plus_owner)
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000897 else:
dpranke@chromium.orgdbf8b4e2013-02-28 19:24:16 +0000898 missing_files = owners_db.files_not_covered_by(affected_files, reviewers)
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000899
dpranke@chromium.org6b1e3ee2013-02-23 00:06:38 +0000900 if missing_files:
zork@chromium.org046e1752012-05-07 05:56:12 +0000901 output_list = [
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100902 output_fn('Missing %s for these files:\n %s' %
903 (needed, '\n '.join(sorted(missing_files))))]
zork@chromium.org046e1752012-05-07 05:56:12 +0000904 if not input_api.is_committing:
dpranke@chromium.org31b42c02013-09-19 19:24:15 +0000905 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
Jochen Eisinger76f5fc62017-04-07 16:27:46 +0200906 finder = input_api.owners_finder(missing_files,
907 input_api.change.RepositoryRoot(),
908 owner_email,
909 fopen=file, os_path=input_api.os_path,
910 email_postfix='', disable_color=True)
911 owners_with_comments = []
912 def RecordComments(text):
913 owners_with_comments.append(finder.print_indent() + text)
914 finder.writeln = RecordComments
915 for owner in suggested_owners:
916 finder.print_comments(owner)
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100917 output_list.append(output_fn('Suggested OWNERS: ' +
scheib@chromium.org93276ab2013-10-14 23:55:32 +0000918 '(Use "git-cl owners" to interactively select owners.)\n %s' %
Jochen Eisinger76f5fc62017-04-07 16:27:46 +0200919 ('\n '.join(owners_with_comments))))
zork@chromium.org046e1752012-05-07 05:56:12 +0000920 return output_list
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000921
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000922 if input_api.is_committing and not reviewers:
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100923 return [output_fn('Missing LGTM from someone other than %s' % owner_email)]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000924 return []
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000925
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000926def GetCodereviewOwnerAndReviewers(input_api, email_regexp, approval_needed):
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000927 """Return the owner and reviewers of a change, if any.
928
929 If approval_needed is True, only reviewers who have approved the change
930 will be returned.
931 """
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000932 # Rietveld is default.
933 func = _RietveldOwnerAndReviewers
934 if input_api.gerrit:
935 func = _GerritOwnerAndReviewers
936 return func(input_api, email_regexp, approval_needed)
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000937
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000938
ilevy@chromium.org1fa5cb92012-12-05 04:04:42 +0000939def _GetRietveldIssueProps(input_api, messages):
940 """Gets the issue properties from rietveld."""
941 issue = input_api.change.issue
942 if issue and input_api.rietveld:
943 return input_api.rietveld.get_issue_properties(
944 issue=int(issue), messages=messages)
945
946
isherman@chromium.orgb5cded62014-03-25 17:47:57 +0000947def _ReviewersFromChange(change):
948 """Return the reviewers specified in the |change|, if any."""
949 reviewers = set()
950 if change.R:
951 reviewers.update(set([r.strip() for r in change.R.split(',')]))
952 if change.TBR:
953 reviewers.update(set([r.strip() for r in change.TBR.split(',')]))
isherman@chromium.org103ae032014-04-09 09:06:19 +0000954
955 # Drop reviewers that aren't specified in email address format.
956 return set(reviewer for reviewer in reviewers if '@' in reviewer)
isherman@chromium.orgb5cded62014-03-25 17:47:57 +0000957
958
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000959def _match_reviewer_email(r, owner_email, email_regexp):
960 return email_regexp.match(r) and r != owner_email
961
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000962def _RietveldOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
963 """Return the owner and reviewers of a change, if any.
964
965 If approval_needed is True, only reviewers who have approved the change
966 will be returned.
967 """
ilevy@chromium.org1fa5cb92012-12-05 04:04:42 +0000968 issue_props = _GetRietveldIssueProps(input_api, True)
969 if not issue_props:
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000970 return None, (set() if approval_needed else
971 _ReviewersFromChange(input_api.change))
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000972
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000973 if not approval_needed:
974 return issue_props['owner_email'], set(issue_props['reviewers'])
975
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000976 owner_email = issue_props['owner_email']
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000977
maruel@chromium.org80941c22011-05-30 20:14:18 +0000978 messages = issue_props.get('messages', [])
979 approvers = set(
980 m['sender'] for m in messages
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000981 if m.get('approval') and _match_reviewer_email(m['sender'], owner_email,
982 email_regexp))
maruel@chromium.org80941c22011-05-30 20:14:18 +0000983 return owner_email, approvers
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000984
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000985
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000986def _GerritOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
987 """Return the owner and reviewers of a change, if any.
988
989 If approval_needed is True, only reviewers who have approved the change
990 will be returned.
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000991 """
992 issue = input_api.change.issue
993 if not issue:
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000994 return None, (set() if approval_needed else
995 _ReviewersFromChange(input_api.change))
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000996
997 owner_email = input_api.gerrit.GetChangeOwner(issue)
998 reviewers = set(
999 r for r in input_api.gerrit.GetChangeReviewers(issue, approval_needed)
1000 if _match_reviewer_email(r, owner_email, email_regexp))
tandrii81665dc2016-08-29 09:16:19 -07001001 input_api.logging.debug('owner: %s; approvals given by: %s',
1002 owner_email, ', '.join(sorted(reviewers)))
tandrii@chromium.org37b07a72016-04-29 16:42:28 +00001003 return owner_email, reviewers
1004
1005
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001006def _CheckConstNSObject(input_api, output_api, source_file_filter):
1007 """Checks to make sure no objective-c files have |const NSSomeClass*|."""
mark@chromium.orgedf744d2011-03-28 16:45:34 +00001008 pattern = input_api.re.compile(
dominik.rottsches@intel.com34a92d92015-03-25 07:36:05 +00001009 r'(?<!reinterpret_cast<)'
1010 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001011
1012 def objective_c_filter(f):
1013 return (source_file_filter(f) and
mark@chromium.orgedf744d2011-03-28 16:45:34 +00001014 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm'))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001015
1016 files = []
1017 for f in input_api.AffectedSourceFiles(objective_c_filter):
1018 contents = input_api.ReadFile(f)
1019 if pattern.search(contents):
1020 files.append(f)
1021
1022 if files:
1023 if input_api.is_committing:
1024 res_type = output_api.PresubmitPromptWarning
1025 else:
1026 res_type = output_api.PresubmitNotifyResult
1027 return [ res_type('|const NSClass*| is wrong, see ' +
1028 'http://dev.chromium.org/developers/clang-mac',
1029 files) ]
1030 return []
1031
1032
bauerb@chromium.org4cea01e2012-03-20 19:49:05 +00001033def CheckSingletonInHeaders(input_api, output_api, source_file_filter=None):
glider@chromium.orgc3617f32015-02-19 16:33:33 +00001034 """Deprecated, must be removed."""
1035 return [
1036 output_api.PresubmitNotifyResult(
1037 'CheckSingletonInHeaders is deprecated, please remove it.')
1038 ]
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001039
1040
1041def PanProjectChecks(input_api, output_api,
1042 excluded_paths=None, text_files=None,
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001043 license_header=None, project_name=None,
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +00001044 owners_check=True, maxlen=80):
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001045 """Checks that ALL chromium orbit projects should use.
1046
1047 These are checks to be run on all Chromium orbit project, including:
1048 Chromium
1049 Native Client
1050 V8
1051 When you update this function, please take this broad scope into account.
1052 Args:
1053 input_api: Bag of input related interfaces.
1054 output_api: Bag of output related interfaces.
1055 excluded_paths: Don't include these paths in common checks.
1056 text_files: Which file are to be treated as documentation text files.
1057 license_header: What license header should be on files.
1058 project_name: What is the name of the project as it appears in the license.
1059 Returns:
1060 A list of warning or error objects.
1061 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +00001062 excluded_paths = tuple(excluded_paths or [])
1063 text_files = tuple(text_files or (
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +00001064 r'.+\.txt$',
1065 r'.+\.json$',
maruel@chromium.org69eaecb2011-06-14 13:09:13 +00001066 ))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001067 project_name = project_name or 'Chromium'
mark@chromium.org2cc66422012-08-07 21:22:32 +00001068
1069 # Accept any year number from 2006 to the current year, or the special
rvargas@chromium.org02652602014-03-14 19:43:20 +00001070 # 2006-20xx string used on the oldest files. 2006-20xx is deprecated, but
1071 # tolerated on old files.
mark@chromium.org2cc66422012-08-07 21:22:32 +00001072 current_year = int(input_api.time.strftime('%Y'))
1073 allowed_years = (str(s) for s in reversed(xrange(2006, current_year + 1)))
rvargas@chromium.org02652602014-03-14 19:43:20 +00001074 years_re = '(' + '|'.join(allowed_years) + '|2006-2008|2006-2009|2006-2010)'
mark@chromium.org2cc66422012-08-07 21:22:32 +00001075
1076 # The (c) is deprecated, but tolerate it until it's removed from all files.
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001077 license_header = license_header or (
mark@chromium.org2cc66422012-08-07 21:22:32 +00001078 r'.*? Copyright (\(c\) )?%(year)s The %(project)s Authors\. '
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001079 r'All rights reserved\.\n'
1080 r'.*? Use of this source code is governed by a BSD-style license that '
1081 r'can be\n'
dbeam@chromium.orgb2312102012-02-15 02:01:55 +00001082 r'.*? found in the LICENSE file\.(?: \*/)?\n'
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001083 ) % {
mark@chromium.org2cc66422012-08-07 21:22:32 +00001084 'year': years_re,
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001085 'project': project_name,
1086 }
1087
1088 results = []
1089 # This code loads the default black list (e.g. third_party, experimental, etc)
1090 # and add our black list (breakpad, skia and v8 are still not following
1091 # google style and are not really living this repository).
1092 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage.
1093 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths
1094 white_list = input_api.DEFAULT_WHITE_LIST + text_files
1095 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list)
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +00001096 text_files = lambda x: input_api.FilterSourceFile(
1097 x, black_list=black_list, white_list=white_list)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001098
1099 snapshot_memory = []
1100 def snapshot(msg):
1101 """Measures & prints performance warning if a rule is running slow."""
1102 dt2 = input_api.time.clock()
1103 if snapshot_memory:
1104 delta_ms = int(1000*(dt2 - snapshot_memory[0]))
1105 if delta_ms > 500:
1106 print " %s took a long time: %dms" % (snapshot_memory[1], delta_ms)
1107 snapshot_memory[:] = (dt2, msg)
1108
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001109 if owners_check:
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001110 snapshot("checking owners")
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001111 results.extend(input_api.canned_checks.CheckOwners(
Dirk Pranke3c86cee2017-01-23 22:02:06 -08001112 input_api, output_api, source_file_filter=None))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001113
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001114 snapshot("checking long lines")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001115 results.extend(input_api.canned_checks.CheckLongLines(
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +00001116 input_api, output_api, maxlen, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001117 snapshot( "checking tabs")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001118 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
1119 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001120 snapshot( "checking stray whitespace")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001121 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
1122 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001123 snapshot("checking nsobjects")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001124 results.extend(_CheckConstNSObject(
1125 input_api, output_api, source_file_filter=sources))
phajdan.jr@chromium.orgd965db32015-11-16 09:46:56 +00001126 snapshot("checking license")
1127 results.extend(input_api.canned_checks.CheckLicense(
1128 input_api, output_api, license_header, source_file_filter=sources))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +00001129
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +00001130 if input_api.is_committing:
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +00001131 snapshot("checking was uploaded")
1132 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
1133 input_api, output_api))
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +00001134 snapshot("checking description")
1135 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1136 input_api, output_api))
1137 results.extend(input_api.canned_checks.CheckDoNotSubmitInDescription(
1138 input_api, output_api))
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +00001139 snapshot("checking do not submit in files")
1140 results.extend(input_api.canned_checks.CheckDoNotSubmitInFiles(
1141 input_api, output_api))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001142 snapshot("done")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001143 return results
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001144
1145
Christopher Lamc5ba6922017-01-24 11:19:14 +11001146def CheckPatchFormatted(input_api, output_api, check_js=False):
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001147 import git_cl
Christopher Lamc5ba6922017-01-24 11:19:14 +11001148 cmd = ['cl', 'format', '--dry-run']
1149 if check_js:
1150 cmd.append('--js')
1151 cmd.append(input_api.PresubmitLocalPath())
1152
enne@chromium.org555cfe42014-01-29 18:21:39 +00001153 code, _ = git_cl.RunGitWithCode(cmd, suppress_stderr=True)
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001154 if code == 2:
enne@chromium.orge969cc72015-03-13 18:56:54 +00001155 short_path = input_api.basename(input_api.PresubmitLocalPath())
1156 full_path = input_api.os_path.relpath(input_api.PresubmitLocalPath(),
1157 input_api.change.RepositoryRoot())
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001158 return [output_api.PresubmitPromptWarning(
erg@chromium.orge0a7c5d2015-02-23 20:30:08 +00001159 'The %s directory requires source formatting. '
Christopher Lamc5ba6922017-01-24 11:19:14 +11001160 'Please run git cl format %s%s' %
1161 (short_path, '--js ' if check_js else '', full_path))]
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001162 # As this is just a warning, ignore all other errors if the user
1163 # happens to have a broken clang-format, doesn't use git, etc etc.
1164 return []
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001165
1166
1167def CheckGNFormatted(input_api, output_api):
1168 import gn
1169 affected_files = input_api.AffectedFiles(
1170 include_deletes=False,
1171 file_filter=lambda x: x.LocalPath().endswith('.gn') or
kylechar58edce22016-06-17 06:07:51 -07001172 x.LocalPath().endswith('.gni') or
1173 x.LocalPath().endswith('.typemap'))
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001174 warnings = []
1175 for f in affected_files:
1176 cmd = ['gn', 'format', '--dry-run', f.AbsoluteLocalPath()]
1177 rc = gn.main(cmd)
1178 if rc == 2:
1179 warnings.append(output_api.PresubmitPromptWarning(
brettw4b8ed592016-08-05 16:19:12 -07001180 '%s requires formatting. Please run:\n gn format %s' % (
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001181 f.AbsoluteLocalPath(), f.LocalPath())))
1182 # It's just a warning, so ignore other types of failures assuming they'll be
1183 # caught elsewhere.
1184 return warnings