blob: ab24b57ecc65c99bc9570cd00872f4ca9e0f769c [file] [log] [blame]
[email protected]a18130a2012-01-03 17:52:081# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ca8d1982009-02-19 16:33:122# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Top-level presubmit script for Chromium.
6
[email protected]f1293792009-07-31 18:09:567See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
[email protected]50d7d721e2009-11-15 17:56:188for more details about the presubmit API built into gcl.
[email protected]ca8d1982009-02-19 16:33:129"""
10
[email protected]eea609a2011-11-18 13:10:1211
[email protected]9d16ad12011-12-14 20:49:4712import re
13
14
[email protected]379e7dd2010-01-28 17:39:2115_EXCLUDED_PATHS = (
[email protected]3e4eb112011-01-18 03:29:5416 r"^breakpad[\\\/].*",
[email protected]a18130a2012-01-03 17:52:0817 r"^native_client_sdk[\\\/].*",
18 r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
[email protected]3e4eb112011-01-18 03:29:5419 r"^skia[\\\/].*",
20 r"^v8[\\\/].*",
21 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5322 r".+_autogen\.h$",
[email protected]4306417642009-06-11 00:33:4023)
[email protected]ca8d1982009-02-19 16:33:1224
[email protected]ca8d1982009-02-19 16:33:1225
[email protected]eea609a2011-11-18 13:10:1226_TEST_ONLY_WARNING = (
27 'You might be calling functions intended only for testing from\n'
28 'production code. It is OK to ignore this warning if you know what\n'
29 'you are doing, as the heuristics used to detect the situation are\n'
30 'not perfect. The commit queue will not block on this warning.\n'
31 'Email [email protected] if you have questions.')
32
33
[email protected]127f18ec2012-06-16 05:05:5934_BANNED_OBJC_FUNCTIONS = (
35 (
36 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:2037 (
38 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:5939 'prohibited. Please use CrTrackingArea instead.',
40 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
41 ),
42 False,
43 ),
44 (
45 'NSTrackingArea',
[email protected]23e6cbc2012-06-16 18:51:2046 (
47 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:5948 'instead.',
49 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
50 ),
51 False,
52 ),
53 (
54 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:2055 (
56 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5957 'Please use |convertPoint:(point) fromView:nil| instead.',
58 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
59 ),
60 True,
61 ),
62 (
63 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:2064 (
65 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5966 'Please use |convertPoint:(point) toView:nil| instead.',
67 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
68 ),
69 True,
70 ),
71 (
72 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:2073 (
74 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5975 'Please use |convertRect:(point) fromView:nil| instead.',
76 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
77 ),
78 True,
79 ),
80 (
81 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:2082 (
83 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5984 'Please use |convertRect:(point) toView:nil| instead.',
85 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
86 ),
87 True,
88 ),
89 (
90 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:2091 (
92 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5993 'Please use |convertSize:(point) fromView:nil| instead.',
94 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
95 ),
96 True,
97 ),
98 (
99 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20100 (
101 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59102 'Please use |convertSize:(point) toView:nil| instead.',
103 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
104 ),
105 True,
106 ),
107)
108
109
110_BANNED_CPP_FUNCTIONS = (
[email protected]23e6cbc2012-06-16 18:51:20111 # Make sure that gtest's FRIEND_TEST() macro is not used; the
112 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
113 # used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes.
114 (
115 'FRIEND_TEST(',
116 (
117 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include'
118 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
119 ),
120 False,
121 ),
122 (
123 'ScopedAllowIO',
124 (
125 'New code should not use ScopedAllowIO. Post a task to the blocking pool'
126 'or the FILE thread instead.',
127 ),
128 False,
129 ),
130 (
131 'FilePathWatcher::Delegate',
132 (
133 'New code should not use FilePathWatcher::Delegate. Use the callback'
134 'interface instead.',
135 ),
136 False,
137 ),
[email protected]127f18ec2012-06-16 05:05:59138)
139
140
[email protected]eea609a2011-11-18 13:10:12141
[email protected]55459852011-08-10 15:17:19142def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
143 """Attempts to prevent use of functions intended only for testing in
144 non-testing code. For now this is just a best-effort implementation
145 that ignores header files and may have some false positives. A
146 better implementation would probably need a proper C++ parser.
147 """
148 # We only scan .cc files and the like, as the declaration of
149 # for-testing functions in header files are hard to distinguish from
150 # calls to such functions without a proper C++ parser.
[email protected]403bfbc92012-06-11 23:30:09151 platform_specifiers = r'(_(android|chromeos|gtk|mac|posix|win))?'
[email protected]55459852011-08-10 15:17:19152 source_extensions = r'\.(cc|cpp|cxx|mm)$'
153 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]19e77fd2011-10-20 05:24:05154 file_exclusion_patterns = (
[email protected]e21ce382012-01-04 18:48:25155 r'.*[/\\](test_|mock_).+%s' % source_extensions,
[email protected]c762d252012-02-28 02:07:24156 r'.+_test_(base|support|util)%s' % source_extensions,
[email protected]403bfbc92012-06-11 23:30:09157 r'.+_(api|browser|perf|unit|ui)?test%s%s' % (platform_specifiers,
158 source_extensions),
[email protected]19e77fd2011-10-20 05:24:05159 r'.+profile_sync_service_harness%s' % source_extensions,
160 )
161 path_exclusion_patterns = (
162 r'.*[/\\](test|tool(s)?)[/\\].*',
163 # At request of folks maintaining this folder.
164 r'chrome[/\\]browser[/\\]automation[/\\].*',
165 )
[email protected]55459852011-08-10 15:17:19166
167 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
168 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
169 exclusion_pattern = input_api.re.compile(
170 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
171 base_function_pattern, base_function_pattern))
172
173 def FilterFile(affected_file):
[email protected]19e77fd2011-10-20 05:24:05174 black_list = (file_exclusion_patterns + path_exclusion_patterns +
[email protected]3afb12a42011-08-15 13:48:33175 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19176 return input_api.FilterSourceFile(
177 affected_file,
178 white_list=(file_inclusion_pattern, ),
179 black_list=black_list)
180
181 problems = []
182 for f in input_api.AffectedSourceFiles(FilterFile):
183 local_path = f.LocalPath()
184 lines = input_api.ReadFile(f).splitlines()
185 line_number = 0
186 for line in lines:
187 if (inclusion_pattern.search(line) and
188 not exclusion_pattern.search(line)):
189 problems.append(
190 '%s:%d\n %s' % (local_path, line_number, line.strip()))
191 line_number += 1
192
193 if problems:
[email protected]eea609a2011-11-18 13:10:12194 if not input_api.is_committing:
195 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
196 else:
197 # We don't warn on commit, to avoid stopping commits going through CQ.
198 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19199 else:
200 return []
201
202
[email protected]10689ca2011-09-02 02:31:54203def _CheckNoIOStreamInHeaders(input_api, output_api):
204 """Checks to make sure no .h files include <iostream>."""
205 files = []
206 pattern = input_api.re.compile(r'^#include\s*<iostream>',
207 input_api.re.MULTILINE)
208 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
209 if not f.LocalPath().endswith('.h'):
210 continue
211 contents = input_api.ReadFile(f)
212 if pattern.search(contents):
213 files.append(f)
214
215 if len(files):
216 return [ output_api.PresubmitError(
217 'Do not #include <iostream> in header files, since it inserts static ' +
218 'initialization into every file including the header. Instead, ' +
219 '#include <ostream>. See http://crbug.com/94794',
220 files) ]
221 return []
222
223
[email protected]72df4e782012-06-21 16:28:18224def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
225 """Checks to make sure no source files use UNIT_TEST"""
226 problems = []
227 for f in input_api.AffectedFiles():
228 if (not f.LocalPath().endswith(('.cc', '.mm'))):
229 continue
230
231 for line_num, line in f.ChangedContents():
232 if 'UNIT_TEST' in line:
233 problems.append(' %s:%d' % (f.LocalPath(), line_num))
234
235 if not problems:
236 return []
237 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
238 '\n'.join(problems))]
239
240
[email protected]8ea5d4b2011-09-13 21:49:22241def _CheckNoNewWStrings(input_api, output_api):
242 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27243 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22244 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20245 if (not f.LocalPath().endswith(('.cc', '.h')) or
246 f.LocalPath().endswith('test.cc')):
247 continue
[email protected]8ea5d4b2011-09-13 21:49:22248
[email protected]b5c24292011-11-28 14:38:20249 for line_num, line in f.ChangedContents():
[email protected]8ea5d4b2011-09-13 21:49:22250 if 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27251 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]8ea5d4b2011-09-13 21:49:22252
[email protected]55463aa62011-10-12 00:48:27253 if not problems:
254 return []
255 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
256 ' If you are calling an API that accepts a wstring, fix the API.\n' +
257 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22258
259
[email protected]2a8ac9c2011-10-19 17:20:44260def _CheckNoDEPSGIT(input_api, output_api):
261 """Make sure .DEPS.git is never modified manually."""
262 if any(f.LocalPath().endswith('.DEPS.git') for f in
263 input_api.AffectedFiles()):
264 return [output_api.PresubmitError(
265 'Never commit changes to .DEPS.git. This file is maintained by an\n'
266 'automated system based on what\'s in DEPS and your changes will be\n'
267 'overwritten.\n'
268 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
269 'for more information')]
270 return []
271
272
[email protected]127f18ec2012-06-16 05:05:59273def _CheckNoBannedFunctions(input_api, output_api):
274 """Make sure that banned functions are not used."""
275 warnings = []
276 errors = []
277
278 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
279 for f in input_api.AffectedFiles(file_filter=file_filter):
280 for line_num, line in f.ChangedContents():
281 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
282 if func_name in line:
283 problems = warnings;
284 if error:
285 problems = errors;
286 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
287 for message_line in message:
288 problems.append(' %s' % message_line)
289
290 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
291 for f in input_api.AffectedFiles(file_filter=file_filter):
292 for line_num, line in f.ChangedContents():
293 for func_name, message, error in _BANNED_CPP_FUNCTIONS:
294 if func_name in line:
295 problems = warnings;
296 if error:
297 problems = errors;
298 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
299 for message_line in message:
300 problems.append(' %s' % message_line)
301
302 result = []
303 if (warnings):
304 result.append(output_api.PresubmitPromptWarning(
305 'Banned functions were used.\n' + '\n'.join(warnings)))
306 if (errors):
307 result.append(output_api.PresubmitError(
308 'Banned functions were used.\n' + '\n'.join(errors)))
309 return result
310
311
312
[email protected]22c9bd72011-03-27 16:47:39313def _CommonChecks(input_api, output_api):
314 """Checks common to both upload and commit."""
315 results = []
316 results.extend(input_api.canned_checks.PanProjectChecks(
317 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
[email protected]66daa702011-05-28 14:41:46318 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19319 results.extend(
320 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54321 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:18322 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22323 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44324 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:59325 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]22c9bd72011-03-27 16:47:39326 return results
[email protected]1f7b4172010-01-28 01:17:34327
[email protected]b337cb5b2011-01-23 21:24:05328
329def _CheckSubversionConfig(input_api, output_api):
330 """Verifies the subversion config file is correctly setup.
331
332 Checks that autoprops are enabled, returns an error otherwise.
333 """
334 join = input_api.os_path.join
335 if input_api.platform == 'win32':
336 appdata = input_api.environ.get('APPDATA', '')
337 if not appdata:
338 return [output_api.PresubmitError('%APPDATA% is not configured.')]
339 path = join(appdata, 'Subversion', 'config')
340 else:
341 home = input_api.environ.get('HOME', '')
342 if not home:
343 return [output_api.PresubmitError('$HOME is not configured.')]
344 path = join(home, '.subversion', 'config')
345
346 error_msg = (
347 'Please look at http://dev.chromium.org/developers/coding-style to\n'
348 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20349 'properties to simplify the project maintenance.\n'
350 'Pro-tip: just download and install\n'
351 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05352
353 try:
354 lines = open(path, 'r').read().splitlines()
355 # Make sure auto-props is enabled and check for 2 Chromium standard
356 # auto-prop.
357 if (not '*.cc = svn:eol-style=LF' in lines or
358 not '*.pdf = svn:mime-type=application/pdf' in lines or
359 not 'enable-auto-props = yes' in lines):
360 return [
[email protected]79ed7e62011-02-21 21:08:53361 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05362 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56363 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05364 ]
365 except (OSError, IOError):
366 return [
[email protected]79ed7e62011-02-21 21:08:53367 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05368 'Can\'t find your subversion config file.\n' + error_msg)
369 ]
370 return []
371
372
[email protected]66daa702011-05-28 14:41:46373def _CheckAuthorizedAuthor(input_api, output_api):
374 """For non-googler/chromites committers, verify the author's email address is
375 in AUTHORS.
376 """
[email protected]9bb9cb82011-06-13 20:43:01377 # TODO(maruel): Add it to input_api?
378 import fnmatch
379
[email protected]66daa702011-05-28 14:41:46380 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01381 if not author:
382 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46383 return []
[email protected]c99663292011-05-31 19:46:08384 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46385 input_api.PresubmitLocalPath(), 'AUTHORS')
386 valid_authors = (
387 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
388 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18389 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01390 if input_api.verbose:
391 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44392 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46393 return [output_api.PresubmitPromptWarning(
394 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
395 '\n'
396 'http://www.chromium.org/developers/contributing-code and read the '
397 '"Legal" section\n'
398 'If you are a chromite, verify the contributor signed the CLA.') %
399 author)]
400 return []
401
402
[email protected]1f7b4172010-01-28 01:17:34403def CheckChangeOnUpload(input_api, output_api):
404 results = []
405 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54406 return results
[email protected]ca8d1982009-02-19 16:33:12407
408
409def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54410 results = []
[email protected]1f7b4172010-01-28 01:17:34411 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51412 # TODO(thestig) temporarily disabled, doesn't work in third_party/
413 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
414 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54415 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27416 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34417 input_api,
418 output_api,
[email protected]4efa42142010-08-26 01:29:26419 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27420 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
[email protected]4ddc5df2011-12-12 03:05:04421 output_api, 'http://codereview.chromium.org',
[email protected]c1ba4c52012-03-09 14:23:28422 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
423 '[email protected]'))
[email protected]806e98e2010-03-19 17:49:27424
[email protected]3e4eb112011-01-18 03:29:54425 results.extend(input_api.canned_checks.CheckChangeHasBugField(
426 input_api, output_api))
427 results.extend(input_api.canned_checks.CheckChangeHasTestField(
428 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:41429 results.extend(input_api.canned_checks.CheckChangeHasDescription(
430 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05431 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54432 return results
[email protected]ca8d1982009-02-19 16:33:12433
434
[email protected]5efb2a822011-09-27 23:06:13435def GetPreferredTrySlaves(project, change):
[email protected]50c30092012-02-15 04:21:36436 affected_files = change.LocalPaths()
437 only_objc_files = all(f.endswith(('.mm', '.m')) for f in affected_files)
[email protected]5efb2a822011-09-27 23:06:13438 if only_objc_files:
[email protected]4ddc5df2011-12-12 03:05:04439 return ['mac_rel']
[email protected]1f2629892012-05-07 19:23:12440 preferred = ['win_rel', 'linux_rel', 'mac_rel', 'linux_clang:compile']
[email protected]9d16ad12011-12-14 20:49:47441 aura_re = '_aura[^/]*[.][^/]*'
[email protected]50c30092012-02-15 04:21:36442 if any(re.search(aura_re, f) for f in affected_files):
[email protected]e9b23882012-02-03 01:05:49443 preferred.append('linux_chromeos')
[email protected]d3b7e7cca2012-03-01 21:25:06444 # Nothing in chrome/
445 android_re_list = ('^base/',
446 '^build/common.gypi$',
447 '^content/',
448 '^ipc/',
449 '^jingle/',
450 '^media/',
451 '^net/',
452 '^sql/')
453 # Nothing that looks like win-only or aura-only
454 win_re = '_win\.(cc|h)$'
455 possibly_android = True
456 for non_android_re in (aura_re, win_re):
457 if all(re.search(non_android_re, f) for f in affected_files):
458 possibly_android = False
459 break
460 if possibly_android:
461 for f in change.AffectedFiles():
462 if any(re.search(r, f.LocalPath()) for r in android_re_list):
463 preferred.append('android')
464 break
[email protected]9d16ad12011-12-14 20:49:47465 return preferred