blob: 65e3185ea7abe8c70922c66c9889fde83e309890 [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
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000109def CheckDoNotSubmitInFiles(input_api, output_api):
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000110 """Checks that the user didn't add 'DO NOT ''SUBMIT' to any files."""
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000111 # We want to check every text file, not just source files.
112 file_filter = lambda x : x
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000113 keyword = 'DO NOT ''SUBMIT'
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000114 errors = _FindNewViolationsOfRule(lambda _, line : keyword not in line,
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000115 input_api, file_filter)
116 text = '\n'.join('Found %s in %s' % (keyword, loc) for loc in errors)
117 if text:
118 return [output_api.PresubmitError(text)]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000119 return []
120
121
tfarina@chromium.orgb6795642014-12-12 00:03:49 +0000122def CheckChangeLintsClean(input_api, output_api, source_file_filter=None,
tfarina@chromium.orga62208f2015-02-25 03:23:11 +0000123 lint_filters=None, verbose_level=None):
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000124 """Checks that all '.cc' and '.h' files pass cpplint.py."""
erg@google.com26970fa2009-11-17 18:07:32 +0000125 _RE_IS_TEST = input_api.re.compile(r'.*tests?.(cc|h)$')
126 result = []
127
enne@chromium.orge72c5f52013-04-16 00:36:40 +0000128 cpplint = input_api.cpplint
maruel@chromium.orgb17b55b2010-11-03 14:42:37 +0000129 # Access to a protected member _XX of a client class
130 # pylint: disable=W0212
erg@google.com26970fa2009-11-17 18:07:32 +0000131 cpplint._cpplint_state.ResetErrorCounts()
132
tfarina@chromium.orgb6795642014-12-12 00:03:49 +0000133 lint_filters = lint_filters or DEFAULT_LINT_FILTERS
danakj@chromium.org0ae71222016-01-11 19:37:11 +0000134 lint_filters.extend(BLACKLIST_LINT_FILTERS)
tfarina@chromium.orgb6795642014-12-12 00:03:49 +0000135 cpplint._SetFilters(','.join(lint_filters))
erg@google.com26970fa2009-11-17 18:07:32 +0000136
137 # We currently are more strict with normal code than unit tests; 4 and 5 are
138 # the verbosity level that would normally be passed to cpplint.py through
139 # --verbose=#. Hopefully, in the future, we can be more verbose.
140 files = [f.AbsoluteLocalPath() for f in
141 input_api.AffectedSourceFiles(source_file_filter)]
142 for file_name in files:
143 if _RE_IS_TEST.match(file_name):
144 level = 5
145 else:
146 level = 4
147
tfarina@chromium.orga62208f2015-02-25 03:23:11 +0000148 verbose_level = verbose_level or level
149 cpplint.ProcessFile(file_name, verbose_level)
erg@google.com26970fa2009-11-17 18:07:32 +0000150
151 if cpplint._cpplint_state.error_count > 0:
152 if input_api.is_committing:
153 res_type = output_api.PresubmitError
154 else:
155 res_type = output_api.PresubmitPromptWarning
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000156 result = [res_type('Changelist failed cpplint.py check.')]
erg@google.com26970fa2009-11-17 18:07:32 +0000157
158 return result
159
160
maruel@chromium.org3410d912009-06-09 20:56:16 +0000161def CheckChangeHasNoCR(input_api, output_api, source_file_filter=None):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000162 """Checks no '\r' (CR) character is in any source files."""
163 cr_files = []
maruel@chromium.org3410d912009-06-09 20:56:16 +0000164 for f in input_api.AffectedSourceFiles(source_file_filter):
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000165 if '\r' in input_api.ReadFile(f, 'rb'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000166 cr_files.append(f.LocalPath())
167 if cr_files:
168 return [output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000169 'Found a CR character in these files:', items=cr_files)]
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000170 return []
171
172
173def CheckChangeHasOnlyOneEol(input_api, output_api, source_file_filter=None):
174 """Checks the files ends with one and only one \n (LF)."""
175 eof_files = []
176 for f in input_api.AffectedSourceFiles(source_file_filter):
177 contents = input_api.ReadFile(f, 'rb')
178 # Check that the file ends in one and only one newline character.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000179 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000180 eof_files.append(f.LocalPath())
181
182 if eof_files:
183 return [output_api.PresubmitPromptWarning(
184 'These files should end in one (and only one) newline character:',
185 items=eof_files)]
186 return []
187
188
189def CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api,
190 source_file_filter=None):
191 """Runs both CheckChangeHasNoCR and CheckChangeHasOnlyOneEOL in one pass.
192
193 It is faster because it is reading the file only once.
194 """
195 cr_files = []
196 eof_files = []
197 for f in input_api.AffectedSourceFiles(source_file_filter):
198 contents = input_api.ReadFile(f, 'rb')
199 if '\r' in contents:
200 cr_files.append(f.LocalPath())
201 # Check that the file ends in one and only one newline character.
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000202 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000203 eof_files.append(f.LocalPath())
204 outputs = []
205 if cr_files:
206 outputs.append(output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000207 'Found a CR character in these files:', items=cr_files))
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000208 if eof_files:
209 outputs.append(output_api.PresubmitPromptWarning(
210 'These files should end in one (and only one) newline character:',
211 items=eof_files))
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000212 return outputs
213
seanmccullough0b670442016-06-07 10:45:58 -0700214def CheckGenderNeutral(input_api, output_api, source_file_filter=None):
215 """Checks that there are no gendered pronouns in any of the text files to be
216 submitted.
217 """
218 gendered_re = input_api.re.compile(
219 '(^|\s|\(|\[)([Hh]e|[Hh]is|[Hh]ers?|[Hh]im|[Ss]he|[Gg]uys?)\\b')
220
221 errors = []
222 for f in input_api.AffectedFiles(include_deletes=False,
223 file_filter=source_file_filter):
224 for line_num, line in f.ChangedContents():
225 if gendered_re.search(line):
226 errors.append('%s (%d): %s' % (f.LocalPath(), line_num, line))
227
228 if len(errors):
229 return [output_api.PresubmitPromptWarning('Found a gendered pronoun in:',
230 long_text='\n'.join(errors))]
231 return []
232
233
maruel@chromium.org44a17ad2009-06-08 14:14:35 +0000234
chrisha@google.com267d6592012-06-19 19:23:31 +0000235def _ReportErrorFileAndLine(filename, line_num, dummy_line):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000236 """Default error formatter for _FindNewViolationsOfRule."""
danakj@chromium.orgc5965ba2013-08-14 00:27:24 +0000237 return '%s:%s' % (filename, line_num)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000238
239
240def _FindNewViolationsOfRule(callable_rule, input_api, source_file_filter=None,
241 error_formatter=_ReportErrorFileAndLine):
242 """Find all newly introduced violations of a per-line rule (a callable).
243
244 Arguments:
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000245 callable_rule: a callable taking a file extension and line of input and
246 returning True if the rule is satisfied and False if there was a problem.
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000247 input_api: object to enumerate the affected files.
248 source_file_filter: a filter to be passed to the input api.
249 error_formatter: a callable taking (filename, line_number, line) and
250 returning a formatted error string.
251
252 Returns:
253 A list of the newly-introduced violations reported by the rule.
254 """
255 errors = []
sail@chromium.org5538e022011-05-12 17:53:16 +0000256 for f in input_api.AffectedFiles(include_deletes=False,
257 file_filter=source_file_filter):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000258 # For speed, we do two passes, checking first the full file. Shelling out
259 # to the SCM to determine the changed region can be quite expensive on
260 # Win32. Assuming that most files will be kept problem-free, we can
261 # skip the SCM operations most of the time.
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000262 extension = str(f.LocalPath()).rsplit('.', 1)[-1]
263 if all(callable_rule(extension, line) for line in f.NewContents()):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000264 continue # No violation found in full text: can skip considering diff.
265
266 for line_num, line in f.ChangedContents():
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000267 if not callable_rule(extension, line):
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000268 errors.append(error_formatter(f.LocalPath(), line_num, line))
269
270 return errors
271
272
maruel@chromium.org3410d912009-06-09 20:56:16 +0000273def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000274 """Checks that there are no tab characters in any of the text files to be
275 submitted.
276 """
maruel@chromium.org115ae6c2010-06-18 17:11:43 +0000277 # In addition to the filter, make sure that makefiles are blacklisted.
278 if not source_file_filter:
279 # It's the default filter.
280 source_file_filter = input_api.FilterSourceFile
281 def filter_more(affected_file):
cmp@chromium.orgcb5e57c2012-04-06 19:50:15 +0000282 basename = input_api.os_path.basename(affected_file.LocalPath())
283 return (not (basename in ('Makefile', 'makefile') or
284 basename.endswith('.mk')) and
maruel@chromium.org115ae6c2010-06-18 17:11:43 +0000285 source_file_filter(affected_file))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000286
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000287 tabs = _FindNewViolationsOfRule(lambda _, line : '\t' not in line,
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000288 input_api, filter_more)
289
maruel@chromium.orge9b71c92009-06-10 18:10:01 +0000290 if tabs:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000291 return [output_api.PresubmitPromptWarning('Found a tab character in:',
292 long_text='\n'.join(tabs))]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000293 return []
294
295
estade@chromium.orgfdcc9f72011-02-07 22:25:07 +0000296def CheckChangeTodoHasOwner(input_api, output_api, source_file_filter=None):
297 """Checks that the user didn't add TODO(name) without an owner."""
298
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000299 unowned_todo = input_api.re.compile('TO''DO[^(]')
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000300 errors = _FindNewViolationsOfRule(lambda _, x : not unowned_todo.search(x),
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000301 input_api, source_file_filter)
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +0000302 errors = ['Found TO''DO with no owner in ' + x for x in errors]
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000303 if errors:
304 return [output_api.PresubmitPromptWarning('\n'.join(errors))]
estade@chromium.orgfdcc9f72011-02-07 22:25:07 +0000305 return []
306
307
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000308def CheckChangeHasNoStrayWhitespace(input_api, output_api,
309 source_file_filter=None):
310 """Checks that there is no stray whitespace at source lines end."""
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000311 errors = _FindNewViolationsOfRule(lambda _, line : line.rstrip() == line,
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000312 input_api, source_file_filter)
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000313 if errors:
314 return [output_api.PresubmitPromptWarning(
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000315 'Found line ending with white spaces in:',
316 long_text='\n'.join(errors))]
maruel@chromium.orgf5888bb2009-06-10 20:26:37 +0000317 return []
318
319
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +0000320def CheckLongLines(input_api, output_api, maxlen, source_file_filter=None):
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000321 """Checks that there aren't any lines longer than maxlen characters in any of
322 the text files to be submitted.
323 """
dpranke@chromium.orge3b1c3d2012-10-20 22:28:14 +0000324 maxlens = {
325 'java': 100,
torne@chromium.org0ecad9c2013-02-15 16:35:16 +0000326 # This is specifically for Android's handwritten makefiles (Android.mk).
327 'mk': 200,
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000328 '': maxlen,
329 }
maruel@chromium.orgeba64622011-06-20 18:26:48 +0000330
erikchen@google.com12816082013-12-03 02:04:20 +0000331 # Language specific exceptions to max line length.
332 # '.h' is considered an obj-c file extension, since OBJC_EXCEPTIONS are a
333 # superset of CPP_EXCEPTIONS.
334 CPP_FILE_EXTS = ('c', 'cc')
335 CPP_EXCEPTIONS = ('#define', '#endif', '#if', '#include', '#pragma')
336 JAVA_FILE_EXTS = ('java',)
337 JAVA_EXCEPTIONS = ('import ', 'package ')
dbeam@chromium.org5a2af6d2015-10-08 17:52:29 +0000338 JS_FILE_EXTS = ('js',)
339 JS_EXCEPTIONS = ("GEN('#include",)
erikchen@google.com12816082013-12-03 02:04:20 +0000340 OBJC_FILE_EXTS = ('h', 'm', 'mm')
341 OBJC_EXCEPTIONS = ('#define', '#endif', '#if', '#import', '#include',
342 '#pragma')
dbeam@chromium.org5a2af6d2015-10-08 17:52:29 +0000343 PY_FILE_EXTS = ('py',)
aiolos@chromium.org84033cc2015-07-16 01:27:15 +0000344 PY_EXCEPTIONS = ('import', 'from')
erikchen@google.com12816082013-12-03 02:04:20 +0000345
346 LANGUAGE_EXCEPTIONS = [
347 (CPP_FILE_EXTS, CPP_EXCEPTIONS),
348 (JAVA_FILE_EXTS, JAVA_EXCEPTIONS),
dbeam@chromium.org5a2af6d2015-10-08 17:52:29 +0000349 (JS_FILE_EXTS, JS_EXCEPTIONS),
erikchen@google.com12816082013-12-03 02:04:20 +0000350 (OBJC_FILE_EXTS, OBJC_EXCEPTIONS),
aiolos@chromium.org84033cc2015-07-16 01:27:15 +0000351 (PY_FILE_EXTS, PY_EXCEPTIONS),
erikchen@google.com12816082013-12-03 02:04:20 +0000352 ]
sivachandra@chromium.org270db5c2012-10-09 22:38:44 +0000353
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000354 def no_long_lines(file_extension, line):
erikchen@google.com12816082013-12-03 02:04:20 +0000355 # Check for language specific exceptions.
356 if any(file_extension in exts and line.startswith(exceptions)
357 for exts, exceptions in LANGUAGE_EXCEPTIONS):
sivachandra@chromium.org270db5c2012-10-09 22:38:44 +0000358 return True
359
bulach@chromium.orgbfffd452012-02-22 01:13:29 +0000360 file_maxlen = maxlens.get(file_extension, maxlens[''])
361 # Stupidly long symbols that needs to be worked around if takes 66% of line.
362 long_symbol = file_maxlen * 2 / 3
363 # Hard line length limit at 50% more.
364 extra_maxlen = file_maxlen * 3 / 2
365
366 line_len = len(line)
367 if line_len <= file_maxlen:
maruel@chromium.orgeba64622011-06-20 18:26:48 +0000368 return True
369
dtu@chromium.org03f0c532015-03-27 22:22:07 +0000370 # Allow long URLs of any length.
treib@chromium.orgde3ee902014-07-28 14:23:11 +0000371 if any((url in line) for url in ('file://', 'http://', 'https://')):
372 return True
373
qyearsley12fa6ff2016-08-24 09:18:40 -0700374 # If 'line-too-long' is explicitly suppressed for the line, any length is
nednguyen@google.com6df90f32015-12-01 20:14:33 +0000375 # acceptable.
376 if 'pylint: disable=line-too-long' in line and file_extension == 'py':
377 return True
378
dtu@chromium.org03f0c532015-03-27 22:22:07 +0000379 if line_len > extra_maxlen:
380 return False
381
treib@chromium.orgde3ee902014-07-28 14:23:11 +0000382 if 'url(' in line and file_extension == 'css':
383 return True
384
dbeam@chromium.orgb5ccc9b2014-09-23 00:42:22 +0000385 if '<include' in line and file_extension in ('css', 'html', 'js'):
386 return True
387
treib@chromium.orgde3ee902014-07-28 14:23:11 +0000388 return input_api.re.match(
389 r'.*[A-Za-z][A-Za-z_0-9]{%d,}.*' % long_symbol, line)
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000390
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000391 def format_error(filename, line_num, line):
392 return '%s, line %s, %s chars' % (filename, line_num, len(line))
393
394 errors = _FindNewViolationsOfRule(no_long_lines, input_api,
395 source_file_filter,
396 error_formatter=format_error)
397 if errors:
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000398 msg = 'Found lines longer than %s characters (first 5 shown).' % maxlen
nick@chromium.orge06cb4e2011-04-23 01:20:38 +0000399 return [output_api.PresubmitPromptWarning(msg, items=errors[:5])]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000400 else:
401 return []
402
403
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000404def CheckLicense(input_api, output_api, license_re, source_file_filter=None,
maruel@chromium.org71626852010-11-03 13:14:25 +0000405 accept_empty_files=True):
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000406 """Verifies the license header.
407 """
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000408 license_re = input_api.re.compile(license_re, input_api.re.MULTILINE)
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000409 bad_files = []
410 for f in input_api.AffectedSourceFiles(source_file_filter):
411 contents = input_api.ReadFile(f, 'rb')
maruel@chromium.org71626852010-11-03 13:14:25 +0000412 if accept_empty_files and not contents:
413 continue
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000414 if not license_re.search(contents):
415 bad_files.append(f.LocalPath())
416 if bad_files:
phajdan.jr@chromium.orge27eb7e2015-11-16 12:47:53 +0000417 return [output_api.PresubmitPromptWarning(
bradnelson@google.com393460b2011-03-29 01:20:26 +0000418 'License must match:\n%s\n' % license_re.pattern +
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000419 'Found a bad license header in these files:', items=bad_files)]
maruel@chromium.orgb9e7ada2010-01-27 23:12:39 +0000420 return []
421
422
maruel@chromium.org1a0e3cb2009-06-10 18:03:04 +0000423def CheckChangeSvnEolStyle(input_api, output_api, source_file_filter=None):
maruel@chromium.orgb7d46902009-06-10 14:12:10 +0000424 """Checks that the source files have svn:eol-style=LF."""
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000425 return CheckSvnProperty(input_api, output_api,
426 'svn:eol-style', 'LF',
427 input_api.AffectedSourceFiles(source_file_filter))
428
429
430def CheckSvnForCommonMimeTypes(input_api, output_api):
431 """Checks that common binary file types have the correct svn:mime-type."""
432 output = []
433 files = input_api.AffectedFiles(include_deletes=False)
maruel@chromium.orge49187c2009-06-26 22:44:53 +0000434 def IsExts(x, exts):
435 path = x.LocalPath()
436 for extension in exts:
437 if path.endswith(extension):
438 return True
439 return False
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000440 def FilterFiles(extension):
maruel@chromium.orge49187c2009-06-26 22:44:53 +0000441 return filter(lambda x: IsExts(x, extension), files)
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000442 def RunCheck(mime_type, files):
443 output.extend(CheckSvnProperty(input_api, output_api, 'svn:mime-type',
444 mime_type, files))
maruel@chromium.orge49187c2009-06-26 22:44:53 +0000445 RunCheck('application/pdf', FilterFiles(['.pdf']))
446 RunCheck('image/bmp', FilterFiles(['.bmp']))
447 RunCheck('image/gif', FilterFiles(['.gif']))
448 RunCheck('image/png', FilterFiles(['.png']))
449 RunCheck('image/jpeg', FilterFiles(['.jpg', '.jpeg', '.jpe']))
450 RunCheck('image/vnd.microsoft.icon', FilterFiles(['.ico']))
maruel@chromium.org46e832a2009-06-18 19:58:07 +0000451 return output
452
453
454def CheckSvnProperty(input_api, output_api, prop, expected, affected_files):
455 """Checks that affected_files files have prop=expected."""
thestig@chromium.orgda8cddd2009-08-13 00:25:55 +0000456 if input_api.change.scm != 'svn':
457 return []
458
459 bad = filter(lambda f: f.Property(prop) != expected, affected_files)
maruel@chromium.orgb7d46902009-06-10 14:12:10 +0000460 if bad:
maruel@chromium.org0874d472009-06-10 19:08:33 +0000461 if input_api.is_committing:
maruel@chromium.orge3608df2009-11-10 20:22:57 +0000462 res_type = output_api.PresubmitError
maruel@chromium.org0874d472009-06-10 19:08:33 +0000463 else:
maruel@chromium.orge3608df2009-11-10 20:22:57 +0000464 res_type = output_api.PresubmitNotifyResult
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000465 message = 'Run the command: svn pset %s %s \\' % (prop, expected)
maruel@chromium.orge3608df2009-11-10 20:22:57 +0000466 return [res_type(message, items=bad)]
maruel@chromium.orgb7d46902009-06-10 14:12:10 +0000467 return []
468
469
maruel@chromium.org3410d912009-06-09 20:56:16 +0000470### Other checks
471
472def CheckDoNotSubmit(input_api, output_api):
473 return (
474 CheckDoNotSubmitInDescription(input_api, output_api) +
475 CheckDoNotSubmitInFiles(input_api, output_api)
476 )
477
478
bradnelson@google.comc0b332a2010-08-26 00:30:37 +0000479def CheckTreeIsOpen(input_api, output_api,
480 url=None, closed=None, json_url=None):
481 """Check whether to allow commit without prompt.
482
483 Supports two styles:
484 1. Checks that an url's content doesn't match a regexp that would mean that
485 the tree is closed. (old)
486 2. Check the json_url to decide whether to allow commit without prompt.
487 Args:
488 input_api: input related apis.
489 output_api: output related apis.
490 url: url to use for regex based tree status.
491 closed: regex to match for closed status.
492 json_url: url to download json style status.
493 """
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000494 if not input_api.is_committing:
495 return []
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000496 try:
bradnelson@google.comc0b332a2010-08-26 00:30:37 +0000497 if json_url:
498 connection = input_api.urllib2.urlopen(json_url)
499 status = input_api.json.loads(connection.read())
500 connection.close()
501 if not status['can_commit_freely']:
502 short_text = 'Tree state is: ' + status['general_state']
503 long_text = status['message'] + '\n' + json_url
504 return [output_api.PresubmitError(short_text, long_text=long_text)]
505 else:
506 # TODO(bradnelson): drop this once all users are gone.
507 connection = input_api.urllib2.urlopen(url)
508 status = connection.read()
509 connection.close()
510 if input_api.re.match(closed, status):
511 long_text = status + '\n' + url
512 return [output_api.PresubmitError('The tree is closed.',
513 long_text=long_text)]
rohitrao@chromium.orgd490ee82012-02-06 19:31:33 +0000514 except IOError as e:
515 return [output_api.PresubmitError('Error fetching tree status.',
516 long_text=str(e))]
maruel@google.comfb2b8eb2009-04-23 21:03:42 +0000517 return []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000518
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000519def GetUnitTestsInDirectory(
smut@google.comac296202014-04-24 21:47:17 +0000520 input_api, output_api, directory, whitelist=None, blacklist=None, env=None):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000521 """Lists all files in a directory and runs them. Doesn't recurse.
522
nick@chromium.orgff526192013-06-10 19:30:26 +0000523 It's mainly a wrapper for RunUnitTests. Use whitelist and blacklist to filter
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000524 tests accordingly.
525 """
526 unit_tests = []
527 test_path = input_api.os_path.abspath(
528 input_api.os_path.join(input_api.PresubmitLocalPath(), directory))
529
530 def check(filename, filters):
531 return any(True for i in filters if input_api.re.match(i, filename))
532
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000533 to_run = found = 0
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000534 for filename in input_api.os_listdir(test_path):
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000535 found += 1
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000536 fullpath = input_api.os_path.join(test_path, filename)
537 if not input_api.os_path.isfile(fullpath):
538 continue
539 if whitelist and not check(filename, whitelist):
540 continue
541 if blacklist and check(filename, blacklist):
542 continue
543 unit_tests.append(input_api.os_path.join(directory, filename))
maruel@chromium.orgfae707b2011-09-15 18:57:58 +0000544 to_run += 1
545 input_api.logging.debug('Found %d files, running %d' % (found, to_run))
546 if not to_run:
547 return [
548 output_api.PresubmitPromptWarning(
549 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
550 % (found, whitelist, blacklist, directory))
551 ]
smut@google.comac296202014-04-24 21:47:17 +0000552 return GetUnitTests(input_api, output_api, unit_tests, env)
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000553
554
smut@google.comac296202014-04-24 21:47:17 +0000555def GetUnitTests(input_api, output_api, unit_tests, env=None):
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000556 """Runs all unit tests in a directory.
557
558 On Windows, sys.executable is used for unit tests ending with ".py".
559 """
560 # We don't want to hinder users from uploading incomplete patches.
561 if input_api.is_committing:
562 message_type = output_api.PresubmitError
563 else:
564 message_type = output_api.PresubmitPromptWarning
565
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000566 results = []
567 for unit_test in unit_tests:
568 cmd = []
569 if input_api.platform == 'win32' and unit_test.endswith('.py'):
570 # Windows needs some help.
571 cmd = [input_api.python_executable]
572 cmd.append(unit_test)
maruel@chromium.org899e1c12011-04-07 17:03:18 +0000573 if input_api.verbose:
maruel@chromium.org6c7723e2011-04-12 19:04:55 +0000574 cmd.append('--verbose')
smut@google.comac296202014-04-24 21:47:17 +0000575 kwargs = {'cwd': input_api.PresubmitLocalPath()}
576 if env:
577 kwargs['env'] = env
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000578 results.append(input_api.Command(
579 name=unit_test,
580 cmd=cmd,
smut@google.comac296202014-04-24 21:47:17 +0000581 kwargs=kwargs,
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000582 message=message_type))
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000583 return results
584
nick@chromium.orgff526192013-06-10 19:30:26 +0000585
agable@chromium.org40a3d0b2014-05-15 01:59:16 +0000586def GetUnitTestsRecursively(input_api, output_api, directory,
587 whitelist, blacklist):
588 """Gets all files in the directory tree (git repo) that match the whitelist.
589
590 Restricts itself to only find files within the Change's source repo, not
591 dependencies.
592 """
593 def check(filename):
594 return (any(input_api.re.match(f, filename) for f in whitelist) and
595 not any(input_api.re.match(f, filename) for f in blacklist))
596
597 tests = []
598
599 to_run = found = 0
600 for filepath in input_api.change.AllFiles(directory):
601 found += 1
602 if check(filepath):
603 to_run += 1
604 tests.append(filepath)
605 input_api.logging.debug('Found %d files, running %d' % (found, to_run))
606 if not to_run:
607 return [
608 output_api.PresubmitPromptWarning(
609 'Out of %d files, found none that matched w=%r, b=%r in directory %s'
610 % (found, whitelist, blacklist, directory))
611 ]
612
613 return GetUnitTests(input_api, output_api, tests)
614
615
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000616def GetPythonUnitTests(input_api, output_api, unit_tests):
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000617 """Run the unit tests out of process, capture the output and use the result
618 code to determine success.
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000619
620 DEPRECATED.
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000621 """
maruel@chromium.orgd7dccf52009-06-06 18:51:58 +0000622 # We don't want to hinder users from uploading incomplete patches.
623 if input_api.is_committing:
624 message_type = output_api.PresubmitError
625 else:
626 message_type = output_api.PresubmitNotifyResult
maruel@chromium.org0e766052011-04-06 13:32:51 +0000627 results = []
maruel@chromium.org7b305e82009-05-19 18:24:20 +0000628 for unit_test in unit_tests:
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000629 # Run the unit tests out of process. This is because some unit tests
630 # stub out base libraries and don't clean up their mess. It's too easy to
631 # get subtle bugs.
632 cwd = None
633 env = None
634 unit_test_name = unit_test
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000635 # 'python -m test.unit_test' doesn't work. We need to change to the right
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000636 # directory instead.
637 if '.' in unit_test:
638 # Tests imported in submodules (subdirectories) assume that the current
639 # directory is in the PYTHONPATH. Manually fix that.
640 unit_test = unit_test.replace('.', '/')
641 cwd = input_api.os_path.dirname(unit_test)
642 unit_test = input_api.os_path.basename(unit_test)
643 env = input_api.environ.copy()
kbr@google.comab318592009-09-04 00:54:55 +0000644 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH
645 backpath = [
646 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1))
647 ]
maruel@chromium.orgc0b22972009-06-25 16:19:14 +0000648 if env.get('PYTHONPATH'):
649 backpath.append(env.get('PYTHONPATH'))
ukai@chromium.orga301f1f2009-08-05 10:37:33 +0000650 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000651 cmd = [input_api.python_executable, '-m', '%s' % unit_test]
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000652 results.append(input_api.Command(
653 name=unit_test_name,
654 cmd=cmd,
655 kwargs={'env': env, 'cwd': cwd},
656 message=message_type))
maruel@chromium.org0e766052011-04-06 13:32:51 +0000657 return results
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000658
659
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000660def RunUnitTestsInDirectory(input_api, *args, **kwargs):
661 """Run tests in a directory serially.
662
663 For better performance, use GetUnitTestsInDirectory and then
664 pass to input_api.RunTests.
665 """
666 return input_api.RunTests(
667 GetUnitTestsInDirectory(input_api, *args, **kwargs), False)
668
669
670def RunUnitTests(input_api, *args, **kwargs):
671 """Run tests serially.
672
673 For better performance, use GetUnitTests and then pass to
674 input_api.RunTests.
675 """
676 return input_api.RunTests(GetUnitTests(input_api, *args, **kwargs), False)
677
678
679def RunPythonUnitTests(input_api, *args, **kwargs):
680 """Run python tests in a directory serially.
681
682 DEPRECATED
683 """
684 return input_api.RunTests(
685 GetPythonUnitTests(input_api, *args, **kwargs), False)
686
687
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000688def _FetchAllFiles(input_api, white_list, black_list):
689 """Hack to fetch all files."""
690 # We cannot use AffectedFiles here because we want to test every python
691 # file on each single python change. It's because a change in a python file
692 # can break another unmodified file.
693 # Use code similar to InputApi.FilterSourceFile()
694 def Find(filepath, filters):
695 for item in filters:
696 if input_api.re.match(item, filepath):
697 return True
698 return False
699
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000700 files = []
701 path_len = len(input_api.PresubmitLocalPath())
maruel@chromium.org2b5ce562011-03-31 16:15:44 +0000702 for dirpath, dirnames, filenames in input_api.os_walk(
703 input_api.PresubmitLocalPath()):
maruel@chromium.org5d0dc432011-01-03 02:40:37 +0000704 # Passes dirnames in black list to speed up search.
705 for item in dirnames[:]:
706 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
707 if Find(filepath, black_list):
708 dirnames.remove(item)
709 for item in filenames:
710 filepath = input_api.os_path.join(dirpath, item)[path_len + 1:]
711 if Find(filepath, white_list) and not Find(filepath, black_list):
712 files.append(filepath)
713 return files
714
715
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000716def GetPylint(input_api, output_api, white_list=None, black_list=None,
dtu@chromium.orgf23524d2016-01-27 20:08:49 +0000717 disabled_warnings=None, extra_paths_list=None, pylintrc=None):
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000718 """Run pylint on python files.
719
chrisha@google.com267d6592012-06-19 19:23:31 +0000720 The default white_list enforces looking only at *.py files.
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000721 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +0000722 white_list = tuple(white_list or ('.*\.py$',))
723 black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000724 extra_paths_list = extra_paths_list or []
725
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000726 if input_api.is_committing:
727 error_type = output_api.PresubmitError
728 else:
729 error_type = output_api.PresubmitPromptWarning
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000730
731 # Only trigger if there is at least one python file affected.
ilevy@chromium.org36576332013-01-08 03:16:15 +0000732 def rel_path(regex):
733 """Modifies a regex for a subject to accept paths relative to root."""
chrisha@chromium.org40e5d3d2013-01-18 17:46:57 +0000734 def samefile(a, b):
735 # Default implementation for platforms lacking os.path.samefile
736 # (like Windows).
737 return input_api.os_path.abspath(a) == input_api.os_path.abspath(b)
738 samefile = getattr(input_api.os_path, 'samefile', samefile)
739 if samefile(input_api.PresubmitLocalPath(),
740 input_api.change.RepositoryRoot()):
ilevy@chromium.orgdd4e9312013-01-15 03:22:04 +0000741 return regex
chrisha@chromium.org40e5d3d2013-01-18 17:46:57 +0000742
ilevy@chromium.org36576332013-01-08 03:16:15 +0000743 prefix = input_api.os_path.join(input_api.os_path.relpath(
744 input_api.PresubmitLocalPath(), input_api.change.RepositoryRoot()), '')
745 return input_api.re.escape(prefix) + regex
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000746 src_filter = lambda x: input_api.FilterSourceFile(
747 x, map(rel_path, white_list), map(rel_path, black_list))
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000748 if not input_api.AffectedSourceFiles(src_filter):
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000749 input_api.logging.info('Skipping pylint: no matching changes.')
maruel@chromium.orgbf38a7e2010-12-14 18:15:54 +0000750 return []
751
agable@chromium.org327d72b2015-04-21 20:22:50 +0000752 if pylintrc is not None:
753 pylintrc = input_api.os_path.join(input_api.PresubmitLocalPath(), pylintrc)
754 else:
755 pylintrc = input_api.os_path.join(_HERE, 'pylintrc')
dtu@chromium.orgf23524d2016-01-27 20:08:49 +0000756 extra_args = ['--rcfile=%s' % pylintrc]
maruel@chromium.orgff9a2172012-04-24 16:55:32 +0000757 if disabled_warnings:
758 extra_args.extend(['-d', ','.join(disabled_warnings)])
759
chrisha@google.com267d6592012-06-19 19:23:31 +0000760 files = _FetchAllFiles(input_api, white_list, black_list)
761 if not files:
maruel@chromium.orgfca53392010-12-21 18:42:57 +0000762 return []
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000763 files.sort()
chrisha@google.com267d6592012-06-19 19:23:31 +0000764
csharp@chromium.org40395342013-02-21 14:57:23 +0000765 input_api.logging.info('Running pylint on %d files', len(files))
766 input_api.logging.debug('Running pylint on: %s', files)
chrisha@google.com267d6592012-06-19 19:23:31 +0000767 # Copy the system path to the environment so pylint can find the right
768 # imports.
769 env = input_api.environ.copy()
770 import sys
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000771 env['PYTHONPATH'] = input_api.os_path.pathsep.join(
robertshield@chromium.orga2873932013-02-20 18:08:46 +0000772 extra_paths_list + sys.path).encode('utf8')
chrisha@google.com267d6592012-06-19 19:23:31 +0000773
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000774 def GetPylintCmd(flist, extra, parallel):
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000775 # Windows needs help running python files so we explicitly specify
776 # the interpreter to use. It also has limitations on the size of
777 # the command-line, so we pass arguments via a pipe.
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000778 cmd = [input_api.python_executable,
779 input_api.os_path.join(_HERE, 'third_party', 'pylint.py'),
780 '--args-on-stdin']
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000781 if len(flist) == 1:
782 description = flist[0]
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000783 else:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000784 description = '%s files' % len(flist)
chrisha@chromium.org1b129e52012-06-22 17:08:11 +0000785
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000786 args = extra_args[:]
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000787 if extra:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000788 args.extend(extra)
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000789 description += ' using %s' % (extra,)
790 if parallel:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000791 args.append('--jobs=%s' % input_api.cpu_count)
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000792 description += ' on %d cores' % input_api.cpu_count
793
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000794 return input_api.Command(
795 name='Pylint (%s)' % description,
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000796 cmd=cmd,
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000797 kwargs={'env': env, 'stdin': '\n'.join(args + flist)},
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000798 message=error_type)
chrisha@chromium.org1b129e52012-06-22 17:08:11 +0000799
sbc@chromium.org7fc75ca2012-09-21 20:14:21 +0000800 # Always run pylint and pass it all the py files at once.
801 # Passing py files one at time is slower and can produce
802 # different results. input_api.verbose used to be used
803 # to enable this behaviour but differing behaviour in
804 # verbose mode is not desirable.
ilevy@chromium.org7b677f72013-01-07 18:49:26 +0000805 # Leave this unreachable code in here so users can make
806 # a quick local edit to diagnose pylint issues more
807 # easily.
sbc@chromium.org7fc75ca2012-09-21 20:14:21 +0000808 if True:
iannucci@chromium.org0af3bb32015-06-12 20:44:35 +0000809 # pylint's cycle detection doesn't work in parallel, so spawn a second,
810 # single-threaded job for just that check.
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000811
812 # Some PRESUBMITs explicitly mention cycle detection.
813 if not any('R0401' in a or 'cyclic-import' in a for a in extra_args):
814 return [
815 GetPylintCmd(files, ["--disable=cyclic-import"], True),
816 GetPylintCmd(files, ["--disable=all", "--enable=cyclic-import"], False)
817 ]
818 else:
819 return [ GetPylintCmd(files, [], True) ]
820
chrisha@google.com267d6592012-06-19 19:23:31 +0000821 else:
iannucci@chromium.orgd61a4952015-07-01 23:21:26 +0000822 return map(lambda x: GetPylintCmd([x], [], 1), files)
ilevy@chromium.orgbc117312013-04-20 03:57:56 +0000823
824
825def RunPylint(input_api, *args, **kwargs):
826 """Legacy presubmit function.
827
828 For better performance, get all tests and then pass to
829 input_api.RunTests.
830 """
831 return input_api.RunTests(GetPylint(input_api, *args, **kwargs), False)
maruel@chromium.orge94aedc2010-12-13 21:11:30 +0000832
maruel@chromium.orgade9c592011-04-07 15:59:11 +0000833
tandrii@chromium.org5f30f2f2016-04-27 13:19:57 +0000834def CheckRietveldTryJobExecution(dummy_input_api, output_api,
chrisha@google.com267d6592012-06-19 19:23:31 +0000835 dummy_host_url, dummy_platforms,
836 dummy_owner):
tandrii@chromium.org5f30f2f2016-04-27 13:19:57 +0000837 return [
838 output_api.PresubmitNotifyResult(
839 'CheckRietveldTryJobExecution is deprecated, please remove it.')
840 ]
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000841
842
843def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
844 ignored):
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000845 try:
846 connection = input_api.urllib2.urlopen(url)
847 raw_data = connection.read()
848 connection.close()
849 except IOError:
850 return [output_api.PresubmitNotifyResult('%s is not accessible' % url)]
851
852 try:
853 data = input_api.json.loads(raw_data)
854 except ValueError:
855 return [output_api.PresubmitNotifyResult('Received malformed json while '
856 'looking up buildbot status')]
857
858 out = []
859 for (builder_name, builder) in data.iteritems():
860 if builder_name in ignored:
861 continue
maruel@chromium.orgcf1982c2010-10-04 15:08:28 +0000862 if builder.get('state', '') == 'offline':
863 continue
maruel@chromium.org3fbcb082010-03-19 14:03:28 +0000864 pending_builds_len = len(builder.get('pending_builds', []))
865 if pending_builds_len > max_pendings:
866 out.append('%s has %d build(s) pending' %
867 (builder_name, pending_builds_len))
868 if out:
869 return [output_api.PresubmitPromptWarning(
870 'Build(s) pending. It is suggested to wait that no more than %d '
871 'builds are pending.' % max_pendings,
872 long_text='\n'.join(out))]
873 return []
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000874
875
dpranke@chromium.orgbce925d2015-01-16 22:38:38 +0000876def CheckOwners(input_api, output_api, source_file_filter=None):
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000877 if input_api.is_committing:
878 if input_api.tbr:
879 return [output_api.PresubmitNotifyResult(
880 '--tbr was specified, skipping OWNERS check')]
rmistry@google.com5fc6c8c2015-04-16 16:38:43 +0000881 if input_api.change.issue:
tandrii@chromium.org9dea2ac2016-04-28 06:26:20 +0000882 if input_api.dry_run:
rmistry@google.com5fc6c8c2015-04-16 16:38:43 +0000883 return [output_api.PresubmitNotifyResult(
tandrii@chromium.org57bafac2016-04-28 05:09:03 +0000884 'This is a dry run, skipping OWNERS check')]
rmistry@google.com5fc6c8c2015-04-16 16:38:43 +0000885 else:
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000886 return [output_api.PresubmitError("OWNERS check failed: this change has "
887 "no Rietveld issue number, so we can't check it for approvals.")]
888 needed = 'LGTM from an OWNER'
889 output = output_api.PresubmitError
890 else:
891 needed = 'OWNER reviewers'
892 output = output_api.PresubmitNotifyResult
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000893
dpranke@chromium.orgadd5df42011-03-08 23:04:01 +0000894 affected_files = set([f.LocalPath() for f in
sail@chromium.org5538e022011-05-12 17:53:16 +0000895 input_api.change.AffectedFiles(file_filter=source_file_filter)])
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000896
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000897 owners_db = input_api.owners_db
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000898 owner_email, reviewers = GetCodereviewOwnerAndReviewers(
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000899 input_api,
900 owners_db.email_regexp,
901 approval_needed=input_api.is_committing)
902
dpranke@chromium.orgf6ddfa42013-03-05 21:06:03 +0000903 owner_email = owner_email or input_api.change.author_email
904
dpranke@chromium.orgbce925d2015-01-16 22:38:38 +0000905 if owner_email:
dpranke@chromium.org4c7ce992013-03-06 17:15:40 +0000906 reviewers_plus_owner = set([owner_email]).union(reviewers)
dpranke@chromium.orgdbf8b4e2013-02-28 19:24:16 +0000907 missing_files = owners_db.files_not_covered_by(affected_files,
dpranke@chromium.orgf6ddfa42013-03-05 21:06:03 +0000908 reviewers_plus_owner)
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000909 else:
dpranke@chromium.orgdbf8b4e2013-02-28 19:24:16 +0000910 missing_files = owners_db.files_not_covered_by(affected_files, reviewers)
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000911
dpranke@chromium.org6b1e3ee2013-02-23 00:06:38 +0000912 if missing_files:
zork@chromium.org046e1752012-05-07 05:56:12 +0000913 output_list = [
dpranke@chromium.orgf6ddfa42013-03-05 21:06:03 +0000914 output('Missing %s for these files:\n %s' %
bauerb@chromium.orgb3b52012013-04-18 19:28:04 +0000915 (needed, '\n '.join(sorted(missing_files))))]
zork@chromium.org046e1752012-05-07 05:56:12 +0000916 if not input_api.is_committing:
dpranke@chromium.org31b42c02013-09-19 19:24:15 +0000917 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
scheib@chromium.org93276ab2013-10-14 23:55:32 +0000918 output_list.append(output('Suggested OWNERS: ' +
919 '(Use "git-cl owners" to interactively select owners.)\n %s' %
920 ('\n '.join(suggested_owners or []))))
zork@chromium.org046e1752012-05-07 05:56:12 +0000921 return output_list
dpranke@chromium.org2a009622011-03-01 02:43:31 +0000922
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000923 if input_api.is_committing and not reviewers:
924 return [output('Missing LGTM from someone other than %s' % owner_email)]
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000925 return []
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000926
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000927def GetCodereviewOwnerAndReviewers(input_api, email_regexp, approval_needed):
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000928 """Return the owner and reviewers of a change, if any.
929
930 If approval_needed is True, only reviewers who have approved the change
931 will be returned.
932 """
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000933 # Rietveld is default.
934 func = _RietveldOwnerAndReviewers
935 if input_api.gerrit:
936 func = _GerritOwnerAndReviewers
937 return func(input_api, email_regexp, approval_needed)
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000938
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000939
ilevy@chromium.org1fa5cb92012-12-05 04:04:42 +0000940def _GetRietveldIssueProps(input_api, messages):
941 """Gets the issue properties from rietveld."""
942 issue = input_api.change.issue
943 if issue and input_api.rietveld:
944 return input_api.rietveld.get_issue_properties(
945 issue=int(issue), messages=messages)
946
947
isherman@chromium.orgb5cded62014-03-25 17:47:57 +0000948def _ReviewersFromChange(change):
949 """Return the reviewers specified in the |change|, if any."""
950 reviewers = set()
951 if change.R:
952 reviewers.update(set([r.strip() for r in change.R.split(',')]))
953 if change.TBR:
954 reviewers.update(set([r.strip() for r in change.TBR.split(',')]))
isherman@chromium.org103ae032014-04-09 09:06:19 +0000955
956 # Drop reviewers that aren't specified in email address format.
957 return set(reviewer for reviewer in reviewers if '@' in reviewer)
isherman@chromium.orgb5cded62014-03-25 17:47:57 +0000958
959
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000960def _match_reviewer_email(r, owner_email, email_regexp):
961 return email_regexp.match(r) and r != owner_email
962
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000963def _RietveldOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
964 """Return the owner and reviewers of a change, if any.
965
966 If approval_needed is True, only reviewers who have approved the change
967 will be returned.
968 """
ilevy@chromium.org1fa5cb92012-12-05 04:04:42 +0000969 issue_props = _GetRietveldIssueProps(input_api, True)
970 if not issue_props:
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000971 return None, (set() if approval_needed else
972 _ReviewersFromChange(input_api.change))
maruel@chromium.orge4067ab2011-06-03 01:07:35 +0000973
pam@chromium.orgf46aed92012-03-08 09:18:17 +0000974 if not approval_needed:
975 return issue_props['owner_email'], set(issue_props['reviewers'])
976
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000977 owner_email = issue_props['owner_email']
dpranke@chromium.org3e331bd2011-03-24 23:13:04 +0000978
maruel@chromium.org80941c22011-05-30 20:14:18 +0000979 messages = issue_props.get('messages', [])
980 approvers = set(
981 m['sender'] for m in messages
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000982 if m.get('approval') and _match_reviewer_email(m['sender'], owner_email,
983 email_regexp))
maruel@chromium.org80941c22011-05-30 20:14:18 +0000984 return owner_email, approvers
dpranke@chromium.org627ea672011-03-11 23:29:03 +0000985
bradnelson@google.com56e48bc2011-03-24 20:51:21 +0000986
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000987def _GerritOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
988 """Return the owner and reviewers of a change, if any.
989
990 If approval_needed is True, only reviewers who have approved the change
991 will be returned.
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000992 """
993 issue = input_api.change.issue
994 if not issue:
tandrii@chromium.org830dc0b2016-05-09 06:26:34 +0000995 return None, (set() if approval_needed else
996 _ReviewersFromChange(input_api.change))
tandrii@chromium.org37b07a72016-04-29 16:42:28 +0000997
998 owner_email = input_api.gerrit.GetChangeOwner(issue)
999 reviewers = set(
1000 r for r in input_api.gerrit.GetChangeReviewers(issue, approval_needed)
1001 if _match_reviewer_email(r, owner_email, email_regexp))
1002 return owner_email, reviewers
1003
1004
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001005def _CheckConstNSObject(input_api, output_api, source_file_filter):
1006 """Checks to make sure no objective-c files have |const NSSomeClass*|."""
mark@chromium.orgedf744d2011-03-28 16:45:34 +00001007 pattern = input_api.re.compile(
dominik.rottsches@intel.com34a92d92015-03-25 07:36:05 +00001008 r'(?<!reinterpret_cast<)'
1009 r'const\s+NS(?!(Point|Range|Rect|Size)\s*\*)\w*\s*\*')
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001010
1011 def objective_c_filter(f):
1012 return (source_file_filter(f) and
mark@chromium.orgedf744d2011-03-28 16:45:34 +00001013 input_api.os_path.splitext(f.LocalPath())[1] in ('.h', '.m', '.mm'))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001014
1015 files = []
1016 for f in input_api.AffectedSourceFiles(objective_c_filter):
1017 contents = input_api.ReadFile(f)
1018 if pattern.search(contents):
1019 files.append(f)
1020
1021 if files:
1022 if input_api.is_committing:
1023 res_type = output_api.PresubmitPromptWarning
1024 else:
1025 res_type = output_api.PresubmitNotifyResult
1026 return [ res_type('|const NSClass*| is wrong, see ' +
1027 'http://dev.chromium.org/developers/clang-mac',
1028 files) ]
1029 return []
1030
1031
bauerb@chromium.org4cea01e2012-03-20 19:49:05 +00001032def CheckSingletonInHeaders(input_api, output_api, source_file_filter=None):
glider@chromium.orgc3617f32015-02-19 16:33:33 +00001033 """Deprecated, must be removed."""
1034 return [
1035 output_api.PresubmitNotifyResult(
1036 'CheckSingletonInHeaders is deprecated, please remove it.')
1037 ]
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001038
1039
1040def PanProjectChecks(input_api, output_api,
1041 excluded_paths=None, text_files=None,
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001042 license_header=None, project_name=None,
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +00001043 owners_check=True, maxlen=80):
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001044 """Checks that ALL chromium orbit projects should use.
1045
1046 These are checks to be run on all Chromium orbit project, including:
1047 Chromium
1048 Native Client
1049 V8
1050 When you update this function, please take this broad scope into account.
1051 Args:
1052 input_api: Bag of input related interfaces.
1053 output_api: Bag of output related interfaces.
1054 excluded_paths: Don't include these paths in common checks.
1055 text_files: Which file are to be treated as documentation text files.
1056 license_header: What license header should be on files.
1057 project_name: What is the name of the project as it appears in the license.
1058 Returns:
1059 A list of warning or error objects.
1060 """
maruel@chromium.org69eaecb2011-06-14 13:09:13 +00001061 excluded_paths = tuple(excluded_paths or [])
1062 text_files = tuple(text_files or (
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +00001063 r'.+\.txt$',
1064 r'.+\.json$',
maruel@chromium.org69eaecb2011-06-14 13:09:13 +00001065 ))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001066 project_name = project_name or 'Chromium'
mark@chromium.org2cc66422012-08-07 21:22:32 +00001067
1068 # Accept any year number from 2006 to the current year, or the special
rvargas@chromium.org02652602014-03-14 19:43:20 +00001069 # 2006-20xx string used on the oldest files. 2006-20xx is deprecated, but
1070 # tolerated on old files.
mark@chromium.org2cc66422012-08-07 21:22:32 +00001071 current_year = int(input_api.time.strftime('%Y'))
1072 allowed_years = (str(s) for s in reversed(xrange(2006, current_year + 1)))
rvargas@chromium.org02652602014-03-14 19:43:20 +00001073 years_re = '(' + '|'.join(allowed_years) + '|2006-2008|2006-2009|2006-2010)'
mark@chromium.org2cc66422012-08-07 21:22:32 +00001074
1075 # The (c) is deprecated, but tolerate it until it's removed from all files.
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001076 license_header = license_header or (
mark@chromium.org2cc66422012-08-07 21:22:32 +00001077 r'.*? Copyright (\(c\) )?%(year)s The %(project)s Authors\. '
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001078 r'All rights reserved\.\n'
1079 r'.*? Use of this source code is governed by a BSD-style license that '
1080 r'can be\n'
dbeam@chromium.orgb2312102012-02-15 02:01:55 +00001081 r'.*? found in the LICENSE file\.(?: \*/)?\n'
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001082 ) % {
mark@chromium.org2cc66422012-08-07 21:22:32 +00001083 'year': years_re,
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001084 'project': project_name,
1085 }
1086
1087 results = []
1088 # This code loads the default black list (e.g. third_party, experimental, etc)
1089 # and add our black list (breakpad, skia and v8 are still not following
1090 # google style and are not really living this repository).
1091 # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage.
1092 black_list = input_api.DEFAULT_BLACK_LIST + excluded_paths
1093 white_list = input_api.DEFAULT_WHITE_LIST + text_files
1094 sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list)
maruel@chromium.orgfe1211a2011-05-28 18:54:17 +00001095 text_files = lambda x: input_api.FilterSourceFile(
1096 x, black_list=black_list, white_list=white_list)
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001097
1098 snapshot_memory = []
1099 def snapshot(msg):
1100 """Measures & prints performance warning if a rule is running slow."""
1101 dt2 = input_api.time.clock()
1102 if snapshot_memory:
1103 delta_ms = int(1000*(dt2 - snapshot_memory[0]))
1104 if delta_ms > 500:
1105 print " %s took a long time: %dms" % (snapshot_memory[1], delta_ms)
1106 snapshot_memory[:] = (dt2, msg)
1107
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001108 if owners_check:
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001109 snapshot("checking owners")
bradnelson@google.comd57771d2011-03-31 19:18:32 +00001110 results.extend(input_api.canned_checks.CheckOwners(
dpranke@chromium.org751797a2011-06-07 18:46:27 +00001111 input_api, output_api, source_file_filter=None))
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001112
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001113 snapshot("checking long lines")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001114 results.extend(input_api.canned_checks.CheckLongLines(
jamesr@chromium.orgaf27f462013-04-04 21:44:22 +00001115 input_api, output_api, maxlen, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001116 snapshot( "checking tabs")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001117 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
1118 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001119 snapshot( "checking stray whitespace")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001120 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
1121 input_api, output_api, source_file_filter=sources))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001122 snapshot("checking nsobjects")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001123 results.extend(_CheckConstNSObject(
1124 input_api, output_api, source_file_filter=sources))
phajdan.jr@chromium.orgd965db32015-11-16 09:46:56 +00001125 snapshot("checking eol style")
1126 results.extend(input_api.canned_checks.CheckChangeSvnEolStyle(
1127 input_api, output_api, source_file_filter=text_files))
1128 snapshot("checking license")
1129 results.extend(input_api.canned_checks.CheckLicense(
1130 input_api, output_api, license_header, source_file_filter=sources))
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +00001131
maruel@chromium.orgb1ce7752011-05-08 13:50:16 +00001132 if input_api.is_committing:
1133 snapshot("checking svn mime types")
1134 results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes(
1135 input_api, output_api))
maruel@chromium.orgcc73ad62011-07-06 17:39:26 +00001136 snapshot("checking was uploaded")
1137 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
1138 input_api, output_api))
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +00001139 snapshot("checking description")
1140 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1141 input_api, output_api))
1142 results.extend(input_api.canned_checks.CheckDoNotSubmitInDescription(
1143 input_api, output_api))
ilevy@chromium.orgc50d7612012-12-05 04:25:14 +00001144 snapshot("checking do not submit in files")
1145 results.extend(input_api.canned_checks.CheckDoNotSubmitInFiles(
1146 input_api, output_api))
nick@chromium.orge06cb4e2011-04-23 01:20:38 +00001147 snapshot("done")
bradnelson@google.com56e48bc2011-03-24 20:51:21 +00001148 return results
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001149
1150
1151def CheckPatchFormatted(input_api, output_api):
1152 import git_cl
enne@chromium.org555cfe42014-01-29 18:21:39 +00001153 cmd = ['cl', 'format', '--dry-run', input_api.PresubmitLocalPath()]
1154 code, _ = git_cl.RunGitWithCode(cmd, suppress_stderr=True)
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001155 if code == 2:
enne@chromium.orge969cc72015-03-13 18:56:54 +00001156 short_path = input_api.basename(input_api.PresubmitLocalPath())
1157 full_path = input_api.os_path.relpath(input_api.PresubmitLocalPath(),
1158 input_api.change.RepositoryRoot())
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001159 return [output_api.PresubmitPromptWarning(
erg@chromium.orge0a7c5d2015-02-23 20:30:08 +00001160 'The %s directory requires source formatting. '
enne@chromium.org7b7b5b22014-10-16 19:23:07 +00001161 'Please run git cl format %s' %
enne@chromium.orge969cc72015-03-13 18:56:54 +00001162 (short_path, full_path))]
enne@chromium.org3b7e15c2014-01-21 17:44:47 +00001163 # As this is just a warning, ignore all other errors if the user
1164 # happens to have a broken clang-format, doesn't use git, etc etc.
1165 return []
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001166
1167
1168def CheckGNFormatted(input_api, output_api):
1169 import gn
1170 affected_files = input_api.AffectedFiles(
1171 include_deletes=False,
1172 file_filter=lambda x: x.LocalPath().endswith('.gn') or
kylechar58edce22016-06-17 06:07:51 -07001173 x.LocalPath().endswith('.gni') or
1174 x.LocalPath().endswith('.typemap'))
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001175 warnings = []
1176 for f in affected_files:
1177 cmd = ['gn', 'format', '--dry-run', f.AbsoluteLocalPath()]
1178 rc = gn.main(cmd)
1179 if rc == 2:
1180 warnings.append(output_api.PresubmitPromptWarning(
brettw4b8ed592016-08-05 16:19:12 -07001181 '%s requires formatting. Please run:\n gn format %s' % (
scottmg@chromium.orgd05ab352014-12-05 17:24:26 +00001182 f.AbsoluteLocalPath(), f.LocalPath())))
1183 # It's just a warning, so ignore other types of failures assuming they'll be
1184 # caught elsewhere.
1185 return warnings