blob: aa79141a90fa5217e6f29f1b7646c0744a21ffd5 [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]4306417642009-06-11 00:33:4022)
[email protected]ca8d1982009-02-19 16:33:1223
[email protected]ca8d1982009-02-19 16:33:1224
[email protected]eea609a2011-11-18 13:10:1225_TEST_ONLY_WARNING = (
26 'You might be calling functions intended only for testing from\n'
27 'production code. It is OK to ignore this warning if you know what\n'
28 'you are doing, as the heuristics used to detect the situation are\n'
29 'not perfect. The commit queue will not block on this warning.\n'
30 'Email [email protected] if you have questions.')
31
32
33
[email protected]22c9bd72011-03-27 16:47:3934def _CheckNoInterfacesInBase(input_api, output_api):
[email protected]6a4c8e682010-12-19 03:31:3435 """Checks to make sure no files in libbase.a have |@interface|."""
[email protected]839c1392011-04-29 20:15:1936 pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
[email protected]6a4c8e682010-12-19 03:31:3437 files = []
[email protected]22c9bd72011-03-27 16:47:3938 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
[email protected]a766a1322011-09-08 20:46:0539 if (f.LocalPath().startswith('base/') and
[email protected]0b2f07b02011-05-02 17:29:0040 not f.LocalPath().endswith('_unittest.mm')):
[email protected]6a4c8e682010-12-19 03:31:3441 contents = input_api.ReadFile(f)
42 if pattern.search(contents):
43 files.append(f)
44
45 if len(files):
46 return [ output_api.PresubmitError(
47 'Objective-C interfaces or categories are forbidden in libbase. ' +
48 'See http://groups.google.com/a/chromium.org/group/chromium-dev/' +
49 'browse_thread/thread/efb28c10435987fd',
50 files) ]
51 return []
52
[email protected]650c9082010-12-14 14:33:4453
[email protected]55459852011-08-10 15:17:1954def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
55 """Attempts to prevent use of functions intended only for testing in
56 non-testing code. For now this is just a best-effort implementation
57 that ignores header files and may have some false positives. A
58 better implementation would probably need a proper C++ parser.
59 """
60 # We only scan .cc files and the like, as the declaration of
61 # for-testing functions in header files are hard to distinguish from
62 # calls to such functions without a proper C++ parser.
63 source_extensions = r'\.(cc|cpp|cxx|mm)$'
64 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]19e77fd2011-10-20 05:24:0565 file_exclusion_patterns = (
[email protected]e21ce382012-01-04 18:48:2566 r'.*[/\\](test_|mock_).+%s' % source_extensions,
[email protected]19e77fd2011-10-20 05:24:0567 r'.+_test_(support|base)%s' % source_extensions,
68 r'.+_(api|browser|perf|unit|ui)?test%s' % source_extensions,
69 r'.+profile_sync_service_harness%s' % source_extensions,
70 )
71 path_exclusion_patterns = (
72 r'.*[/\\](test|tool(s)?)[/\\].*',
73 # At request of folks maintaining this folder.
74 r'chrome[/\\]browser[/\\]automation[/\\].*',
75 )
[email protected]55459852011-08-10 15:17:1976
77 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
78 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
79 exclusion_pattern = input_api.re.compile(
80 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
81 base_function_pattern, base_function_pattern))
82
83 def FilterFile(affected_file):
[email protected]19e77fd2011-10-20 05:24:0584 black_list = (file_exclusion_patterns + path_exclusion_patterns +
[email protected]3afb12a42011-08-15 13:48:3385 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:1986 return input_api.FilterSourceFile(
87 affected_file,
88 white_list=(file_inclusion_pattern, ),
89 black_list=black_list)
90
91 problems = []
92 for f in input_api.AffectedSourceFiles(FilterFile):
93 local_path = f.LocalPath()
94 lines = input_api.ReadFile(f).splitlines()
95 line_number = 0
96 for line in lines:
97 if (inclusion_pattern.search(line) and
98 not exclusion_pattern.search(line)):
99 problems.append(
100 '%s:%d\n %s' % (local_path, line_number, line.strip()))
101 line_number += 1
102
103 if problems:
[email protected]eea609a2011-11-18 13:10:12104 if not input_api.is_committing:
105 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
106 else:
107 # We don't warn on commit, to avoid stopping commits going through CQ.
108 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19109 else:
110 return []
111
112
[email protected]10689ca2011-09-02 02:31:54113def _CheckNoIOStreamInHeaders(input_api, output_api):
114 """Checks to make sure no .h files include <iostream>."""
115 files = []
116 pattern = input_api.re.compile(r'^#include\s*<iostream>',
117 input_api.re.MULTILINE)
118 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
119 if not f.LocalPath().endswith('.h'):
120 continue
121 contents = input_api.ReadFile(f)
122 if pattern.search(contents):
123 files.append(f)
124
125 if len(files):
126 return [ output_api.PresubmitError(
127 'Do not #include <iostream> in header files, since it inserts static ' +
128 'initialization into every file including the header. Instead, ' +
129 '#include <ostream>. See http://crbug.com/94794',
130 files) ]
131 return []
132
133
[email protected]8ea5d4b2011-09-13 21:49:22134def _CheckNoNewWStrings(input_api, output_api):
135 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27136 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22137 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20138 if (not f.LocalPath().endswith(('.cc', '.h')) or
139 f.LocalPath().endswith('test.cc')):
140 continue
[email protected]8ea5d4b2011-09-13 21:49:22141
[email protected]b5c24292011-11-28 14:38:20142 for line_num, line in f.ChangedContents():
[email protected]8ea5d4b2011-09-13 21:49:22143 if 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27144 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]8ea5d4b2011-09-13 21:49:22145
[email protected]55463aa62011-10-12 00:48:27146 if not problems:
147 return []
148 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
149 ' If you are calling an API that accepts a wstring, fix the API.\n' +
150 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22151
152
[email protected]2a8ac9c2011-10-19 17:20:44153def _CheckNoDEPSGIT(input_api, output_api):
154 """Make sure .DEPS.git is never modified manually."""
155 if any(f.LocalPath().endswith('.DEPS.git') for f in
156 input_api.AffectedFiles()):
157 return [output_api.PresubmitError(
158 'Never commit changes to .DEPS.git. This file is maintained by an\n'
159 'automated system based on what\'s in DEPS and your changes will be\n'
160 'overwritten.\n'
161 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
162 'for more information')]
163 return []
164
165
[email protected]b5c24292011-11-28 14:38:20166def _CheckNoFRIEND_TEST(input_api, output_api):
167 """Make sure that gtest's FRIEND_TEST() macro is not used, the
168 FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be used
169 instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
170 problems = []
171
172 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
173 for f in input_api.AffectedFiles(file_filter=file_filter):
174 for line_num, line in f.ChangedContents():
175 if 'FRIEND_TEST(' in line:
176 problems.append(' %s:%d' % (f.LocalPath(), line_num))
177
178 if not problems:
179 return []
180 return [output_api.PresubmitPromptWarning('Chromium code should not use '
[email protected]24a4ac62011-11-29 15:30:33181 'gtest\'s FRIEND_TEST() macro. Include base/gtest_prod_util.h and use '
[email protected]b5c24292011-11-28 14:38:20182 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
183
184
[email protected]ecdf8ea52011-11-28 18:40:07185def _CheckNoNewOldCallback(input_api, output_api):
186 """Checks to make sure we don't introduce new uses of old callbacks."""
187
188 def HasOldCallbackKeywords(line):
189 """Returns True if a line of text contains keywords that indicate the use
190 of the old callback system.
191 """
192 return ('NewRunnableMethod' in line or
[email protected]ecdf8ea52011-11-28 18:40:07193 '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]65d07a42011-12-21 23:31:29341 preferred = ['win_rel', 'linux_rel', 'mac_rel']
[email protected]9d16ad12011-12-14 20:49:47342 aura_re = '_aura[^/]*[.][^/]*'
343 if any(re.search(aura_re, f.LocalPath()) for f in change.AffectedFiles()):
[email protected]e9b23882012-02-03 01:05:49344 preferred.append('linux_chromeos')
[email protected]9d16ad12011-12-14 20:49:47345 return preferred