WebKit roll 98705:98715
[chromium-blink-merge.git] / PRESUBMIT.py
blob27f03a9ffb0b5303734eedf3a6450d0d399e658e
1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Top-level presubmit script for Chromium.
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8 for more details about the presubmit API built into gcl.
9 """
11 _EXCLUDED_PATHS = (
12 r"^breakpad[\\\/].*",
13 r"^net/tools/spdyshark/[\\\/].*",
14 r"^skia[\\\/].*",
15 r"^v8[\\\/].*",
16 r".*MakeFile$",
20 def _CheckNoInterfacesInBase(input_api, output_api):
21 """Checks to make sure no files in libbase.a have |@interface|."""
22 pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
23 files = []
24 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
25 if (f.LocalPath().startswith('base/') and
26 not f.LocalPath().endswith('_unittest.mm')):
27 contents = input_api.ReadFile(f)
28 if pattern.search(contents):
29 files.append(f)
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 []
40 def _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
51 file_exclusion_patterns = (
52 r'.*/(test_|mock_).+%s' % source_extensions,
53 r'.+_test_(support|base)%s' % source_extensions,
54 r'.+_(api|browser|perf|unit|ui)?test%s' % source_extensions,
55 r'.+profile_sync_service_harness%s' % source_extensions,
57 path_exclusion_patterns = (
58 r'.*[/\\](test|tool(s)?)[/\\].*',
59 # At request of folks maintaining this folder.
60 r'chrome[/\\]browser[/\\]automation[/\\].*',
63 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
64 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
65 exclusion_pattern = input_api.re.compile(
66 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
67 base_function_pattern, base_function_pattern))
69 def FilterFile(affected_file):
70 black_list = (file_exclusion_patterns + path_exclusion_patterns +
71 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
72 return input_api.FilterSourceFile(
73 affected_file,
74 white_list=(file_inclusion_pattern, ),
75 black_list=black_list)
77 problems = []
78 for f in input_api.AffectedSourceFiles(FilterFile):
79 local_path = f.LocalPath()
80 lines = input_api.ReadFile(f).splitlines()
81 line_number = 0
82 for line in lines:
83 if (inclusion_pattern.search(line) and
84 not exclusion_pattern.search(line)):
85 problems.append(
86 '%s:%d\n %s' % (local_path, line_number, line.strip()))
87 line_number += 1
89 if problems:
90 return [output_api.PresubmitPromptWarning(
91 'You might be calling functions intended only for testing from\n'
92 'production code. Please verify that the following usages are OK,\n'
93 'and email joi@chromium.org if you are seeing false positives:',
94 problems)]
95 else:
96 return []
99 def _CheckNoIOStreamInHeaders(input_api, output_api):
100 """Checks to make sure no .h files include <iostream>."""
101 files = []
102 pattern = input_api.re.compile(r'^#include\s*<iostream>',
103 input_api.re.MULTILINE)
104 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
105 if not f.LocalPath().endswith('.h'):
106 continue
107 contents = input_api.ReadFile(f)
108 if pattern.search(contents):
109 files.append(f)
111 if len(files):
112 return [ output_api.PresubmitError(
113 'Do not #include <iostream> in header files, since it inserts static ' +
114 'initialization into every file including the header. Instead, ' +
115 '#include <ostream>. See http://crbug.com/94794',
116 files) ]
117 return []
120 def _CheckNoNewWStrings(input_api, output_api):
121 """Checks to make sure we don't introduce use of wstrings."""
122 problems = []
123 for f in input_api.AffectedFiles():
124 for line_num, line in f.ChangedContents():
125 if (not f.LocalPath().endswith(('.cc', '.h')) or
126 f.LocalPath().endswith('test.cc')):
127 continue
129 if 'wstring' in line:
130 problems.append(' %s:%d' % (f.LocalPath(), line_num))
132 if not problems:
133 return []
134 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
135 ' If you are calling an API that accepts a wstring, fix the API.\n' +
136 '\n'.join(problems))]
139 def _CheckNoDEPSGIT(input_api, output_api):
140 """Make sure .DEPS.git is never modified manually."""
141 if any(f.LocalPath().endswith('.DEPS.git') for f in
142 input_api.AffectedFiles()):
143 return [output_api.PresubmitError(
144 'Never commit changes to .DEPS.git. This file is maintained by an\n'
145 'automated system based on what\'s in DEPS and your changes will be\n'
146 'overwritten.\n'
147 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
148 'for more information')]
149 return []
152 def _CommonChecks(input_api, output_api):
153 """Checks common to both upload and commit."""
154 results = []
155 results.extend(input_api.canned_checks.PanProjectChecks(
156 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
157 results.extend(_CheckNoInterfacesInBase(input_api, output_api))
158 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
159 results.extend(
160 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
161 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
162 results.extend(_CheckNoNewWStrings(input_api, output_api))
163 results.extend(_CheckNoDEPSGIT(input_api, output_api))
164 return results
167 def _CheckSubversionConfig(input_api, output_api):
168 """Verifies the subversion config file is correctly setup.
170 Checks that autoprops are enabled, returns an error otherwise.
172 join = input_api.os_path.join
173 if input_api.platform == 'win32':
174 appdata = input_api.environ.get('APPDATA', '')
175 if not appdata:
176 return [output_api.PresubmitError('%APPDATA% is not configured.')]
177 path = join(appdata, 'Subversion', 'config')
178 else:
179 home = input_api.environ.get('HOME', '')
180 if not home:
181 return [output_api.PresubmitError('$HOME is not configured.')]
182 path = join(home, '.subversion', 'config')
184 error_msg = (
185 'Please look at http://dev.chromium.org/developers/coding-style to\n'
186 'configure your subversion configuration file. This enables automatic\n'
187 'properties to simplify the project maintenance.\n'
188 'Pro-tip: just download and install\n'
189 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
191 try:
192 lines = open(path, 'r').read().splitlines()
193 # Make sure auto-props is enabled and check for 2 Chromium standard
194 # auto-prop.
195 if (not '*.cc = svn:eol-style=LF' in lines or
196 not '*.pdf = svn:mime-type=application/pdf' in lines or
197 not 'enable-auto-props = yes' in lines):
198 return [
199 output_api.PresubmitNotifyResult(
200 'It looks like you have not configured your subversion config '
201 'file or it is not up-to-date.\n' + error_msg)
203 except (OSError, IOError):
204 return [
205 output_api.PresubmitNotifyResult(
206 'Can\'t find your subversion config file.\n' + error_msg)
208 return []
211 def _CheckAuthorizedAuthor(input_api, output_api):
212 """For non-googler/chromites committers, verify the author's email address is
213 in AUTHORS.
215 # TODO(maruel): Add it to input_api?
216 import fnmatch
218 author = input_api.change.author_email
219 if not author:
220 input_api.logging.info('No author, skipping AUTHOR check')
221 return []
222 authors_path = input_api.os_path.join(
223 input_api.PresubmitLocalPath(), 'AUTHORS')
224 valid_authors = (
225 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
226 for line in open(authors_path))
227 valid_authors = [item.group(1).lower() for item in valid_authors if item]
228 if input_api.verbose:
229 print 'Valid authors are %s' % ', '.join(valid_authors)
230 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
231 return [output_api.PresubmitPromptWarning(
232 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
233 '\n'
234 'http://www.chromium.org/developers/contributing-code and read the '
235 '"Legal" section\n'
236 'If you are a chromite, verify the contributor signed the CLA.') %
237 author)]
238 return []
241 def CheckChangeOnUpload(input_api, output_api):
242 results = []
243 results.extend(_CommonChecks(input_api, output_api))
244 return results
247 def CheckChangeOnCommit(input_api, output_api):
248 results = []
249 results.extend(_CommonChecks(input_api, output_api))
250 # TODO(thestig) temporarily disabled, doesn't work in third_party/
251 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
252 # input_api, output_api, sources))
253 # Make sure the tree is 'open'.
254 results.extend(input_api.canned_checks.CheckTreeIsOpen(
255 input_api,
256 output_api,
257 json_url='http://chromium-status.appspot.com/current?format=json'))
258 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
259 output_api, 'http://codereview.chromium.org', ('win', 'linux', 'mac'),
260 'tryserver@chromium.org'))
262 results.extend(input_api.canned_checks.CheckChangeHasBugField(
263 input_api, output_api))
264 results.extend(input_api.canned_checks.CheckChangeHasTestField(
265 input_api, output_api))
266 results.extend(_CheckSubversionConfig(input_api, output_api))
267 return results
270 def GetPreferredTrySlaves(project, change):
271 only_objc_files = all(
272 f.LocalPath().endswith(('.mm', '.m')) for f in change.AffectedFiles())
273 if only_objc_files:
274 return ['mac']
275 return ['win', 'linux', 'mac']