blob: d4d00ed9796ec373399757dc0fe0929ef0a248c8 [file] [log] [blame]
[email protected]a18130a2012-01-03 17:52:081# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ca8d19842009-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]ca8d19842009-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]ca8d19842009-02-19 16:33:1224
[email protected]ca8d19842009-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
34
[email protected]22c9bd72011-03-27 16:47:3935def _CheckNoInterfacesInBase(input_api, output_api):
[email protected]6a4c8e682010-12-19 03:31:3436 """Checks to make sure no files in libbase.a have |@interface|."""
[email protected]839c1392011-04-29 20:15:1937 pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
[email protected]6a4c8e682010-12-19 03:31:3438 files = []
[email protected]22c9bd72011-03-27 16:47:3939 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
[email protected]a766a1322011-09-08 20:46:0540 if (f.LocalPath().startswith('base/') and
[email protected]0b2f07b02011-05-02 17:29:0041 not f.LocalPath().endswith('_unittest.mm')):
[email protected]6a4c8e682010-12-19 03:31:3442 contents = input_api.ReadFile(f)
43 if pattern.search(contents):
44 files.append(f)
45
46 if len(files):
47 return [ output_api.PresubmitError(
48 'Objective-C interfaces or categories are forbidden in libbase. ' +
49 'See http://groups.google.com/a/chromium.org/group/chromium-dev/' +
50 'browse_thread/thread/efb28c10435987fd',
51 files) ]
52 return []
53
[email protected]650c9082010-12-14 14:33:4454
[email protected]55459852011-08-10 15:17:1955def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
56 """Attempts to prevent use of functions intended only for testing in
57 non-testing code. For now this is just a best-effort implementation
58 that ignores header files and may have some false positives. A
59 better implementation would probably need a proper C++ parser.
60 """
61 # We only scan .cc files and the like, as the declaration of
62 # for-testing functions in header files are hard to distinguish from
63 # calls to such functions without a proper C++ parser.
[email protected]403bfbc92012-06-11 23:30:0964 platform_specifiers = r'(_(android|chromeos|gtk|mac|posix|win))?'
[email protected]55459852011-08-10 15:17:1965 source_extensions = r'\.(cc|cpp|cxx|mm)$'
66 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]19e77fd2011-10-20 05:24:0567 file_exclusion_patterns = (
[email protected]e21ce382012-01-04 18:48:2568 r'.*[/\\](test_|mock_).+%s' % source_extensions,
[email protected]c762d252012-02-28 02:07:2469 r'.+_test_(base|support|util)%s' % source_extensions,
[email protected]403bfbc92012-06-11 23:30:0970 r'.+_(api|browser|perf|unit|ui)?test%s%s' % (platform_specifiers,
71 source_extensions),
[email protected]19e77fd2011-10-20 05:24:0572 r'.+profile_sync_service_harness%s' % source_extensions,
73 )
74 path_exclusion_patterns = (
75 r'.*[/\\](test|tool(s)?)[/\\].*',
76 # At request of folks maintaining this folder.
77 r'chrome[/\\]browser[/\\]automation[/\\].*',
78 )
[email protected]55459852011-08-10 15:17:1979
80 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
81 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
82 exclusion_pattern = input_api.re.compile(
83 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
84 base_function_pattern, base_function_pattern))
85
86 def FilterFile(affected_file):
[email protected]19e77fd2011-10-20 05:24:0587 black_list = (file_exclusion_patterns + path_exclusion_patterns +
[email protected]3afb12a42011-08-15 13:48:3388 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:1989 return input_api.FilterSourceFile(
90 affected_file,
91 white_list=(file_inclusion_pattern, ),
92 black_list=black_list)
93
94 problems = []
95 for f in input_api.AffectedSourceFiles(FilterFile):
96 local_path = f.LocalPath()
97 lines = input_api.ReadFile(f).splitlines()
98 line_number = 0
99 for line in lines:
100 if (inclusion_pattern.search(line) and
101 not exclusion_pattern.search(line)):
102 problems.append(
103 '%s:%d\n %s' % (local_path, line_number, line.strip()))
104 line_number += 1
105
106 if problems:
[email protected]eea609a2011-11-18 13:10:12107 if not input_api.is_committing:
108 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
109 else:
110 # We don't warn on commit, to avoid stopping commits going through CQ.
111 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19112 else:
113 return []
114
115
[email protected]10689ca2011-09-02 02:31:54116def _CheckNoIOStreamInHeaders(input_api, output_api):
117 """Checks to make sure no .h files include <iostream>."""
118 files = []
119 pattern = input_api.re.compile(r'^#include\s*<iostream>',
120 input_api.re.MULTILINE)
121 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
122 if not f.LocalPath().endswith('.h'):
123 continue
124 contents = input_api.ReadFile(f)
125 if pattern.search(contents):
126 files.append(f)
127
128 if len(files):
129 return [ output_api.PresubmitError(
130 'Do not #include <iostream> in header files, since it inserts static ' +
131 'initialization into every file including the header. Instead, ' +
132 '#include <ostream>. See http://crbug.com/94794',
133 files) ]
134 return []
135
136
[email protected]8ea5d4b2011-09-13 21:49:22137def _CheckNoNewWStrings(input_api, output_api):
138 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27139 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22140 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20141 if (not f.LocalPath().endswith(('.cc', '.h')) or
142 f.LocalPath().endswith('test.cc')):
143 continue
[email protected]8ea5d4b2011-09-13 21:49:22144
[email protected]b5c24292011-11-28 14:38:20145 for line_num, line in f.ChangedContents():
[email protected]8ea5d4b2011-09-13 21:49:22146 if 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27147 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]8ea5d4b2011-09-13 21:49:22148
[email protected]55463aa62011-10-12 00:48:27149 if not problems:
150 return []
151 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
152 ' If you are calling an API that accepts a wstring, fix the API.\n' +
153 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22154
155
[email protected]2a8ac9c2011-10-19 17:20:44156def _CheckNoDEPSGIT(input_api, output_api):
157 """Make sure .DEPS.git is never modified manually."""
158 if any(f.LocalPath().endswith('.DEPS.git') for f in
159 input_api.AffectedFiles()):
160 return [output_api.PresubmitError(
161 'Never commit changes to .DEPS.git. This file is maintained by an\n'
162 'automated system based on what\'s in DEPS and your changes will be\n'
163 'overwritten.\n'
164 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
165 'for more information')]
166 return []
167
168
[email protected]b5c24292011-11-28 14:38:20169def _CheckNoFRIEND_TEST(input_api, output_api):
170 """Make sure that gtest's FRIEND_TEST() macro is not used, the
171 FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be used
172 instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
173 problems = []
174
175 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
176 for f in input_api.AffectedFiles(file_filter=file_filter):
177 for line_num, line in f.ChangedContents():
178 if 'FRIEND_TEST(' in line:
179 problems.append(' %s:%d' % (f.LocalPath(), line_num))
180
181 if not problems:
182 return []
183 return [output_api.PresubmitPromptWarning('Chromium code should not use '
[email protected]24a4ac62011-11-29 15:30:33184 'gtest\'s FRIEND_TEST() macro. Include base/gtest_prod_util.h and use '
[email protected]b5c24292011-11-28 14:38:20185 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
186
187
[email protected]7bbc7d12012-03-22 21:44:41188def _CheckNoScopedAllowIO(input_api, output_api):
189 """Make sure that ScopedAllowIO is not used."""
190 problems = []
191
192 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
193 for f in input_api.AffectedFiles(file_filter=file_filter):
194 for line_num, line in f.ChangedContents():
195 if 'ScopedAllowIO' in line:
196 problems.append(' %s:%d' % (f.LocalPath(), line_num))
197
198 if not problems:
199 return []
200 return [output_api.PresubmitPromptWarning('New code should not use '
201 'ScopedAllowIO. Post a task to the blocking pool or the FILE thread '
202 'instead.\n' + '\n'.join(problems))]
203
204
[email protected]10e38b62012-06-04 14:21:27205def _CheckNoFilePathWatcherDelegate(input_api, output_api):
206 """Make sure that FilePathWatcher::Delegate is not used."""
207 problems = []
208
209 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
210 for f in input_api.AffectedFiles(file_filter=file_filter):
211 for line_num, line in f.ChangedContents():
212 if 'FilePathWatcher::Delegate' in line:
213 problems.append(' %s:%d' % (f.LocalPath(), line_num))
214
215 if not problems:
216 return []
217 return [output_api.PresubmitPromptWarning('New code should not use '
218 'FilePathWatcher::Delegate. Use the callback interface instead.\n' +
219 '\n'.join(problems))]
220
221
[email protected]22c9bd72011-03-27 16:47:39222def _CommonChecks(input_api, output_api):
223 """Checks common to both upload and commit."""
224 results = []
225 results.extend(input_api.canned_checks.PanProjectChecks(
226 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
227 results.extend(_CheckNoInterfacesInBase(input_api, output_api))
[email protected]66daa702011-05-28 14:41:46228 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19229 results.extend(
230 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54231 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22232 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44233 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]b5c24292011-11-28 14:38:20234 results.extend(_CheckNoFRIEND_TEST(input_api, output_api))
[email protected]7bbc7d12012-03-22 21:44:41235 results.extend(_CheckNoScopedAllowIO(input_api, output_api))
[email protected]10e38b62012-06-04 14:21:27236 results.extend(_CheckNoFilePathWatcherDelegate(input_api, output_api))
[email protected]22c9bd72011-03-27 16:47:39237 return results
[email protected]1f7b4172010-01-28 01:17:34238
[email protected]b337cb5b2011-01-23 21:24:05239
240def _CheckSubversionConfig(input_api, output_api):
241 """Verifies the subversion config file is correctly setup.
242
243 Checks that autoprops are enabled, returns an error otherwise.
244 """
245 join = input_api.os_path.join
246 if input_api.platform == 'win32':
247 appdata = input_api.environ.get('APPDATA', '')
248 if not appdata:
249 return [output_api.PresubmitError('%APPDATA% is not configured.')]
250 path = join(appdata, 'Subversion', 'config')
251 else:
252 home = input_api.environ.get('HOME', '')
253 if not home:
254 return [output_api.PresubmitError('$HOME is not configured.')]
255 path = join(home, '.subversion', 'config')
256
257 error_msg = (
258 'Please look at http://dev.chromium.org/developers/coding-style to\n'
259 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20260 'properties to simplify the project maintenance.\n'
261 'Pro-tip: just download and install\n'
262 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05263
264 try:
265 lines = open(path, 'r').read().splitlines()
266 # Make sure auto-props is enabled and check for 2 Chromium standard
267 # auto-prop.
268 if (not '*.cc = svn:eol-style=LF' in lines or
269 not '*.pdf = svn:mime-type=application/pdf' in lines or
270 not 'enable-auto-props = yes' in lines):
271 return [
[email protected]79ed7e62011-02-21 21:08:53272 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05273 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56274 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05275 ]
276 except (OSError, IOError):
277 return [
[email protected]79ed7e62011-02-21 21:08:53278 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05279 'Can\'t find your subversion config file.\n' + error_msg)
280 ]
281 return []
282
283
[email protected]66daa702011-05-28 14:41:46284def _CheckAuthorizedAuthor(input_api, output_api):
285 """For non-googler/chromites committers, verify the author's email address is
286 in AUTHORS.
287 """
[email protected]9bb9cb82011-06-13 20:43:01288 # TODO(maruel): Add it to input_api?
289 import fnmatch
290
[email protected]66daa702011-05-28 14:41:46291 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01292 if not author:
293 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46294 return []
[email protected]c99663292011-05-31 19:46:08295 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46296 input_api.PresubmitLocalPath(), 'AUTHORS')
297 valid_authors = (
298 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
299 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18300 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01301 if input_api.verbose:
302 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44303 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46304 return [output_api.PresubmitPromptWarning(
305 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
306 '\n'
307 'http://www.chromium.org/developers/contributing-code and read the '
308 '"Legal" section\n'
309 'If you are a chromite, verify the contributor signed the CLA.') %
310 author)]
311 return []
312
313
[email protected]1f7b4172010-01-28 01:17:34314def CheckChangeOnUpload(input_api, output_api):
315 results = []
316 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54317 return results
[email protected]ca8d19842009-02-19 16:33:12318
319
320def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54321 results = []
[email protected]1f7b4172010-01-28 01:17:34322 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51323 # TODO(thestig) temporarily disabled, doesn't work in third_party/
324 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
325 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54326 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27327 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34328 input_api,
329 output_api,
[email protected]4efa42142010-08-26 01:29:26330 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27331 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
[email protected]4ddc5df2011-12-12 03:05:04332 output_api, 'http://codereview.chromium.org',
[email protected]c1ba4c52012-03-09 14:23:28333 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
334 '[email protected]'))
[email protected]806e98e2010-03-19 17:49:27335
[email protected]3e4eb112011-01-18 03:29:54336 results.extend(input_api.canned_checks.CheckChangeHasBugField(
337 input_api, output_api))
338 results.extend(input_api.canned_checks.CheckChangeHasTestField(
339 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:41340 results.extend(input_api.canned_checks.CheckChangeHasDescription(
341 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05342 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54343 return results
[email protected]ca8d19842009-02-19 16:33:12344
345
[email protected]5efb2a822011-09-27 23:06:13346def GetPreferredTrySlaves(project, change):
[email protected]50c30092012-02-15 04:21:36347 affected_files = change.LocalPaths()
348 only_objc_files = all(f.endswith(('.mm', '.m')) for f in affected_files)
[email protected]5efb2a822011-09-27 23:06:13349 if only_objc_files:
[email protected]4ddc5df2011-12-12 03:05:04350 return ['mac_rel']
[email protected]1f2629892012-05-07 19:23:12351 preferred = ['win_rel', 'linux_rel', 'mac_rel', 'linux_clang:compile']
[email protected]9d16ad12011-12-14 20:49:47352 aura_re = '_aura[^/]*[.][^/]*'
[email protected]50c30092012-02-15 04:21:36353 if any(re.search(aura_re, f) for f in affected_files):
[email protected]e9b23882012-02-03 01:05:49354 preferred.append('linux_chromeos')
[email protected]d3b7e7cca2012-03-01 21:25:06355 # Nothing in chrome/
356 android_re_list = ('^base/',
357 '^build/common.gypi$',
358 '^content/',
359 '^ipc/',
360 '^jingle/',
361 '^media/',
362 '^net/',
363 '^sql/')
364 # Nothing that looks like win-only or aura-only
365 win_re = '_win\.(cc|h)$'
366 possibly_android = True
367 for non_android_re in (aura_re, win_re):
368 if all(re.search(non_android_re, f) for f in affected_files):
369 possibly_android = False
370 break
371 if possibly_android:
372 for f in change.AffectedFiles():
373 if any(re.search(r, f.LocalPath()) for r in android_re_list):
374 preferred.append('android')
375 break
[email protected]9d16ad12011-12-14 20:49:47376 return preferred