1 # Copyright (c) 2012 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.
17 r
"^native_client_sdk[\\\/].*",
18 r
"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
25 _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 joi@chromium.org if you have questions.')
34 def _CheckNoInterfacesInBase(input_api
, output_api
):
35 """Checks to make sure no files in libbase.a have |@interface|."""
36 pattern
= input_api
.re
.compile(r
'^\s*@interface', input_api
.re
.MULTILINE
)
38 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
39 if (f
.LocalPath().startswith('base/') and
40 not f
.LocalPath().endswith('_unittest.mm')):
41 contents
= input_api
.ReadFile(f
)
42 if pattern
.search(contents
):
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',
54 def _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.
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
65 file_exclusion_patterns
= (
66 r
'.*[/\\](test_|mock_).+%s' % source_extensions
,
67 r
'.+_test_(base|support|util)%s' % source_extensions
,
68 r
'.+_(api|browser|perf|unit|ui)?test%s' % source_extensions
,
69 r
'.+profile_sync_service_harness%s' % source_extensions
,
71 path_exclusion_patterns
= (
72 r
'.*[/\\](test|tool(s)?)[/\\].*',
73 # At request of folks maintaining this folder.
74 r
'chrome[/\\]browser[/\\]automation[/\\].*',
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
))
83 def FilterFile(affected_file
):
84 black_list
= (file_exclusion_patterns
+ path_exclusion_patterns
+
85 _EXCLUDED_PATHS
+ input_api
.DEFAULT_BLACK_LIST
)
86 return input_api
.FilterSourceFile(
88 white_list
=(file_inclusion_pattern
, ),
89 black_list
=black_list
)
92 for f
in input_api
.AffectedSourceFiles(FilterFile
):
93 local_path
= f
.LocalPath()
94 lines
= input_api
.ReadFile(f
).splitlines()
97 if (inclusion_pattern
.search(line
) and
98 not exclusion_pattern
.search(line
)):
100 '%s:%d\n %s' % (local_path
, line_number
, line
.strip()))
104 if not input_api
.is_committing
:
105 return [output_api
.PresubmitPromptWarning(_TEST_ONLY_WARNING
, problems
)]
107 # We don't warn on commit, to avoid stopping commits going through CQ.
108 return [output_api
.PresubmitNotifyResult(_TEST_ONLY_WARNING
, problems
)]
113 def _CheckNoIOStreamInHeaders(input_api
, output_api
):
114 """Checks to make sure no .h files include <iostream>."""
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'):
121 contents
= input_api
.ReadFile(f
)
122 if pattern
.search(contents
):
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',
134 def _CheckNoNewWStrings(input_api
, output_api
):
135 """Checks to make sure we don't introduce use of wstrings."""
137 for f
in input_api
.AffectedFiles():
138 if (not f
.LocalPath().endswith(('.cc', '.h')) or
139 f
.LocalPath().endswith('test.cc')):
142 for line_num
, line
in f
.ChangedContents():
143 if 'wstring' in line
:
144 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
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
))]
153 def _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'
161 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
162 'for more information')]
166 def _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."""
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
))
180 return [output_api
.PresubmitPromptWarning('Chromium code should not use '
181 'gtest\'s FRIEND_TEST() macro. Include base/gtest_prod_util.h and use '
182 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems
))]
185 def _CommonChecks(input_api
, output_api
):
186 """Checks common to both upload and commit."""
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
))
191 results
.extend(_CheckAuthorizedAuthor(input_api
, output_api
))
193 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
))
194 results
.extend(_CheckNoIOStreamInHeaders(input_api
, output_api
))
195 results
.extend(_CheckNoNewWStrings(input_api
, output_api
))
196 results
.extend(_CheckNoDEPSGIT(input_api
, output_api
))
197 results
.extend(_CheckNoFRIEND_TEST(input_api
, output_api
))
201 def _CheckSubversionConfig(input_api
, output_api
):
202 """Verifies the subversion config file is correctly setup.
204 Checks that autoprops are enabled, returns an error otherwise.
206 join
= input_api
.os_path
.join
207 if input_api
.platform
== 'win32':
208 appdata
= input_api
.environ
.get('APPDATA', '')
210 return [output_api
.PresubmitError('%APPDATA% is not configured.')]
211 path
= join(appdata
, 'Subversion', 'config')
213 home
= input_api
.environ
.get('HOME', '')
215 return [output_api
.PresubmitError('$HOME is not configured.')]
216 path
= join(home
, '.subversion', 'config')
219 'Please look at http://dev.chromium.org/developers/coding-style to\n'
220 'configure your subversion configuration file. This enables automatic\n'
221 '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')
226 lines
= open(path
, 'r').read().splitlines()
227 # Make sure auto-props is enabled and check for 2 Chromium standard
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
):
233 output_api
.PresubmitNotifyResult(
234 'It looks like you have not configured your subversion config '
235 'file or it is not up-to-date.\n' + error_msg
)
237 except (OSError, IOError):
239 output_api
.PresubmitNotifyResult(
240 'Can\'t find your subversion config file.\n' + error_msg
)
245 def _CheckAuthorizedAuthor(input_api
, output_api
):
246 """For non-googler/chromites committers, verify the author's email address is
249 # TODO(maruel): Add it to input_api?
252 author
= input_api
.change
.author_email
254 input_api
.logging
.info('No author, skipping AUTHOR check')
256 authors_path
= input_api
.os_path
.join(
257 input_api
.PresubmitLocalPath(), 'AUTHORS')
259 input_api
.re
.match(r
'[^#]+\s+\<(.+?)\>\s*$', line
)
260 for line
in open(authors_path
))
261 valid_authors
= [item
.group(1).lower() for item
in valid_authors
if item
]
262 if input_api
.verbose
:
263 print 'Valid authors are %s' % ', '.join(valid_authors
)
264 if not any(fnmatch
.fnmatch(author
.lower(), valid
) for valid
in valid_authors
):
265 return [output_api
.PresubmitPromptWarning(
266 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
268 'http://www.chromium.org/developers/contributing-code and read the '
270 'If you are a chromite, verify the contributor signed the CLA.') %
275 def CheckChangeOnUpload(input_api
, output_api
):
277 results
.extend(_CommonChecks(input_api
, output_api
))
281 def CheckChangeOnCommit(input_api
, output_api
):
283 results
.extend(_CommonChecks(input_api
, output_api
))
284 # TODO(thestig) temporarily disabled, doesn't work in third_party/
285 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
286 # input_api, output_api, sources))
287 # Make sure the tree is 'open'.
288 results
.extend(input_api
.canned_checks
.CheckTreeIsOpen(
291 json_url
='http://chromium-status.appspot.com/current?format=json'))
292 results
.extend(input_api
.canned_checks
.CheckRietveldTryJobExecution(input_api
,
293 output_api
, 'http://codereview.chromium.org',
294 ('win_rel', 'linux_rel', 'mac_rel'), 'tryserver@chromium.org'))
296 results
.extend(input_api
.canned_checks
.CheckChangeHasBugField(
297 input_api
, output_api
))
298 results
.extend(input_api
.canned_checks
.CheckChangeHasTestField(
299 input_api
, output_api
))
300 results
.extend(input_api
.canned_checks
.CheckChangeHasDescription(
301 input_api
, output_api
))
302 results
.extend(_CheckSubversionConfig(input_api
, output_api
))
306 def GetPreferredTrySlaves(project
, change
):
307 affected_files
= change
.LocalPaths()
308 only_objc_files
= all(f
.endswith(('.mm', '.m')) for f
in affected_files
)
311 preferred
= ['win_rel', 'linux_rel', 'mac_rel']
312 if any(f
.endswith(('.h', '.cc', '.cpp', '.cxx')) for f
in affected_files
):
313 preferred
.append('linux_clang')
314 aura_re
= '_aura[^/]*[.][^/]*'
315 if any(re
.search(aura_re
, f
) for f
in affected_files
):
316 preferred
.append('linux_chromeos')
318 android_re_list
= ('^base/',
319 '^build/common.gypi$',
326 # Nothing that looks like win-only or aura-only
327 win_re
= '_win\.(cc|h)$'
328 possibly_android
= True
329 for non_android_re
in (aura_re
, win_re
):
330 if all(re
.search(non_android_re
, f
) for f
in affected_files
):
331 possibly_android
= False
334 for f
in change
.AffectedFiles():
335 if any(re
.search(r
, f
.LocalPath()) for r
in android_re_list
):
336 preferred
.append('android')