blob: acb81968594ce4fcfcce06e09dba8979df881471 [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]c762d252012-02-28 02:07:2467 r'.+_test_(base|support|util)%s' % source_extensions,
[email protected]19e77fd2011-10-20 05:24:0568 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]22c9bd72011-03-27 16:47:39185def _CommonChecks(input_api, output_api):
186 """Checks common to both upload and commit."""
187 results = []
188 results.extend(input_api.canned_checks.PanProjectChecks(
189 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
190 results.extend(_CheckNoInterfacesInBase(input_api, output_api))
[email protected]66daa702011-05-28 14:41:46191 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19192 results.extend(
193 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54194 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22195 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44196 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]b5c24292011-11-28 14:38:20197 results.extend(_CheckNoFRIEND_TEST(input_api, output_api))
[email protected]22c9bd72011-03-27 16:47:39198 return results
[email protected]1f7b4172010-01-28 01:17:34199
[email protected]b337cb5b2011-01-23 21:24:05200
201def _CheckSubversionConfig(input_api, output_api):
202 """Verifies the subversion config file is correctly setup.
203
204 Checks that autoprops are enabled, returns an error otherwise.
205 """
206 join = input_api.os_path.join
207 if input_api.platform == 'win32':
208 appdata = input_api.environ.get('APPDATA', '')
209 if not appdata:
210 return [output_api.PresubmitError('%APPDATA% is not configured.')]
211 path = join(appdata, 'Subversion', 'config')
212 else:
213 home = input_api.environ.get('HOME', '')
214 if not home:
215 return [output_api.PresubmitError('$HOME is not configured.')]
216 path = join(home, '.subversion', 'config')
217
218 error_msg = (
219 'Please look at http://dev.chromium.org/developers/coding-style to\n'
220 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20221 'properties to simplify the project maintenance.\n'
222 'Pro-tip: just download and install\n'
223 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05224
225 try:
226 lines = open(path, 'r').read().splitlines()
227 # Make sure auto-props is enabled and check for 2 Chromium standard
228 # auto-prop.
229 if (not '*.cc = svn:eol-style=LF' in lines or
230 not '*.pdf = svn:mime-type=application/pdf' in lines or
231 not 'enable-auto-props = yes' in lines):
232 return [
[email protected]79ed7e62011-02-21 21:08:53233 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05234 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56235 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05236 ]
237 except (OSError, IOError):
238 return [
[email protected]79ed7e62011-02-21 21:08:53239 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05240 'Can\'t find your subversion config file.\n' + error_msg)
241 ]
242 return []
243
244
[email protected]66daa702011-05-28 14:41:46245def _CheckAuthorizedAuthor(input_api, output_api):
246 """For non-googler/chromites committers, verify the author's email address is
247 in AUTHORS.
248 """
[email protected]9bb9cb82011-06-13 20:43:01249 # TODO(maruel): Add it to input_api?
250 import fnmatch
251
[email protected]66daa702011-05-28 14:41:46252 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01253 if not author:
254 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46255 return []
[email protected]c99663292011-05-31 19:46:08256 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46257 input_api.PresubmitLocalPath(), 'AUTHORS')
258 valid_authors = (
259 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
260 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18261 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01262 if input_api.verbose:
263 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44264 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46265 return [output_api.PresubmitPromptWarning(
266 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
267 '\n'
268 'http://www.chromium.org/developers/contributing-code and read the '
269 '"Legal" section\n'
270 'If you are a chromite, verify the contributor signed the CLA.') %
271 author)]
272 return []
273
274
[email protected]1f7b4172010-01-28 01:17:34275def CheckChangeOnUpload(input_api, output_api):
276 results = []
277 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54278 return results
[email protected]ca8d1982009-02-19 16:33:12279
280
281def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54282 results = []
[email protected]1f7b4172010-01-28 01:17:34283 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51284 # TODO(thestig) temporarily disabled, doesn't work in third_party/
285 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
286 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54287 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27288 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34289 input_api,
290 output_api,
[email protected]4efa42142010-08-26 01:29:26291 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27292 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
[email protected]4ddc5df2011-12-12 03:05:04293 output_api, 'http://codereview.chromium.org',
[email protected]c1ba4c52012-03-09 14:23:28294 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
295 '[email protected]'))
[email protected]806e98e2010-03-19 17:49:27296
[email protected]3e4eb112011-01-18 03:29:54297 results.extend(input_api.canned_checks.CheckChangeHasBugField(
298 input_api, output_api))
299 results.extend(input_api.canned_checks.CheckChangeHasTestField(
300 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:41301 results.extend(input_api.canned_checks.CheckChangeHasDescription(
302 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05303 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54304 return results
[email protected]ca8d1982009-02-19 16:33:12305
306
[email protected]5efb2a822011-09-27 23:06:13307def GetPreferredTrySlaves(project, change):
[email protected]50c30092012-02-15 04:21:36308 affected_files = change.LocalPaths()
309 only_objc_files = all(f.endswith(('.mm', '.m')) for f in affected_files)
[email protected]5efb2a822011-09-27 23:06:13310 if only_objc_files:
[email protected]4ddc5df2011-12-12 03:05:04311 return ['mac_rel']
[email protected]d3b7e7cca2012-03-01 21:25:06312 preferred = ['win_rel', 'linux_rel', 'mac_rel']
[email protected]50c30092012-02-15 04:21:36313 if any(f.endswith(('.h', '.cc', '.cpp', '.cxx')) for f in affected_files):
314 preferred.append('linux_clang')
[email protected]9d16ad12011-12-14 20:49:47315 aura_re = '_aura[^/]*[.][^/]*'
[email protected]50c30092012-02-15 04:21:36316 if any(re.search(aura_re, f) for f in affected_files):
[email protected]e9b23882012-02-03 01:05:49317 preferred.append('linux_chromeos')
[email protected]d3b7e7cca2012-03-01 21:25:06318 # Nothing in chrome/
319 android_re_list = ('^base/',
320 '^build/common.gypi$',
321 '^content/',
322 '^ipc/',
323 '^jingle/',
324 '^media/',
325 '^net/',
326 '^sql/')
327 # Nothing that looks like win-only or aura-only
328 win_re = '_win\.(cc|h)$'
329 possibly_android = True
330 for non_android_re in (aura_re, win_re):
331 if all(re.search(non_android_re, f) for f in affected_files):
332 possibly_android = False
333 break
334 if possibly_android:
335 for f in change.AffectedFiles():
336 if any(re.search(r, f.LocalPath()) for r in android_re_list):
337 preferred.append('android')
338 break
[email protected]9d16ad12011-12-14 20:49:47339 return preferred