blob: 6f9f5307087557db93eb11dc9dbc2e2ec9287f36 [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]379e7dd2010-01-28 17:39:2112_EXCLUDED_PATHS = (
[email protected]3e4eb112011-01-18 03:29:5413 r"^breakpad[\\\/].*",
14 r"^net/tools/spdyshark/[\\\/].*",
15 r"^skia[\\\/].*",
16 r"^v8[\\\/].*",
17 r".*MakeFile$",
[email protected]4306417642009-06-11 00:33:4018)
[email protected]ca8d1982009-02-19 16:33:1219
[email protected]ca8d1982009-02-19 16:33:1220
[email protected]eea609a2011-11-18 13:10:1221_TEST_ONLY_WARNING = (
22 'You might be calling functions intended only for testing from\n'
23 'production code. It is OK to ignore this warning if you know what\n'
24 'you are doing, as the heuristics used to detect the situation are\n'
25 'not perfect. The commit queue will not block on this warning.\n'
26 'Email [email protected] if you have questions.')
27
28
29
[email protected]22c9bd72011-03-27 16:47:3930def _CheckNoInterfacesInBase(input_api, output_api):
[email protected]6a4c8e682010-12-19 03:31:3431 """Checks to make sure no files in libbase.a have |@interface|."""
[email protected]839c1392011-04-29 20:15:1932 pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
[email protected]6a4c8e682010-12-19 03:31:3433 files = []
[email protected]22c9bd72011-03-27 16:47:3934 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
[email protected]a766a1322011-09-08 20:46:0535 if (f.LocalPath().startswith('base/') and
[email protected]0b2f07b02011-05-02 17:29:0036 not f.LocalPath().endswith('_unittest.mm')):
[email protected]6a4c8e682010-12-19 03:31:3437 contents = input_api.ReadFile(f)
38 if pattern.search(contents):
39 files.append(f)
40
41 if len(files):
42 return [ output_api.PresubmitError(
43 'Objective-C interfaces or categories are forbidden in libbase. ' +
44 'See http://groups.google.com/a/chromium.org/group/chromium-dev/' +
45 'browse_thread/thread/efb28c10435987fd',
46 files) ]
47 return []
48
[email protected]650c9082010-12-14 14:33:4449
[email protected]55459852011-08-10 15:17:1950def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
51 """Attempts to prevent use of functions intended only for testing in
52 non-testing code. For now this is just a best-effort implementation
53 that ignores header files and may have some false positives. A
54 better implementation would probably need a proper C++ parser.
55 """
56 # We only scan .cc files and the like, as the declaration of
57 # for-testing functions in header files are hard to distinguish from
58 # calls to such functions without a proper C++ parser.
59 source_extensions = r'\.(cc|cpp|cxx|mm)$'
60 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]19e77fd2011-10-20 05:24:0561 file_exclusion_patterns = (
62 r'.*/(test_|mock_).+%s' % source_extensions,
63 r'.+_test_(support|base)%s' % source_extensions,
64 r'.+_(api|browser|perf|unit|ui)?test%s' % source_extensions,
65 r'.+profile_sync_service_harness%s' % source_extensions,
66 )
67 path_exclusion_patterns = (
68 r'.*[/\\](test|tool(s)?)[/\\].*',
69 # At request of folks maintaining this folder.
70 r'chrome[/\\]browser[/\\]automation[/\\].*',
71 )
[email protected]55459852011-08-10 15:17:1972
73 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
74 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
75 exclusion_pattern = input_api.re.compile(
76 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
77 base_function_pattern, base_function_pattern))
78
79 def FilterFile(affected_file):
[email protected]19e77fd2011-10-20 05:24:0580 black_list = (file_exclusion_patterns + path_exclusion_patterns +
[email protected]3afb12a42011-08-15 13:48:3381 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:1982 return input_api.FilterSourceFile(
83 affected_file,
84 white_list=(file_inclusion_pattern, ),
85 black_list=black_list)
86
87 problems = []
88 for f in input_api.AffectedSourceFiles(FilterFile):
89 local_path = f.LocalPath()
90 lines = input_api.ReadFile(f).splitlines()
91 line_number = 0
92 for line in lines:
93 if (inclusion_pattern.search(line) and
94 not exclusion_pattern.search(line)):
95 problems.append(
96 '%s:%d\n %s' % (local_path, line_number, line.strip()))
97 line_number += 1
98
99 if problems:
[email protected]eea609a2011-11-18 13:10:12100 if not input_api.is_committing:
101 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
102 else:
103 # We don't warn on commit, to avoid stopping commits going through CQ.
104 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19105 else:
106 return []
107
108
[email protected]10689ca2011-09-02 02:31:54109def _CheckNoIOStreamInHeaders(input_api, output_api):
110 """Checks to make sure no .h files include <iostream>."""
111 files = []
112 pattern = input_api.re.compile(r'^#include\s*<iostream>',
113 input_api.re.MULTILINE)
114 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
115 if not f.LocalPath().endswith('.h'):
116 continue
117 contents = input_api.ReadFile(f)
118 if pattern.search(contents):
119 files.append(f)
120
121 if len(files):
122 return [ output_api.PresubmitError(
123 'Do not #include <iostream> in header files, since it inserts static ' +
124 'initialization into every file including the header. Instead, ' +
125 '#include <ostream>. See http://crbug.com/94794',
126 files) ]
127 return []
128
129
[email protected]8ea5d4b2011-09-13 21:49:22130def _CheckNoNewWStrings(input_api, output_api):
131 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27132 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22133 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20134 if (not f.LocalPath().endswith(('.cc', '.h')) or
135 f.LocalPath().endswith('test.cc')):
136 continue
[email protected]8ea5d4b2011-09-13 21:49:22137
[email protected]b5c24292011-11-28 14:38:20138 for line_num, line in f.ChangedContents():
[email protected]8ea5d4b2011-09-13 21:49:22139 if 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27140 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]8ea5d4b2011-09-13 21:49:22141
[email protected]55463aa62011-10-12 00:48:27142 if not problems:
143 return []
144 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
145 ' If you are calling an API that accepts a wstring, fix the API.\n' +
146 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22147
148
[email protected]2a8ac9c2011-10-19 17:20:44149def _CheckNoDEPSGIT(input_api, output_api):
150 """Make sure .DEPS.git is never modified manually."""
151 if any(f.LocalPath().endswith('.DEPS.git') for f in
152 input_api.AffectedFiles()):
153 return [output_api.PresubmitError(
154 'Never commit changes to .DEPS.git. This file is maintained by an\n'
155 'automated system based on what\'s in DEPS and your changes will be\n'
156 'overwritten.\n'
157 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
158 'for more information')]
159 return []
160
161
[email protected]b5c24292011-11-28 14:38:20162def _CheckNoFRIEND_TEST(input_api, output_api):
163 """Make sure that gtest's FRIEND_TEST() macro is not used, the
164 FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be used
165 instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
166 problems = []
167
168 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
169 for f in input_api.AffectedFiles(file_filter=file_filter):
170 for line_num, line in f.ChangedContents():
171 if 'FRIEND_TEST(' in line:
172 problems.append(' %s:%d' % (f.LocalPath(), line_num))
173
174 if not problems:
175 return []
176 return [output_api.PresubmitPromptWarning('Chromium code should not use '
[email protected]24a4ac62011-11-29 15:30:33177 'gtest\'s FRIEND_TEST() macro. Include base/gtest_prod_util.h and use '
[email protected]b5c24292011-11-28 14:38:20178 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
179
180
[email protected]ecdf8ea52011-11-28 18:40:07181def _CheckNoNewOldCallback(input_api, output_api):
182 """Checks to make sure we don't introduce new uses of old callbacks."""
183
184 def HasOldCallbackKeywords(line):
185 """Returns True if a line of text contains keywords that indicate the use
186 of the old callback system.
187 """
188 return ('NewRunnableMethod' in line or
189 'NewRunnableFunction' in line or
190 'NewCallback' in line or
191 input_api.re.search(r'\bCallback\d<', line) or
192 input_api.re.search(r'\bpublic Task\b', line) or
193 'public CancelableTask' in line)
194
195 problems = []
196 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
197 for f in input_api.AffectedFiles(file_filter=file_filter):
198 if not any(HasOldCallbackKeywords(line) for line in f.NewContents()):
199 continue
200 for line_num, line in f.ChangedContents():
201 if HasOldCallbackKeywords(line):
202 problems.append(' %s:%d' % (f.LocalPath(), line_num))
203
204 if not problems:
205 return []
206 return [output_api.PresubmitPromptWarning('The old callback system is '
207 'deprecated. If possible, use base::Bind and base::Callback instead.\n' +
208 '\n'.join(problems))]
209
210
[email protected]22c9bd72011-03-27 16:47:39211def _CommonChecks(input_api, output_api):
212 """Checks common to both upload and commit."""
213 results = []
214 results.extend(input_api.canned_checks.PanProjectChecks(
215 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
216 results.extend(_CheckNoInterfacesInBase(input_api, output_api))
[email protected]66daa702011-05-28 14:41:46217 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19218 results.extend(
219 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54220 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22221 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44222 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]b5c24292011-11-28 14:38:20223 results.extend(_CheckNoFRIEND_TEST(input_api, output_api))
[email protected]ecdf8ea52011-11-28 18:40:07224 results.extend(_CheckNoNewOldCallback(input_api, output_api))
[email protected]22c9bd72011-03-27 16:47:39225 return results
[email protected]1f7b4172010-01-28 01:17:34226
[email protected]b337cb5b2011-01-23 21:24:05227
228def _CheckSubversionConfig(input_api, output_api):
229 """Verifies the subversion config file is correctly setup.
230
231 Checks that autoprops are enabled, returns an error otherwise.
232 """
233 join = input_api.os_path.join
234 if input_api.platform == 'win32':
235 appdata = input_api.environ.get('APPDATA', '')
236 if not appdata:
237 return [output_api.PresubmitError('%APPDATA% is not configured.')]
238 path = join(appdata, 'Subversion', 'config')
239 else:
240 home = input_api.environ.get('HOME', '')
241 if not home:
242 return [output_api.PresubmitError('$HOME is not configured.')]
243 path = join(home, '.subversion', 'config')
244
245 error_msg = (
246 'Please look at http://dev.chromium.org/developers/coding-style to\n'
247 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20248 'properties to simplify the project maintenance.\n'
249 'Pro-tip: just download and install\n'
250 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05251
252 try:
253 lines = open(path, 'r').read().splitlines()
254 # Make sure auto-props is enabled and check for 2 Chromium standard
255 # auto-prop.
256 if (not '*.cc = svn:eol-style=LF' in lines or
257 not '*.pdf = svn:mime-type=application/pdf' in lines or
258 not 'enable-auto-props = yes' in lines):
259 return [
[email protected]79ed7e62011-02-21 21:08:53260 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05261 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56262 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05263 ]
264 except (OSError, IOError):
265 return [
[email protected]79ed7e62011-02-21 21:08:53266 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05267 'Can\'t find your subversion config file.\n' + error_msg)
268 ]
269 return []
270
271
[email protected]66daa702011-05-28 14:41:46272def _CheckAuthorizedAuthor(input_api, output_api):
273 """For non-googler/chromites committers, verify the author's email address is
274 in AUTHORS.
275 """
[email protected]9bb9cb82011-06-13 20:43:01276 # TODO(maruel): Add it to input_api?
277 import fnmatch
278
[email protected]66daa702011-05-28 14:41:46279 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01280 if not author:
281 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46282 return []
[email protected]c99663292011-05-31 19:46:08283 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46284 input_api.PresubmitLocalPath(), 'AUTHORS')
285 valid_authors = (
286 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
287 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18288 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01289 if input_api.verbose:
290 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44291 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46292 return [output_api.PresubmitPromptWarning(
293 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
294 '\n'
295 'http://www.chromium.org/developers/contributing-code and read the '
296 '"Legal" section\n'
297 'If you are a chromite, verify the contributor signed the CLA.') %
298 author)]
299 return []
300
301
[email protected]1f7b4172010-01-28 01:17:34302def CheckChangeOnUpload(input_api, output_api):
303 results = []
304 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54305 return results
[email protected]ca8d1982009-02-19 16:33:12306
307
308def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54309 results = []
[email protected]1f7b4172010-01-28 01:17:34310 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51311 # TODO(thestig) temporarily disabled, doesn't work in third_party/
312 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
313 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54314 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27315 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34316 input_api,
317 output_api,
[email protected]4efa42142010-08-26 01:29:26318 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27319 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
320 output_api, 'http://codereview.chromium.org', ('win', 'linux', 'mac'),
321 '[email protected]'))
322
[email protected]3e4eb112011-01-18 03:29:54323 results.extend(input_api.canned_checks.CheckChangeHasBugField(
324 input_api, output_api))
325 results.extend(input_api.canned_checks.CheckChangeHasTestField(
326 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05327 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54328 return results
[email protected]ca8d1982009-02-19 16:33:12329
330
[email protected]5efb2a822011-09-27 23:06:13331def GetPreferredTrySlaves(project, change):
332 only_objc_files = all(
333 f.LocalPath().endswith(('.mm', '.m')) for f in change.AffectedFiles())
334 if only_objc_files:
335 return ['mac']
[email protected]5fa06292009-09-29 01:55:00336 return ['win', 'linux', 'mac']