blob: 2b63cc194af15359d473bd4d82d102ec394a033f [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
528 input_api.logging.debug('Found %d files, running %d' % (found, to_run))
529 if not to_run:
530 return [
531 output_api.PresubmitPromptWarning(
532 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
533 % (found, whitelist, blacklist, directory))
534 ]
smut@google.comac296202014-04-24 21:47:17 +0000535 return GetUnitTests(input_api, output_api, unit_tests, env)
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000536
537
smut@google.comac296202014-04-24 21:47:17 +0000538def GetUnitTests(input_api, output_api, unit_tests, env=None):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000539 """Runs all unit tests in a directory.
540
541 On Windows, sys.executable is used for unit tests ending with ".py".
542 """
543 # 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.PresubmitPromptWarning
548
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000549 results = []
550 for unit_test in unit_tests:
551 cmd = []
552 if input_api.platform == 'win32' and unit_test.endswith('.py'):
553 # Windows needs some help.
554 cmd = [input_api.python_executable]
555 cmd.append(unit_test)
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000556 if input_api.verbose:
maruel@chromium.org6c7723e2011-04-12 19:04:55 +0000557 cmd.append('--verbose')
smut@google.comac296202014-04-24 21:47:17 +0000558 kwargs = {'cwd': input_api.PresubmitLocalPath()}
559 if env:
560 kwargs['env'] = env
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000561 results.append(input_api.Command(
562 name=unit_test,
563 cmd=cmd,
smut@google.comac296202014-04-24 21:47:17 +0000564 kwargs=kwargs,
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000565 message=message_type))
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000566 return results
567
nick@chromium.orgff526192013-06-10 19:30:26 +0000568
agable@chromium.org40a3d0b2014-05-15 01:59:16 +0000569def GetUnitTestsRecursively(input_api, output_api, directory,
570 whitelist, blacklist):
571 """Gets all files in the directory tree (git repo) that match the whitelist.
572
573 Restricts itself to only find files within the Change's source repo, not
574 dependencies.
575 """
576 def check(filename):
577 return (any(input_api.re.match(f, filename) for f in whitelist) and
578 not any(input_api.re.match(f, filename) for f in blacklist))
579
580 tests = []
581
582 to_run = found = 0
583 for filepath in input_api.change.AllFiles(directory):
584 found += 1
585 if check(filepath):
586 to_run += 1
587 tests.append(filepath)
588 input_api.logging.debug('Found %d files, running %d' % (found, to_run))
589 if not to_run:
590 return [
591 output_api.PresubmitPromptWarning(
592 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
593 % (found, whitelist, blacklist, directory))
594 ]
595
596 return GetUnitTests(input_api, output_api, tests)
597
598
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000599def GetPythonUnitTests(input_api, output_api, unit_tests):
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000600 """Run the unit tests out of process, capture the output and use the result
601 code to determine success.
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000602
603 DEPRECATED.
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000604 """
maruel@chromium.orgd7dccf52009-06-06 18:51:58 +0000605 # We don't want to hinder users from uploading incomplete patches.
606 if input_api.is_committing:
607 message_type = output_api.PresubmitError
608 else:
609 message_type = output_api.PresubmitNotifyResult
maruel@chromium.org0e766052011-04-06 13:32:51 +0000610 results = []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000611 for unit_test in unit_tests:
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000612 # Run the unit tests out of process. This is because some unit tests
613 # stub out base libraries and don't clean up their mess. It's too easy to
614 # get subtle bugs.
615 cwd = None
616 env = None
617 unit_test_name = unit_test
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000618 # 'python -m test.unit_test' doesn't work. We need to change to the right
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000619 # directory instead.
620 if '.' in unit_test:
621 # Tests imported in submodules (subdirectories) assume that the current
622 # directory is in the PYTHONPATH. Manually fix that.
623 unit_test = unit_test.replace('.', '/')
624 cwd = input_api.os_path.dirname(unit_test)
625 unit_test = input_api.os_path.basename(unit_test)
626 env = input_api.environ.copy()
kbr@google.comab318592009-09-04 00:54:55 +0000627 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH
628 backpath = [
629 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1))
630 ]
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000631 if env.get('PYTHONPATH'):
632 backpath.append(env.get('PYTHONPATH'))
ukai@chromium.orga301f1f2009-08-05 10:37:33 +0000633 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000634 cmd = [input_api.python_executable, '-m', '%s' % unit_test]
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000635 results.append(input_api.Command(
636 name=unit_test_name,
637 cmd=cmd,
638 kwargs={'env': env, 'cwd': cwd},
639 message=message_type))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000640 return results
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000641
642
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000643def RunUnitTestsInDirectory(input_api, *args, **kwargs):
644 """Run tests in a directory serially.
645
646 For better performance, use GetUnitTestsInDirectory and then
647 pass to input_api.RunTests.
648 """
649 return input_api.RunTests(
650 GetUnitTestsInDirectory(input_api, *args, **kwargs), False)
651
652
653def RunUnitTests(input_api, *args, **kwargs):
654 """Run tests serially.
655
656 For better performance, use GetUnitTests and then pass to
657 input_api.RunTests.
658 """
659 return input_api.RunTests(GetUnitTests(input_api, *args, **kwargs), False)
660
661
662def RunPythonUnitTests(input_api, *args, **kwargs):
663 """Run python tests in a directory serially.
664
665 DEPRECATED
666 """
667 return input_api.RunTests(
668 GetPythonUnitTests(input_api, *args, **kwargs), False)
669
670
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000671def _FetchAllFiles(input_api, white_list, black_list):
672 """Hack to fetch all files."""
673 # We cannot use AffectedFiles here because we want to test every python
674 # file on each single python change. It's because a change in a python file
675 # can break another unmodified file.
676 # Use code similar to InputApi.FilterSourceFile()
677 def Find(filepath, filters):
678 for item in filters:
679 if input_api.re.match(item, filepath):
680 return True
681 return False
682
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000683 files = []
684 path_len = len(input_api.PresubmitLocalPath())
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000685 for dirpath, dirnames, filenames in input_api.os_walk(
686 input_api.PresubmitLocalPath()):
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000687 # Passes dirnames in black list to speed up search.
688 for item in dirnames[:]:
689 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
690 if Find(filepath, black_list):
691 dirnames.remove(item)
692 for item in filenames:
693 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
694 if Find(filepath, white_list) and not Find(filepath, black_list):
695 files.append(filepath)
696 return files
697
698
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000699def GetPylint(input_api, output_api, white_list=None, black_list=None,
dtu@chromium.orgf23524d2016-01-27 20:08:49 +0000700 disabled_warnings=None, extra_paths_list=None, pylintrc=None):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000701 """Run pylint on python files.
702
chrisha@google.com267d6592012-06-19 19:23:31 +0000703 The default white_list enforces looking only at *.py files.
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000704 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000705 white_list = tuple(white_list or ('.*\.py$',))
706 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000707 extra_paths_list = extra_paths_list or []
708
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000709 if input_api.is_committing:
710 error_type = output_api.PresubmitError
711 else:
712 error_type = output_api.PresubmitPromptWarning
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000713
714 # Only trigger if there is at least one python file affected.
ilevy@chromium.org36576332013-01-08 03:16:15 +0000715 def rel_path(regex):
716 """Modifies a regex for a subject to accept paths relative to root."""
chrisha@chromium.org40e5d3d2013-01-18 17:46:57 +0000717 def samefile(a, b):
718 # Default implementation for platforms lacking os.path.samefile
719 # (like Windows).
720 return input_api.os_path.abspath(a) == input_api.os_path.abspath(b)
721 samefile = getattr(input_api.os_path, 'samefile', samefile)
722 if samefile(input_api.PresubmitLocalPath(),
723 input_api.change.RepositoryRoot()):
ilevy@chromium.orgdd4e9312013-01-15 03:22:04 +0000724 return regex
chrisha@chromium.org40e5d3d2013-01-18 17:46:57 +0000725
ilevy@chromium.org36576332013-01-08 03:16:15 +0000726 prefix = input_api.os_path.join(input_api.os_path.relpath(
727 input_api.PresubmitLocalPath(), input_api.change.RepositoryRoot()), '')
728 return input_api.re.escape(prefix) + regex
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000729 src_filter = lambda x: input_api.FilterSourceFile(
730 x, map(rel_path, white_list), map(rel_path, black_list))
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000731 if not input_api.AffectedSourceFiles(src_filter):
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000732 input_api.logging.info('Skipping pylint: no matching changes.')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000733 return []
734
agable@chromium.org327d72b2015-04-21 20:22:50 +0000735 if pylintrc is not None:
736 pylintrc = input_api.os_path.join(input_api.PresubmitLocalPath(), pylintrc)
737 else:
738 pylintrc = input_api.os_path.join(_HERE, 'pylintrc')
dtu@chromium.orgf23524d2016-01-27 20:08:49 +0000739 extra_args = ['--rcfile=%s' % pylintrc]
maruel@chromium.orgff9a2172012-04-24 16:55:32 +0000740 if disabled_warnings:
741 extra_args.extend(['-d', ','.join(disabled_warnings)])
742
chrisha@google.com267d6592012-06-19 19:23:31 +0000743 files = _FetchAllFiles(input_api, white_list, black_list)
744 if not files:
maruel@chromium.orgfca53392010-12-21 18:42:57 +0000745 return []
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000746 files.sort()
chrisha@google.com267d6592012-06-19 19:23:31 +0000747
csharp@chromium.org40395342013-02-21 14:57:23 +0000748 input_api.logging.info('Running pylint on %d files', len(files))
749 input_api.logging.debug('Running pylint on: %s', files)
chrisha@google.com267d6592012-06-19 19:23:31 +0000750 # Copy the system path to the environment so pylint can find the right
751 # imports.
752 env = input_api.environ.copy()
753 import sys
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000754 env['PYTHONPATH'] = input_api.os_path.pathsep.join(
robertshield@chromium.orga2873932013-02-20 18:08:46 +0000755 extra_paths_list + sys.path).encode('utf8')
chrisha@google.com267d6592012-06-19 19:23:31 +0000756
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000757 def GetPylintCmd(flist, extra, parallel):
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000758 # Windows needs help running python files so we explicitly specify
759 # the interpreter to use. It also has limitations on the size of
760 # the command-line, so we pass arguments via a pipe.
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000761 cmd = [input_api.python_executable,
762 input_api.os_path.join(_HERE, 'third_party', 'pylint.py'),
763 '--args-on-stdin']
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000764 if len(flist) == 1:
765 description = flist[0]
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000766 else:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000767 description = '%s files' % len(flist)
chrisha@chromium.org1b129e52012-06-22 17:08:11 +0000768
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000769 args = extra_args[:]
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000770 if extra:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000771 args.extend(extra)
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000772 description += ' using %s' % (extra,)
773 if parallel:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000774 args.append('--jobs=%s' % input_api.cpu_count)
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000775 description += ' on %d cores' % input_api.cpu_count
776
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000777 return input_api.Command(
778 name='Pylint (%s)' % description,
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000779 cmd=cmd,
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000780 kwargs={'env': env, 'stdin': '\n'.join(args + flist)},
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000781 message=error_type)
chrisha@chromium.org1b129e52012-06-22 17:08:11 +0000782
sbc@chromium.org7fc75ca2012-09-21 20:14:21 +0000783 # Always run pylint and pass it all the py files at once.
784 # Passing py files one at time is slower and can produce
785 # different results. input_api.verbose used to be used
786 # to enable this behaviour but differing behaviour in
787 # verbose mode is not desirable.
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000788 # Leave this unreachable code in here so users can make
789 # a quick local edit to diagnose pylint issues more
790 # easily.
sbc@chromium.org7fc75ca2012-09-21 20:14:21 +0000791 if True:
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000792 # pylint's cycle detection doesn't work in parallel, so spawn a second,
793 # single-threaded job for just that check.
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000794
795 # Some PRESUBMITs explicitly mention cycle detection.
796 if not any('R0401' in a or 'cyclic-import' in a for a in extra_args):
797 return [
798 GetPylintCmd(files, ["--disable=cyclic-import"], True),
799 GetPylintCmd(files, ["--disable=all", "--enable=cyclic-import"], False)
800 ]
801 else:
802 return [ GetPylintCmd(files, [], True) ]
803
chrisha@google.com267d6592012-06-19 19:23:31 +0000804 else:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000805 return map(lambda x: GetPylintCmd([x], [], 1), files)
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000806
807
808def RunPylint(input_api, *args, **kwargs):
809 """Legacy presubmit function.
810
811 For better performance, get all tests and then pass to
812 input_api.RunTests.
813 """
814 return input_api.RunTests(GetPylint(input_api, *args, **kwargs), False)
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000815
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000816
tandrii@chromium.org5f30f2f2016-04-27 13:19:57 +0000817def CheckRietveldTryJobExecution(dummy_input_api, output_api,
chrisha@google.com267d6592012-06-19 19:23:31 +0000818 dummy_host_url, dummy_platforms,
819 dummy_owner):
tandrii@chromium.org5f30f2f2016-04-27 13:19:57 +0000820 return [
821 output_api.PresubmitNotifyResult(
822 'CheckRietveldTryJobExecution is deprecated, please remove it.')
823 ]
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000824
825
826def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
827 ignored):
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000828 try:
829 connection = input_api.urllib2.urlopen(url)
830 raw_data = connection.read()
831 connection.close()
832 except IOError:
833 return [output_api.PresubmitNotifyResult('%s is not accessible' % url)]
834
835 try:
836 data = input_api.json.loads(raw_data)
837 except ValueError:
838 return [output_api.PresubmitNotifyResult('Received malformed json while '
839 'looking up buildbot status')]
840
841 out = []
842 for (builder_name, builder) in data.iteritems():
843 if builder_name in ignored:
844 continue
maruel@chromium.orgcf1982c2010-10-04 15:08:28 +0000845 if builder.get('state', '') == 'offline':
846 continue
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000847 pending_builds_len = len(builder.get('pending_builds', []))
848 if pending_builds_len > max_pendings:
849 out.append('%s has %d build(s) pending' %
850 (builder_name, pending_builds_len))
851 if out:
852 return [output_api.PresubmitPromptWarning(
853 'Build(s) pending. It is suggested to wait that no more than %d '
854 'builds are pending.' % max_pendings,
855 long_text='\n'.join(out))]
856 return []
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000857
858
dpranke@chromium.orgbce925d2015-01-16 22:38:38 +0000859def CheckOwners(input_api, output_api, source_file_filter=None):
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000860 if input_api.is_committing:
861 if input_api.tbr:
862 return [output_api.PresubmitNotifyResult(
863 '--tbr was specified, skipping OWNERS check')]
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100864 needed = 'LGTM from an OWNER'
865 output_fn = output_api.PresubmitError
rmistry@google.com5fc6c8c2015-04-16 16:38:43 +0000866 if input_api.change.issue:
tandrii@chromium.org9dea2ac2016-04-28 06:26:20 +0000867 if input_api.dry_run:
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100868 output_fn = lambda text: output_api.PresubmitNotifyResult(
869 'This is a dry run, but these failures would be reported on ' +
870 'commit:\n' + text)
rmistry@google.com5fc6c8c2015-04-16 16:38:43 +0000871 else:
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000872 return [output_api.PresubmitError("OWNERS check failed: this change has "
873 "no Rietveld issue number, so we can't check it for approvals.")]
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000874 else:
875 needed = 'OWNER reviewers'
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100876 output_fn = output_api.PresubmitNotifyResult
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000877
dpranke@chromium.orgadd5df42011-03-08 23:04:01 +0000878 affected_files = set([f.LocalPath() for f in
Dirk Pranke3c86cee2017-01-23 22:02:06 -0800879 input_api.change.AffectedFiles(file_filter=source_file_filter)])
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000880
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000881 owners_db = input_api.owners_db
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000882 owner_email, reviewers = GetCodereviewOwnerAndReviewers(
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000883 input_api,
884 owners_db.email_regexp,
885 approval_needed=input_api.is_committing)
886
dpranke@chromium.orgf6ddfa42013-03-05 21:06:03 +0000887 owner_email = owner_email or input_api.change.author_email
888
dpranke@chromium.orgbce925d2015-01-16 22:38:38 +0000889 if owner_email:
dpranke@chromium.org4c7ce992013-03-06 17:15:40 +0000890 reviewers_plus_owner = set([owner_email]).union(reviewers)
dpranke@chromium.orgdbf8b4e2013-02-28 19:24:16 +0000891 missing_files = owners_db.files_not_covered_by(affected_files,
dpranke@chromium.orgf6ddfa42013-03-05 21:06:03 +0000892 reviewers_plus_owner)
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000893 else:
dpranke@chromium.orgdbf8b4e2013-02-28 19:24:16 +0000894 missing_files = owners_db.files_not_covered_by(affected_files, reviewers)
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000895
dpranke@chromium.org6b1e3ee2013-02-23 00:06:38 +0000896 if missing_files:
zork@chromium.org046e1752012-05-07 05:56:12 +0000897 output_list = [
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100898 output_fn('Missing %s for these files:\n %s' %
899 (needed, '\n '.join(sorted(missing_files))))]
zork@chromium.org046e1752012-05-07 05:56:12 +0000900 if not input_api.is_committing:
dpranke@chromium.org31b42c02013-09-19 19:24:15 +0000901 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100902 output_list.append(output_fn('Suggested OWNERS: ' +
scheib@chromium.org93276ab2013-10-14 23:55:32 +0000903 '(Use "git-cl owners" to interactively select owners.)\n %s' %
904 ('\n '.join(suggested_owners or []))))
zork@chromium.org046e1752012-05-07 05:56:12 +0000905 return output_list
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000906
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000907 if input_api.is_committing and not reviewers:
Clemens Hammacherd0c226a2017-01-16 14:09:52 +0100908 return [output_fn('Missing LGTM from someone other than %s' % owner_email)]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000909 return []
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000910
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000911def GetCodereviewOwnerAndReviewers(input_api, email_regexp, approval_needed):
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000912 """Return the owner and reviewers of a change, if any.
913
914 If approval_needed is True, only reviewers who have approved the change
915 will be returned.
916 """
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000917 # Rietveld is default.
918 func = _RietveldOwnerAndReviewers
919 if input_api.gerrit:
920 func = _GerritOwnerAndReviewers
921 return func(input_api, email_regexp, approval_needed)
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000922
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000923
ilevy@chromium.org1fa5cb92012-12-05 04:04:42 +0000924def _GetRietveldIssueProps(input_api, messages):
925 """Gets the issue properties from rietveld."""
926 issue = input_api.change.issue
927 if issue and input_api.rietveld:
928 return input_api.rietveld.get_issue_properties(
929 issue=int(issue), messages=messages)
930
931
isherman@chromium.orgb5cded62014-03-25 17:47:57 +0000932def _ReviewersFromChange(change):
933 """Return the reviewers specified in the |change|, if any."""
934 reviewers = set()
935 if change.R:
936 reviewers.update(set([r.strip() for r in change.R.split(',')]))
937 if change.TBR:
938 reviewers.update(set([r.strip() for r in change.TBR.split(',')]))
isherman@chromium.org103ae032014-04-09 09:06:19 +0000939
940 # Drop reviewers that aren't specified in email address format.
941 return set(reviewer for reviewer in reviewers if '@' in reviewer)
isherman@chromium.orgb5cded62014-03-25 17:47:57 +0000942
943
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000944def _match_reviewer_email(r, owner_email, email_regexp):
945 return email_regexp.match(r) and r != owner_email
946
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000947def _RietveldOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
948 """Return the owner and reviewers of a change, if any.
949
950 If approval_needed is True, only reviewers who have approved the change
951 will be returned.
952 """
ilevy@chromium.org1fa5cb92012-12-05 04:04:42 +0000953 issue_props = _GetRietveldIssueProps(input_api, True)
954 if not issue_props:
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000955 return None, (set() if approval_needed else
956 _ReviewersFromChange(input_api.change))
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000957
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000958 if not approval_needed:
959 return issue_props['owner_email'], set(issue_props['reviewers'])
960
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000961 owner_email = issue_props['owner_email']
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000962
maruel@chromium.org80941c22011-05-30 20:14:18 +0000963 messages = issue_props.get('messages', [])
964 approvers = set(
965 m['sender'] for m in messages
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000966 if m.get('approval') and _match_reviewer_email(m['sender'], owner_email,
967 email_regexp))
maruel@chromium.org80941c22011-05-30 20:14:18 +0000968 return owner_email, approvers
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000969
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000970
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000971def _GerritOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
972 """Return the owner and reviewers of a change, if any.
973
974 If approval_needed is True, only reviewers who have approved the change
975 will be returned.
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000976 """
977 issue = input_api.change.issue
978 if not issue:
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000979 return None, (set() if approval_needed else
980 _ReviewersFromChange(input_api.change))
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000981
982 owner_email = input_api.gerrit.GetChangeOwner(issue)
983 reviewers = set(
984 r for r in input_api.gerrit.GetChangeReviewers(issue, approval_needed)
985 if _match_reviewer_email(r, owner_email, email_regexp))
tandrii81665dc2016-08-29 09:16:19 -0700986 input_api.logging.debug('owner: %s; approvals given by: %s',
987 owner_email, ', '.join(sorted(reviewers)))
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000988 return owner_email, reviewers
989
990
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000991def _CheckConstNSObject(input_api, output_api, source_file_filter):
992 """Checks to make sure no objective-c files have |const NSSomeClass*|."""
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000993 pattern = input_api.re.compile(
dominik.rottsches@intel.com34a92d92015-03-25 07:36:05 +0000994 r'(?<!reinterpret_cast<)'
995 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000996
997 def objective_c_filter(f):
998 return (source_file_filter(f) and
mark@chromium.orgedf744d2011-03-28 16:45:34 +0000999 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm'))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001000
1001 files = []
1002 for f in input_api.AffectedSourceFiles(objective_c_filter):
1003 contents = input_api.ReadFile(f)
1004 if pattern.search(contents):
1005 files.append(f)
1006
1007 if files:
1008 if input_api.is_committing:
1009 res_type = output_api.PresubmitPromptWarning
1010 else:
1011 res_type = output_api.PresubmitNotifyResult
1012 return [ res_type('|const NSClass*| is wrong, see ' +
1013 'http://dev.chromium.org/developers/clang-mac',
1014 files) ]
1015 return []
1016
1017
bauerb@chromium.org4cea01e2012-03-20 19:49:05 +00001018def CheckSingletonInHeaders(input_api, output_api, source_file_filter=None):
glider@chromium.orgc3617f32015-02-19 16:33:33 +00001019 """Deprecated, must be removed."""
1020 return [
1021 output_api.PresubmitNotifyResult(
1022 'CheckSingletonInHeaders is deprecated, please remove it.')
1023 ]
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001024
1025
1026def PanProjectChecks(input_api, output_api,
1027 excluded_paths=None, text_files=None,
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001028 license_header=None, project_name=None,
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +00001029 owners_check=True, maxlen=80):
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001030 """Checks that ALL chromium orbit projects should use.
1031
1032 These are checks to be run on all Chromium orbit project, including:
1033 Chromium
1034 Native Client
1035 V8
1036 When you update this function, please take this broad scope into account.
1037 Args:
1038 input_api: Bag of input related interfaces.
1039 output_api: Bag of output related interfaces.
1040 excluded_paths: Don't include these paths in common checks.
1041 text_files: Which file are to be treated as documentation text files.
1042 license_header: What license header should be on files.
1043 project_name: What is the name of the project as it appears in the license.
1044 Returns:
1045 A list of warning or error objects.
1046 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +00001047 excluded_paths = tuple(excluded_paths or [])
1048 text_files = tuple(text_files or (
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +00001049 r'.+\.txt$',
1050 r'.+\.json$',
maruel@chromium.org69eaecb2011-06-14 13:09:13 +00001051 ))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001052 project_name = project_name or 'Chromium'
mark@chromium.org2cc66422012-08-07 21:22:32 +00001053
1054 # Accept any year number from 2006 to the current year, or the special
rvargas@chromium.org02652602014-03-14 19:43:20 +00001055 # 2006-20xx string used on the oldest files. 2006-20xx is deprecated, but
1056 # tolerated on old files.
mark@chromium.org2cc66422012-08-07 21:22:32 +00001057 current_year = int(input_api.time.strftime('%Y'))
1058 allowed_years = (str(s) for s in reversed(xrange(2006, current_year + 1)))
rvargas@chromium.org02652602014-03-14 19:43:20 +00001059 years_re = '(' + '|'.join(allowed_years) + '|2006-2008|2006-2009|2006-2010)'
mark@chromium.org2cc66422012-08-07 21:22:32 +00001060
1061 # The (c) is deprecated, but tolerate it until it's removed from all files.
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001062 license_header = license_header or (
mark@chromium.org2cc66422012-08-07 21:22:32 +00001063 r'.*? Copyright (\(c\) )?%(year)s The %(project)s Authors\. '
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001064 r'All rights reserved\.\n'
1065 r'.*? Use of this source code is governed by a BSD-style license that '
1066 r'can be\n'
dbeam@chromium.orgb2312102012-02-15 02:01:55 +00001067 r'.*? found in the LICENSE file\.(?: \*/)?\n'
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001068 ) % {
mark@chromium.org2cc66422012-08-07 21:22:32 +00001069 'year': years_re,
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001070 'project': project_name,
1071 }
1072
1073 results = []
1074 # This code loads the default black list (e.g. third_party, experimental, etc)
1075 # and add our black list (breakpad, skia and v8 are still not following
1076 # google style and are not really living this repository).
1077 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage.
1078 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths
1079 white_list = input_api.DEFAULT_WHITE_LIST + text_files
1080 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list)
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +00001081 text_files = lambda x: input_api.FilterSourceFile(
1082 x, black_list=black_list, white_list=white_list)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001083
1084 snapshot_memory = []
1085 def snapshot(msg):
1086 """Measures & prints performance warning if a rule is running slow."""
1087 dt2 = input_api.time.clock()
1088 if snapshot_memory:
1089 delta_ms = int(1000*(dt2 - snapshot_memory[0]))
1090 if delta_ms > 500:
1091 print " %s took a long time: %dms" % (snapshot_memory[1], delta_ms)
1092 snapshot_memory[:] = (dt2, msg)
1093
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001094 if owners_check:
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001095 snapshot("checking owners")
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001096 results.extend(input_api.canned_checks.CheckOwners(
Dirk Pranke3c86cee2017-01-23 22:02:06 -08001097 input_api, output_api, source_file_filter=None))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001098
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001099 snapshot("checking long lines")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001100 results.extend(input_api.canned_checks.CheckLongLines(
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +00001101 input_api, output_api, maxlen, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001102 snapshot( "checking tabs")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001103 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
1104 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001105 snapshot( "checking stray whitespace")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001106 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
1107 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001108 snapshot("checking nsobjects")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001109 results.extend(_CheckConstNSObject(
1110 input_api, output_api, source_file_filter=sources))
phajdan.jr@chromium.orgd965db32015-11-16 09:46:56 +00001111 snapshot("checking license")
1112 results.extend(input_api.canned_checks.CheckLicense(
1113 input_api, output_api, license_header, source_file_filter=sources))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +00001114
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +00001115 if input_api.is_committing:
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +00001116 snapshot("checking was uploaded")
1117 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
1118 input_api, output_api))
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +00001119 snapshot("checking description")
1120 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1121 input_api, output_api))
1122 results.extend(input_api.canned_checks.CheckDoNotSubmitInDescription(
1123 input_api, output_api))
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +00001124 snapshot("checking do not submit in files")
1125 results.extend(input_api.canned_checks.CheckDoNotSubmitInFiles(
1126 input_api, output_api))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001127 snapshot("done")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001128 return results
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001129
1130
Christopher Lamc5ba6922017-01-24 11:19:14 +11001131def CheckPatchFormatted(input_api, output_api, check_js=False):
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001132 import git_cl
Christopher Lamc5ba6922017-01-24 11:19:14 +11001133 cmd = ['cl', 'format', '--dry-run']
1134 if check_js:
1135 cmd.append('--js')
1136 cmd.append(input_api.PresubmitLocalPath())
1137
enne@chromium.org555cfe42014-01-29 18:21:39 +00001138 code, _ = git_cl.RunGitWithCode(cmd, suppress_stderr=True)
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001139 if code == 2:
enne@chromium.orge969cc72015-03-13 18:56:54 +00001140 short_path = input_api.basename(input_api.PresubmitLocalPath())
1141 full_path = input_api.os_path.relpath(input_api.PresubmitLocalPath(),
1142 input_api.change.RepositoryRoot())
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001143 return [output_api.PresubmitPromptWarning(
erg@chromium.orge0a7c5d2015-02-23 20:30:08 +00001144 'The %s directory requires source formatting. '
Christopher Lamc5ba6922017-01-24 11:19:14 +11001145 'Please run git cl format %s%s' %
1146 (short_path, '--js ' if check_js else '', full_path))]
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001147 # As this is just a warning, ignore all other errors if the user
1148 # happens to have a broken clang-format, doesn't use git, etc etc.
1149 return []
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001150
1151
1152def CheckGNFormatted(input_api, output_api):
1153 import gn
1154 affected_files = input_api.AffectedFiles(
1155 include_deletes=False,
1156 file_filter=lambda x: x.LocalPath().endswith('.gn') or
kylechar58edce22016-06-17 06:07:51 -07001157 x.LocalPath().endswith('.gni') or
1158 x.LocalPath().endswith('.typemap'))
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001159 warnings = []
1160 for f in affected_files:
1161 cmd = ['gn', 'format', '--dry-run', f.AbsoluteLocalPath()]
1162 rc = gn.main(cmd)
1163 if rc == 2:
1164 warnings.append(output_api.PresubmitPromptWarning(
brettw4b8ed592016-08-05 16:19:12 -07001165 '%s requires formatting. Please run:\n gn format %s' % (
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001166 f.AbsoluteLocalPath(), f.LocalPath())))
1167 # It's just a warning, so ignore other types of failures assuming they'll be
1168 # caught elsewhere.
1169 return warnings