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.
18 r
"^native_client_sdk[\\\/].*",
19 r
"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
27 _TEST_ONLY_WARNING
= (
28 'You might be calling functions intended only for testing from\n'
29 'production code. It is OK to ignore this warning if you know what\n'
30 'you are doing, as the heuristics used to detect the situation are\n'
31 'not perfect. The commit queue will not block on this warning.\n'
32 'Email joi@chromium.org if you have questions.')
35 _BANNED_OBJC_FUNCTIONS
= (
39 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
40 'prohibited. Please use CrTrackingArea instead.',
41 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
48 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
50 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
55 'convertPointFromBase:',
57 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
58 'Please use |convertPoint:(point) fromView:nil| instead.',
59 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
64 'convertPointToBase:',
66 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
67 'Please use |convertPoint:(point) toView:nil| instead.',
68 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
73 'convertRectFromBase:',
75 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
76 'Please use |convertRect:(point) fromView:nil| instead.',
77 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
84 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
85 'Please use |convertRect:(point) toView:nil| instead.',
86 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
91 'convertSizeFromBase:',
93 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
94 'Please use |convertSize:(point) fromView:nil| instead.',
95 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
100 'convertSizeToBase:',
102 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
103 'Please use |convertSize:(point) toView:nil| instead.',
104 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
111 _BANNED_CPP_FUNCTIONS
= (
112 # Make sure that gtest's FRIEND_TEST() macro is not used; the
113 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
114 # used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes.
118 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
119 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
126 'New code should not use ScopedAllowIO. Post a task to the blocking',
127 'pool or the FILE thread instead.',
132 'FilePathWatcher::Delegate',
134 'New code should not use FilePathWatcher::Delegate. Use the callback',
135 'interface instead.',
140 'browser::FindLastActiveWithProfile',
142 'This function is deprecated and we\'re working on removing it. Pass',
143 'more context to get a Browser*, like a WebContents, window, or session',
144 'id. Talk to ben@ or jam@ for more information.',
149 'browser::FindBrowserWithProfile',
151 'This function is deprecated and we\'re working on removing it. Pass',
152 'more context to get a Browser*, like a WebContents, window, or session',
153 'id. Talk to ben@ or jam@ for more information.',
158 'browser::FindAnyBrowser',
160 'This function is deprecated and we\'re working on removing it. Pass',
161 'more context to get a Browser*, like a WebContents, window, or session',
162 'id. Talk to ben@ or jam@ for more information.',
167 'browser::FindOrCreateTabbedBrowser',
169 'This function is deprecated and we\'re working on removing it. Pass',
170 'more context to get a Browser*, like a WebContents, window, or session',
171 'id. Talk to ben@ or jam@ for more information.',
176 'browser::FindTabbedBrowser',
178 'This function is deprecated and we\'re working on removing it. Pass',
179 'more context to get a Browser*, like a WebContents, window, or session',
180 'id. Talk to ben@ or jam@ for more information.',
188 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
):
189 """Attempts to prevent use of functions intended only for testing in
190 non-testing code. For now this is just a best-effort implementation
191 that ignores header files and may have some false positives. A
192 better implementation would probably need a proper C++ parser.
194 # We only scan .cc files and the like, as the declaration of
195 # for-testing functions in header files are hard to distinguish from
196 # calls to such functions without a proper C++ parser.
197 platform_specifiers
= r
'(_(android|chromeos|gtk|mac|posix|win))?'
198 source_extensions
= r
'\.(cc|cpp|cxx|mm)$'
199 file_inclusion_pattern
= r
'.+%s' % source_extensions
200 file_exclusion_patterns
= (
201 r
'.*[/\\](test_|mock_).+%s' % source_extensions
,
202 r
'.+_test_(base|support|util)%s' % source_extensions
,
203 r
'.+_(api|browser|perf|unit|ui)?test%s%s' % (platform_specifiers
,
205 r
'.+profile_sync_service_harness%s' % source_extensions
,
207 path_exclusion_patterns
= (
208 r
'.*[/\\](test|tool(s)?)[/\\].*',
209 # At request of folks maintaining this folder.
210 r
'chrome[/\\]browser[/\\]automation[/\\].*',
213 base_function_pattern
= r
'ForTest(ing)?|for_test(ing)?'
214 inclusion_pattern
= input_api
.re
.compile(r
'(%s)\s*\(' % base_function_pattern
)
215 exclusion_pattern
= input_api
.re
.compile(
216 r
'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
217 base_function_pattern
, base_function_pattern
))
219 def FilterFile(affected_file
):
220 black_list
= (file_exclusion_patterns
+ path_exclusion_patterns
+
221 _EXCLUDED_PATHS
+ input_api
.DEFAULT_BLACK_LIST
)
222 return input_api
.FilterSourceFile(
224 white_list
=(file_inclusion_pattern
, ),
225 black_list
=black_list
)
228 for f
in input_api
.AffectedSourceFiles(FilterFile
):
229 local_path
= f
.LocalPath()
230 lines
= input_api
.ReadFile(f
).splitlines()
233 if (inclusion_pattern
.search(line
) and
234 not exclusion_pattern
.search(line
)):
236 '%s:%d\n %s' % (local_path
, line_number
, line
.strip()))
240 if not input_api
.is_committing
:
241 return [output_api
.PresubmitPromptWarning(_TEST_ONLY_WARNING
, problems
)]
243 # We don't warn on commit, to avoid stopping commits going through CQ.
244 return [output_api
.PresubmitNotifyResult(_TEST_ONLY_WARNING
, problems
)]
249 def _CheckNoIOStreamInHeaders(input_api
, output_api
):
250 """Checks to make sure no .h files include <iostream>."""
252 pattern
= input_api
.re
.compile(r
'^#include\s*<iostream>',
253 input_api
.re
.MULTILINE
)
254 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
255 if not f
.LocalPath().endswith('.h'):
257 contents
= input_api
.ReadFile(f
)
258 if pattern
.search(contents
):
262 return [ output_api
.PresubmitError(
263 'Do not #include <iostream> in header files, since it inserts static '
264 'initialization into every file including the header. Instead, '
265 '#include <ostream>. See http://crbug.com/94794',
270 def _CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
):
271 """Checks to make sure no source files use UNIT_TEST"""
273 for f
in input_api
.AffectedFiles():
274 if (not f
.LocalPath().endswith(('.cc', '.mm'))):
277 for line_num
, line
in f
.ChangedContents():
278 if 'UNIT_TEST' in line
:
279 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
283 return [output_api
.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
284 '\n'.join(problems
))]
287 def _CheckNoNewWStrings(input_api
, output_api
):
288 """Checks to make sure we don't introduce use of wstrings."""
290 for f
in input_api
.AffectedFiles():
291 if (not f
.LocalPath().endswith(('.cc', '.h')) or
292 f
.LocalPath().endswith('test.cc')):
296 for line_num
, line
in f
.ChangedContents():
297 if 'presubmit: allow wstring' in line
:
299 elif not allowWString
and 'wstring' in line
:
300 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
307 return [output_api
.PresubmitPromptWarning('New code should not use wstrings.'
308 ' If you are calling a cross-platform API that accepts a wstring, '
310 '\n'.join(problems
))]
313 def _CheckNoDEPSGIT(input_api
, output_api
):
314 """Make sure .DEPS.git is never modified manually."""
315 if any(f
.LocalPath().endswith('.DEPS.git') for f
in
316 input_api
.AffectedFiles()):
317 return [output_api
.PresubmitError(
318 'Never commit changes to .DEPS.git. This file is maintained by an\n'
319 'automated system based on what\'s in DEPS and your changes will be\n'
321 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
322 'for more information')]
326 def _CheckNoBannedFunctions(input_api
, output_api
):
327 """Make sure that banned functions are not used."""
331 file_filter
= lambda f
: f
.LocalPath().endswith(('.mm', '.m', '.h'))
332 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
333 for line_num
, line
in f
.ChangedContents():
334 for func_name
, message
, error
in _BANNED_OBJC_FUNCTIONS
:
335 if func_name
in line
:
339 problems
.append(' %s:%d:' % (f
.LocalPath(), line_num
))
340 for message_line
in message
:
341 problems
.append(' %s' % message_line
)
343 file_filter
= lambda f
: f
.LocalPath().endswith(('.cc', '.mm', '.h'))
344 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
345 for line_num
, line
in f
.ChangedContents():
346 for func_name
, message
, error
in _BANNED_CPP_FUNCTIONS
:
347 if func_name
in line
:
351 problems
.append(' %s:%d:' % (f
.LocalPath(), line_num
))
352 for message_line
in message
:
353 problems
.append(' %s' % message_line
)
357 result
.append(output_api
.PresubmitPromptWarning(
358 'Banned functions were used.\n' + '\n'.join(warnings
)))
360 result
.append(output_api
.PresubmitError(
361 'Banned functions were used.\n' + '\n'.join(errors
)))
365 def _CheckNoPragmaOnce(input_api
, output_api
):
366 """Make sure that banned functions are not used."""
368 pattern
= input_api
.re
.compile(r
'^#pragma\s+once',
369 input_api
.re
.MULTILINE
)
370 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
371 if not f
.LocalPath().endswith('.h'):
373 contents
= input_api
.ReadFile(f
)
374 if pattern
.search(contents
):
378 return [output_api
.PresubmitError(
379 'Do not use #pragma once in header files.\n'
380 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
385 def _CheckUnwantedDependencies(input_api
, output_api
):
386 """Runs checkdeps on #include statements added in this
387 change. Breaking - rules is an error, breaking ! rules is a
390 # We need to wait until we have an input_api object and use this
391 # roundabout construct to import checkdeps because this file is
392 # eval-ed and thus doesn't have __file__.
393 original_sys_path
= sys
.path
395 sys
.path
= sys
.path
+ [input_api
.os_path
.join(
396 input_api
.PresubmitLocalPath(), 'tools', 'checkdeps')]
398 from cpp_checker
import CppChecker
399 from rules
import Rule
401 # Restore sys.path to what it was before.
402 sys
.path
= original_sys_path
405 for f
in input_api
.AffectedFiles():
406 if not CppChecker
.IsCppFile(f
.LocalPath()):
409 changed_lines
= [line
for line_num
, line
in f
.ChangedContents()]
410 added_includes
.append([f
.LocalPath(), changed_lines
])
412 deps_checker
= checkdeps
.DepsChecker()
414 error_descriptions
= []
415 warning_descriptions
= []
416 for path
, rule_type
, rule_description
in deps_checker
.CheckAddedCppIncludes(
418 description_with_path
= '%s\n %s' % (path
, rule_description
)
419 if rule_type
== Rule
.DISALLOW
:
420 error_descriptions
.append(description_with_path
)
422 warning_descriptions
.append(description_with_path
)
425 if error_descriptions
:
426 results
.append(output_api
.PresubmitError(
427 'You added one or more #includes that violate checkdeps rules.',
429 if warning_descriptions
:
430 results
.append(output_api
.PresubmitPromptWarning(
431 'You added one or more #includes of files that are temporarily\n'
432 'allowed but being removed. Can you avoid introducing the\n'
433 '#include? See relevant DEPS file(s) for details and contacts.',
434 warning_descriptions
))
438 def _CommonChecks(input_api
, output_api
):
439 """Checks common to both upload and commit."""
441 results
.extend(input_api
.canned_checks
.PanProjectChecks(
442 input_api
, output_api
, excluded_paths
=_EXCLUDED_PATHS
))
443 results
.extend(_CheckAuthorizedAuthor(input_api
, output_api
))
445 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
))
446 results
.extend(_CheckNoIOStreamInHeaders(input_api
, output_api
))
447 results
.extend(_CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
))
448 results
.extend(_CheckNoNewWStrings(input_api
, output_api
))
449 results
.extend(_CheckNoDEPSGIT(input_api
, output_api
))
450 results
.extend(_CheckNoBannedFunctions(input_api
, output_api
))
451 results
.extend(_CheckNoPragmaOnce(input_api
, output_api
))
452 results
.extend(_CheckUnwantedDependencies(input_api
, output_api
))
456 def _CheckSubversionConfig(input_api
, output_api
):
457 """Verifies the subversion config file is correctly setup.
459 Checks that autoprops are enabled, returns an error otherwise.
461 join
= input_api
.os_path
.join
462 if input_api
.platform
== 'win32':
463 appdata
= input_api
.environ
.get('APPDATA', '')
465 return [output_api
.PresubmitError('%APPDATA% is not configured.')]
466 path
= join(appdata
, 'Subversion', 'config')
468 home
= input_api
.environ
.get('HOME', '')
470 return [output_api
.PresubmitError('$HOME is not configured.')]
471 path
= join(home
, '.subversion', 'config')
474 'Please look at http://dev.chromium.org/developers/coding-style to\n'
475 'configure your subversion configuration file. This enables automatic\n'
476 'properties to simplify the project maintenance.\n'
477 'Pro-tip: just download and install\n'
478 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
481 lines
= open(path
, 'r').read().splitlines()
482 # Make sure auto-props is enabled and check for 2 Chromium standard
484 if (not '*.cc = svn:eol-style=LF' in lines
or
485 not '*.pdf = svn:mime-type=application/pdf' in lines
or
486 not 'enable-auto-props = yes' in lines
):
488 output_api
.PresubmitNotifyResult(
489 'It looks like you have not configured your subversion config '
490 'file or it is not up-to-date.\n' + error_msg
)
492 except (OSError, IOError):
494 output_api
.PresubmitNotifyResult(
495 'Can\'t find your subversion config file.\n' + error_msg
)
500 def _CheckAuthorizedAuthor(input_api
, output_api
):
501 """For non-googler/chromites committers, verify the author's email address is
504 # TODO(maruel): Add it to input_api?
507 author
= input_api
.change
.author_email
509 input_api
.logging
.info('No author, skipping AUTHOR check')
511 authors_path
= input_api
.os_path
.join(
512 input_api
.PresubmitLocalPath(), 'AUTHORS')
514 input_api
.re
.match(r
'[^#]+\s+\<(.+?)\>\s*$', line
)
515 for line
in open(authors_path
))
516 valid_authors
= [item
.group(1).lower() for item
in valid_authors
if item
]
517 if input_api
.verbose
:
518 print 'Valid authors are %s' % ', '.join(valid_authors
)
519 if not any(fnmatch
.fnmatch(author
.lower(), valid
) for valid
in valid_authors
):
520 return [output_api
.PresubmitPromptWarning(
521 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
523 'http://www.chromium.org/developers/contributing-code and read the '
525 'If you are a chromite, verify the contributor signed the CLA.') %
530 def CheckChangeOnUpload(input_api
, output_api
):
532 results
.extend(_CommonChecks(input_api
, output_api
))
536 def CheckChangeOnCommit(input_api
, output_api
):
538 results
.extend(_CommonChecks(input_api
, output_api
))
539 # TODO(thestig) temporarily disabled, doesn't work in third_party/
540 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
541 # input_api, output_api, sources))
542 # Make sure the tree is 'open'.
543 results
.extend(input_api
.canned_checks
.CheckTreeIsOpen(
546 json_url
='http://chromium-status.appspot.com/current?format=json'))
547 results
.extend(input_api
.canned_checks
.CheckRietveldTryJobExecution(input_api
,
548 output_api
, 'http://codereview.chromium.org',
549 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
550 'tryserver@chromium.org'))
552 results
.extend(input_api
.canned_checks
.CheckChangeHasBugField(
553 input_api
, output_api
))
554 results
.extend(input_api
.canned_checks
.CheckChangeHasDescription(
555 input_api
, output_api
))
556 results
.extend(_CheckSubversionConfig(input_api
, output_api
))
560 def GetPreferredTrySlaves(project
, change
):
561 files
= change
.LocalPaths()
566 if all(re
.search('\.(m|mm)$|[/_]mac[/_.]', f
) for f
in files
):
568 if all(re
.search('[/_]win[/_.]', f
) for f
in files
):
570 if all(re
.search('[/_]android[/_.]', f
) for f
in files
):
573 trybots
= ['win_rel', 'linux_rel', 'mac_rel', 'linux_clang:compile',
576 # Match things like path/aura/file.cc and path/file_aura.cc.
578 if any(re
.search('[/_](aura|chromeos)', f
) for f
in files
):
579 trybots
+= ['linux_chromeos', 'linux_chromeos_clang:compile']