blob: fe7f79c6a9ae032533f6aff732d78df0f8511f82 [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]379e7dd2010-01-28 17:39:2111_EXCLUDED_PATHS = (
[email protected]3e4eb112011-01-18 03:29:5412 r"^breakpad[\\\/].*",
13 r"^net/tools/spdyshark/[\\\/].*",
14 r"^skia[\\\/].*",
15 r"^v8[\\\/].*",
16 r".*MakeFile$",
[email protected]4306417642009-06-11 00:33:4017)
[email protected]ca8d1982009-02-19 16:33:1218
[email protected]ca8d1982009-02-19 16:33:1219
[email protected]22c9bd72011-03-27 16:47:3920def _CheckNoInterfacesInBase(input_api, output_api):
[email protected]6a4c8e682010-12-19 03:31:3421 """Checks to make sure no files in libbase.a have |@interface|."""
[email protected]839c1392011-04-29 20:15:1922 pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
[email protected]6a4c8e682010-12-19 03:31:3423 files = []
[email protected]22c9bd72011-03-27 16:47:3924 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
[email protected]a766a1322011-09-08 20:46:0525 if (f.LocalPath().startswith('base/') and
[email protected]0b2f07b02011-05-02 17:29:0026 not f.LocalPath().endswith('_unittest.mm')):
[email protected]6a4c8e682010-12-19 03:31:3427 contents = input_api.ReadFile(f)
28 if pattern.search(contents):
29 files.append(f)
30
31 if len(files):
32 return [ output_api.PresubmitError(
33 'Objective-C interfaces or categories are forbidden in libbase. ' +
34 'See http://groups.google.com/a/chromium.org/group/chromium-dev/' +
35 'browse_thread/thread/efb28c10435987fd',
36 files) ]
37 return []
38
[email protected]650c9082010-12-14 14:33:4439
[email protected]55459852011-08-10 15:17:1940def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
41 """Attempts to prevent use of functions intended only for testing in
42 non-testing code. For now this is just a best-effort implementation
43 that ignores header files and may have some false positives. A
44 better implementation would probably need a proper C++ parser.
45 """
46 # We only scan .cc files and the like, as the declaration of
47 # for-testing functions in header files are hard to distinguish from
48 # calls to such functions without a proper C++ parser.
49 source_extensions = r'\.(cc|cpp|cxx|mm)$'
50 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]1546aed92011-09-12 23:12:2551 file_exclusion_pattern = (
[email protected]2aefa8622011-10-18 02:39:1152 r'(.*/(test_|mock_).+|.+(_test_support|profile_sync_service_harness|'
[email protected]58971b312011-10-04 22:45:0853 r'_(api|browser|perf|unit|ui)?test))%s' % source_extensions)
[email protected]3afb12a42011-08-15 13:48:3354 path_exclusion_pattern = r'.*[/\\](test|tool(s)?)[/\\].*'
[email protected]55459852011-08-10 15:17:1955
56 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
57 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
58 exclusion_pattern = input_api.re.compile(
59 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
60 base_function_pattern, base_function_pattern))
61
62 def FilterFile(affected_file):
[email protected]3afb12a42011-08-15 13:48:3363 black_list = ((file_exclusion_pattern, path_exclusion_pattern, ) +
64 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:1965 return input_api.FilterSourceFile(
66 affected_file,
67 white_list=(file_inclusion_pattern, ),
68 black_list=black_list)
69
70 problems = []
71 for f in input_api.AffectedSourceFiles(FilterFile):
72 local_path = f.LocalPath()
73 lines = input_api.ReadFile(f).splitlines()
74 line_number = 0
75 for line in lines:
76 if (inclusion_pattern.search(line) and
77 not exclusion_pattern.search(line)):
78 problems.append(
79 '%s:%d\n %s' % (local_path, line_number, line.strip()))
80 line_number += 1
81
82 if problems:
83 return [output_api.PresubmitPromptWarning(
84 'You might be calling functions intended only for testing from\n'
85 'production code. Please verify that the following usages are OK,\n'
86 'and email [email protected] if you are seeing false positives:',
87 problems)]
88 else:
89 return []
90
91
[email protected]10689ca2011-09-02 02:31:5492def _CheckNoIOStreamInHeaders(input_api, output_api):
93 """Checks to make sure no .h files include <iostream>."""
94 files = []
95 pattern = input_api.re.compile(r'^#include\s*<iostream>',
96 input_api.re.MULTILINE)
97 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
98 if not f.LocalPath().endswith('.h'):
99 continue
100 contents = input_api.ReadFile(f)
101 if pattern.search(contents):
102 files.append(f)
103
104 if len(files):
105 return [ output_api.PresubmitError(
106 'Do not #include <iostream> in header files, since it inserts static ' +
107 'initialization into every file including the header. Instead, ' +
108 '#include <ostream>. See http://crbug.com/94794',
109 files) ]
110 return []
111
112
[email protected]8ea5d4b2011-09-13 21:49:22113def _CheckNoNewWStrings(input_api, output_api):
114 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27115 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22116 for f in input_api.AffectedFiles():
117 for line_num, line in f.ChangedContents():
[email protected]044ef4d2011-09-23 03:37:52118 if (not f.LocalPath().endswith(('.cc', '.h')) or
119 f.LocalPath().endswith('test.cc')):
[email protected]8ea5d4b2011-09-13 21:49:22120 continue
121
122 if 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27123 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]8ea5d4b2011-09-13 21:49:22124
[email protected]55463aa62011-10-12 00:48:27125 if not problems:
126 return []
127 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
128 ' If you are calling an API that accepts a wstring, fix the API.\n' +
129 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22130
131
[email protected]2a8ac9c2011-10-19 17:20:44132def _CheckNoDEPSGIT(input_api, output_api):
133 """Make sure .DEPS.git is never modified manually."""
134 if any(f.LocalPath().endswith('.DEPS.git') for f in
135 input_api.AffectedFiles()):
136 return [output_api.PresubmitError(
137 'Never commit changes to .DEPS.git. This file is maintained by an\n'
138 'automated system based on what\'s in DEPS and your changes will be\n'
139 'overwritten.\n'
140 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
141 'for more information')]
142 return []
143
144
[email protected]22c9bd72011-03-27 16:47:39145def _CommonChecks(input_api, output_api):
146 """Checks common to both upload and commit."""
147 results = []
148 results.extend(input_api.canned_checks.PanProjectChecks(
149 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
150 results.extend(_CheckNoInterfacesInBase(input_api, output_api))
[email protected]66daa702011-05-28 14:41:46151 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19152 results.extend(
153 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54154 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22155 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44156 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]22c9bd72011-03-27 16:47:39157 return results
[email protected]1f7b4172010-01-28 01:17:34158
[email protected]b337cb5b2011-01-23 21:24:05159
160def _CheckSubversionConfig(input_api, output_api):
161 """Verifies the subversion config file is correctly setup.
162
163 Checks that autoprops are enabled, returns an error otherwise.
164 """
165 join = input_api.os_path.join
166 if input_api.platform == 'win32':
167 appdata = input_api.environ.get('APPDATA', '')
168 if not appdata:
169 return [output_api.PresubmitError('%APPDATA% is not configured.')]
170 path = join(appdata, 'Subversion', 'config')
171 else:
172 home = input_api.environ.get('HOME', '')
173 if not home:
174 return [output_api.PresubmitError('$HOME is not configured.')]
175 path = join(home, '.subversion', 'config')
176
177 error_msg = (
178 'Please look at http://dev.chromium.org/developers/coding-style to\n'
179 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20180 'properties to simplify the project maintenance.\n'
181 'Pro-tip: just download and install\n'
182 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05183
184 try:
185 lines = open(path, 'r').read().splitlines()
186 # Make sure auto-props is enabled and check for 2 Chromium standard
187 # auto-prop.
188 if (not '*.cc = svn:eol-style=LF' in lines or
189 not '*.pdf = svn:mime-type=application/pdf' in lines or
190 not 'enable-auto-props = yes' in lines):
191 return [
[email protected]79ed7e62011-02-21 21:08:53192 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05193 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56194 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05195 ]
196 except (OSError, IOError):
197 return [
[email protected]79ed7e62011-02-21 21:08:53198 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05199 'Can\'t find your subversion config file.\n' + error_msg)
200 ]
201 return []
202
203
[email protected]66daa702011-05-28 14:41:46204def _CheckAuthorizedAuthor(input_api, output_api):
205 """For non-googler/chromites committers, verify the author's email address is
206 in AUTHORS.
207 """
[email protected]9bb9cb82011-06-13 20:43:01208 # TODO(maruel): Add it to input_api?
209 import fnmatch
210
[email protected]66daa702011-05-28 14:41:46211 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01212 if not author:
213 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46214 return []
[email protected]c99663292011-05-31 19:46:08215 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46216 input_api.PresubmitLocalPath(), 'AUTHORS')
217 valid_authors = (
218 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
219 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18220 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01221 if input_api.verbose:
222 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44223 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46224 return [output_api.PresubmitPromptWarning(
225 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
226 '\n'
227 'http://www.chromium.org/developers/contributing-code and read the '
228 '"Legal" section\n'
229 'If you are a chromite, verify the contributor signed the CLA.') %
230 author)]
231 return []
232
233
[email protected]1f7b4172010-01-28 01:17:34234def CheckChangeOnUpload(input_api, output_api):
235 results = []
236 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54237 return results
[email protected]ca8d1982009-02-19 16:33:12238
239
240def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54241 results = []
[email protected]1f7b4172010-01-28 01:17:34242 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51243 # TODO(thestig) temporarily disabled, doesn't work in third_party/
244 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
245 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54246 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27247 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34248 input_api,
249 output_api,
[email protected]4efa42142010-08-26 01:29:26250 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27251 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
252 output_api, 'http://codereview.chromium.org', ('win', 'linux', 'mac'),
253 '[email protected]'))
254
[email protected]3e4eb112011-01-18 03:29:54255 results.extend(input_api.canned_checks.CheckChangeHasBugField(
256 input_api, output_api))
257 results.extend(input_api.canned_checks.CheckChangeHasTestField(
258 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05259 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54260 return results
[email protected]ca8d1982009-02-19 16:33:12261
262
[email protected]5efb2a822011-09-27 23:06:13263def GetPreferredTrySlaves(project, change):
264 only_objc_files = all(
265 f.LocalPath().endswith(('.mm', '.m')) for f in change.AffectedFiles())
266 if only_objc_files:
267 return ['mac']
[email protected]5fa06292009-09-29 01:55:00268 return ['win', 'linux', 'mac']