blob: 8caa4b5fc75bf61e0ee809d1a51e0c2764b6a066 [file] [log] [blame]
[email protected]377ab1da2011-03-17 15:36:281# Copyright (c) 2011 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[\\\/].*",
17 r"^net/tools/spdyshark/[\\\/].*",
18 r"^skia[\\\/].*",
19 r"^v8[\\\/].*",
20 r".*MakeFile$",
[email protected]4306417642009-06-11 00:33:4021)
[email protected]ca8d1982009-02-19 16:33:1222
[email protected]ca8d1982009-02-19 16:33:1223
[email protected]eea609a2011-11-18 13:10:1224_TEST_ONLY_WARNING = (
25 'You might be calling functions intended only for testing from\n'
26 'production code. It is OK to ignore this warning if you know what\n'
27 'you are doing, as the heuristics used to detect the situation are\n'
28 'not perfect. The commit queue will not block on this warning.\n'
29 'Email [email protected] if you have questions.')
30
31
32
[email protected]22c9bd72011-03-27 16:47:3933def _CheckNoInterfacesInBase(input_api, output_api):
[email protected]6a4c8e682010-12-19 03:31:3434 """Checks to make sure no files in libbase.a have |@interface|."""
[email protected]839c1392011-04-29 20:15:1935 pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
[email protected]6a4c8e682010-12-19 03:31:3436 files = []
[email protected]22c9bd72011-03-27 16:47:3937 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
[email protected]a766a1322011-09-08 20:46:0538 if (f.LocalPath().startswith('base/') and
[email protected]0b2f07b02011-05-02 17:29:0039 not f.LocalPath().endswith('_unittest.mm')):
[email protected]6a4c8e682010-12-19 03:31:3440 contents = input_api.ReadFile(f)
41 if pattern.search(contents):
42 files.append(f)
43
44 if len(files):
45 return [ output_api.PresubmitError(
46 'Objective-C interfaces or categories are forbidden in libbase. ' +
47 'See http://groups.google.com/a/chromium.org/group/chromium-dev/' +
48 'browse_thread/thread/efb28c10435987fd',
49 files) ]
50 return []
51
[email protected]650c9082010-12-14 14:33:4452
[email protected]55459852011-08-10 15:17:1953def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
54 """Attempts to prevent use of functions intended only for testing in
55 non-testing code. For now this is just a best-effort implementation
56 that ignores header files and may have some false positives. A
57 better implementation would probably need a proper C++ parser.
58 """
59 # We only scan .cc files and the like, as the declaration of
60 # for-testing functions in header files are hard to distinguish from
61 # calls to such functions without a proper C++ parser.
62 source_extensions = r'\.(cc|cpp|cxx|mm)$'
63 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]19e77fd2011-10-20 05:24:0564 file_exclusion_patterns = (
65 r'.*/(test_|mock_).+%s' % source_extensions,
66 r'.+_test_(support|base)%s' % source_extensions,
67 r'.+_(api|browser|perf|unit|ui)?test%s' % source_extensions,
68 r'.+profile_sync_service_harness%s' % source_extensions,
69 )
70 path_exclusion_patterns = (
71 r'.*[/\\](test|tool(s)?)[/\\].*',
72 # At request of folks maintaining this folder.
73 r'chrome[/\\]browser[/\\]automation[/\\].*',
74 )
[email protected]55459852011-08-10 15:17:1975
76 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
77 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
78 exclusion_pattern = input_api.re.compile(
79 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
80 base_function_pattern, base_function_pattern))
81
82 def FilterFile(affected_file):
[email protected]19e77fd2011-10-20 05:24:0583 black_list = (file_exclusion_patterns + path_exclusion_patterns +
[email protected]3afb12a42011-08-15 13:48:3384 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:1985 return input_api.FilterSourceFile(
86 affected_file,
87 white_list=(file_inclusion_pattern, ),
88 black_list=black_list)
89
90 problems = []
91 for f in input_api.AffectedSourceFiles(FilterFile):
92 local_path = f.LocalPath()
93 lines = input_api.ReadFile(f).splitlines()
94 line_number = 0
95 for line in lines:
96 if (inclusion_pattern.search(line) and
97 not exclusion_pattern.search(line)):
98 problems.append(
99 '%s:%d\n %s' % (local_path, line_number, line.strip()))
100 line_number += 1
101
102 if problems:
[email protected]eea609a2011-11-18 13:10:12103 if not input_api.is_committing:
104 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
105 else:
106 # We don't warn on commit, to avoid stopping commits going through CQ.
107 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19108 else:
109 return []
110
111
[email protected]10689ca2011-09-02 02:31:54112def _CheckNoIOStreamInHeaders(input_api, output_api):
113 """Checks to make sure no .h files include <iostream>."""
114 files = []
115 pattern = input_api.re.compile(r'^#include\s*<iostream>',
116 input_api.re.MULTILINE)
117 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
118 if not f.LocalPath().endswith('.h'):
119 continue
120 contents = input_api.ReadFile(f)
121 if pattern.search(contents):
122 files.append(f)
123
124 if len(files):
125 return [ output_api.PresubmitError(
126 'Do not #include <iostream> in header files, since it inserts static ' +
127 'initialization into every file including the header. Instead, ' +
128 '#include <ostream>. See http://crbug.com/94794',
129 files) ]
130 return []
131
132
[email protected]8ea5d4b2011-09-13 21:49:22133def _CheckNoNewWStrings(input_api, output_api):
134 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27135 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22136 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20137 if (not f.LocalPath().endswith(('.cc', '.h')) or
138 f.LocalPath().endswith('test.cc')):
139 continue
[email protected]8ea5d4b2011-09-13 21:49:22140
[email protected]b5c24292011-11-28 14:38:20141 for line_num, line in f.ChangedContents():
[email protected]8ea5d4b2011-09-13 21:49:22142 if 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27143 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]8ea5d4b2011-09-13 21:49:22144
[email protected]55463aa62011-10-12 00:48:27145 if not problems:
146 return []
147 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
148 ' If you are calling an API that accepts a wstring, fix the API.\n' +
149 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22150
151
[email protected]2a8ac9c2011-10-19 17:20:44152def _CheckNoDEPSGIT(input_api, output_api):
153 """Make sure .DEPS.git is never modified manually."""
154 if any(f.LocalPath().endswith('.DEPS.git') for f in
155 input_api.AffectedFiles()):
156 return [output_api.PresubmitError(
157 'Never commit changes to .DEPS.git. This file is maintained by an\n'
158 'automated system based on what\'s in DEPS and your changes will be\n'
159 'overwritten.\n'
160 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
161 'for more information')]
162 return []
163
164
[email protected]b5c24292011-11-28 14:38:20165def _CheckNoFRIEND_TEST(input_api, output_api):
166 """Make sure that gtest's FRIEND_TEST() macro is not used, the
167 FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be used
168 instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
169 problems = []
170
171 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
172 for f in input_api.AffectedFiles(file_filter=file_filter):
173 for line_num, line in f.ChangedContents():
174 if 'FRIEND_TEST(' in line:
175 problems.append(' %s:%d' % (f.LocalPath(), line_num))
176
177 if not problems:
178 return []
179 return [output_api.PresubmitPromptWarning('Chromium code should not use '
[email protected]24a4ac62011-11-29 15:30:33180 'gtest\'s FRIEND_TEST() macro. Include base/gtest_prod_util.h and use '
[email protected]b5c24292011-11-28 14:38:20181 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
182
183
[email protected]ecdf8ea52011-11-28 18:40:07184def _CheckNoNewOldCallback(input_api, output_api):
185 """Checks to make sure we don't introduce new uses of old callbacks."""
186
187 def HasOldCallbackKeywords(line):
188 """Returns True if a line of text contains keywords that indicate the use
189 of the old callback system.
190 """
191 return ('NewRunnableMethod' in line or
192 'NewRunnableFunction' in line or
193 'NewCallback' in line or
194 input_api.re.search(r'\bCallback\d<', line) or
195 input_api.re.search(r'\bpublic Task\b', line) or
196 'public CancelableTask' in line)
197
198 problems = []
199 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
200 for f in input_api.AffectedFiles(file_filter=file_filter):
201 if not any(HasOldCallbackKeywords(line) for line in f.NewContents()):
202 continue
203 for line_num, line in f.ChangedContents():
204 if HasOldCallbackKeywords(line):
205 problems.append(' %s:%d' % (f.LocalPath(), line_num))
206
207 if not problems:
208 return []
209 return [output_api.PresubmitPromptWarning('The old callback system is '
210 'deprecated. If possible, use base::Bind and base::Callback instead.\n' +
211 '\n'.join(problems))]
212
213
[email protected]22c9bd72011-03-27 16:47:39214def _CommonChecks(input_api, output_api):
215 """Checks common to both upload and commit."""
216 results = []
217 results.extend(input_api.canned_checks.PanProjectChecks(
218 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
219 results.extend(_CheckNoInterfacesInBase(input_api, output_api))
[email protected]66daa702011-05-28 14:41:46220 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19221 results.extend(
222 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54223 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22224 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44225 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]b5c24292011-11-28 14:38:20226 results.extend(_CheckNoFRIEND_TEST(input_api, output_api))
[email protected]ecdf8ea52011-11-28 18:40:07227 results.extend(_CheckNoNewOldCallback(input_api, output_api))
[email protected]22c9bd72011-03-27 16:47:39228 return results
[email protected]1f7b4172010-01-28 01:17:34229
[email protected]b337cb5b2011-01-23 21:24:05230
231def _CheckSubversionConfig(input_api, output_api):
232 """Verifies the subversion config file is correctly setup.
233
234 Checks that autoprops are enabled, returns an error otherwise.
235 """
236 join = input_api.os_path.join
237 if input_api.platform == 'win32':
238 appdata = input_api.environ.get('APPDATA', '')
239 if not appdata:
240 return [output_api.PresubmitError('%APPDATA% is not configured.')]
241 path = join(appdata, 'Subversion', 'config')
242 else:
243 home = input_api.environ.get('HOME', '')
244 if not home:
245 return [output_api.PresubmitError('$HOME is not configured.')]
246 path = join(home, '.subversion', 'config')
247
248 error_msg = (
249 'Please look at http://dev.chromium.org/developers/coding-style to\n'
250 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20251 'properties to simplify the project maintenance.\n'
252 'Pro-tip: just download and install\n'
253 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05254
255 try:
256 lines = open(path, 'r').read().splitlines()
257 # Make sure auto-props is enabled and check for 2 Chromium standard
258 # auto-prop.
259 if (not '*.cc = svn:eol-style=LF' in lines or
260 not '*.pdf = svn:mime-type=application/pdf' in lines or
261 not 'enable-auto-props = yes' in lines):
262 return [
[email protected]79ed7e62011-02-21 21:08:53263 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05264 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56265 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05266 ]
267 except (OSError, IOError):
268 return [
[email protected]79ed7e62011-02-21 21:08:53269 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05270 'Can\'t find your subversion config file.\n' + error_msg)
271 ]
272 return []
273
274
[email protected]66daa702011-05-28 14:41:46275def _CheckAuthorizedAuthor(input_api, output_api):
276 """For non-googler/chromites committers, verify the author's email address is
277 in AUTHORS.
278 """
[email protected]9bb9cb82011-06-13 20:43:01279 # TODO(maruel): Add it to input_api?
280 import fnmatch
281
[email protected]66daa702011-05-28 14:41:46282 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01283 if not author:
284 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46285 return []
[email protected]c99663292011-05-31 19:46:08286 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46287 input_api.PresubmitLocalPath(), 'AUTHORS')
288 valid_authors = (
289 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
290 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18291 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01292 if input_api.verbose:
293 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44294 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46295 return [output_api.PresubmitPromptWarning(
296 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
297 '\n'
298 'http://www.chromium.org/developers/contributing-code and read the '
299 '"Legal" section\n'
300 'If you are a chromite, verify the contributor signed the CLA.') %
301 author)]
302 return []
303
304
[email protected]1f7b4172010-01-28 01:17:34305def CheckChangeOnUpload(input_api, output_api):
306 results = []
307 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54308 return results
[email protected]ca8d1982009-02-19 16:33:12309
310
311def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54312 results = []
[email protected]1f7b4172010-01-28 01:17:34313 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51314 # TODO(thestig) temporarily disabled, doesn't work in third_party/
315 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
316 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54317 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27318 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34319 input_api,
320 output_api,
[email protected]4efa42142010-08-26 01:29:26321 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27322 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
[email protected]4ddc5df2011-12-12 03:05:04323 output_api, 'http://codereview.chromium.org',
324 ('win_rel', 'linux_rel', 'mac_rel'), '[email protected]'))
[email protected]806e98e2010-03-19 17:49:27325
[email protected]3e4eb112011-01-18 03:29:54326 results.extend(input_api.canned_checks.CheckChangeHasBugField(
327 input_api, output_api))
328 results.extend(input_api.canned_checks.CheckChangeHasTestField(
329 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:41330 results.extend(input_api.canned_checks.CheckChangeHasDescription(
331 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05332 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54333 return results
[email protected]ca8d1982009-02-19 16:33:12334
335
[email protected]5efb2a822011-09-27 23:06:13336def GetPreferredTrySlaves(project, change):
337 only_objc_files = all(
338 f.LocalPath().endswith(('.mm', '.m')) for f in change.AffectedFiles())
339 if only_objc_files:
[email protected]4ddc5df2011-12-12 03:05:04340 return ['mac_rel']
[email protected]9d16ad12011-12-14 20:49:47341 preferred = ['win_rel', 'linux_rel', 'mac_rel']
342 aura_re = '_aura[^/]*[.][^/]*'
343 if any(re.search(aura_re, f.LocalPath()) for f in change.AffectedFiles()):
344 preferred.append('linux_aura:compile')
345 return preferred