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.
19 r
"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
20 r
"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
21 r
"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
27 r
"^webkit[\\\/]compositor_bindings[\\\/].*",
28 r
".+[\\\/]pnacl_shim\.c$",
32 _TEST_ONLY_WARNING
= (
33 'You might be calling functions intended only for testing from\n'
34 'production code. It is OK to ignore this warning if you know what\n'
35 'you are doing, as the heuristics used to detect the situation are\n'
36 'not perfect. The commit queue will not block on this warning.\n'
37 'Email joi@chromium.org if you have questions.')
40 _BANNED_OBJC_FUNCTIONS
= (
44 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
45 'prohibited. Please use CrTrackingArea instead.',
46 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
53 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
55 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
60 'convertPointFromBase:',
62 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
63 'Please use |convertPoint:(point) fromView:nil| instead.',
64 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
69 'convertPointToBase:',
71 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
72 'Please use |convertPoint:(point) toView:nil| instead.',
73 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
78 'convertRectFromBase:',
80 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
81 'Please use |convertRect:(point) fromView:nil| instead.',
82 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
89 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
90 'Please use |convertRect:(point) toView:nil| instead.',
91 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
96 'convertSizeFromBase:',
98 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
99 'Please use |convertSize:(point) fromView:nil| instead.',
100 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
105 'convertSizeToBase:',
107 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
108 'Please use |convertSize:(point) toView:nil| instead.',
109 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
116 _BANNED_CPP_FUNCTIONS
= (
117 # Make sure that gtest's FRIEND_TEST() macro is not used; the
118 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
119 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
123 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
124 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
131 'New code should not use ScopedAllowIO. Post a task to the blocking',
132 'pool or the FILE thread instead.',
137 'FilePathWatcher::Delegate',
139 'New code should not use FilePathWatcher::Delegate. Use the callback',
140 'interface instead.',
145 'browser::FindLastActiveWithProfile',
147 'This function is deprecated and we\'re working on removing it. Pass',
148 'more context to get a Browser*, like a WebContents, window, or session',
149 'id. Talk to robertshield@ for more information.',
154 'browser::FindAnyBrowser',
156 'This function is deprecated and we\'re working on removing it. Pass',
157 'more context to get a Browser*, like a WebContents, window, or session',
158 'id. Talk to robertshield@ for more information.',
163 'browser::FindOrCreateTabbedBrowser',
165 'This function is deprecated and we\'re working on removing it. Pass',
166 'more context to get a Browser*, like a WebContents, window, or session',
167 'id. Talk to robertshield@ for more information.',
172 'browser::FindTabbedBrowserDeprecated',
174 'This function is deprecated and we\'re working on removing it. Pass',
175 'more context to get a Browser*, like a WebContents, window, or session',
176 'id. Talk to robertshield@ for more information.',
183 'This function is deprecated and we\'re working on removing it. Rename',
192 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
):
193 """Attempts to prevent use of functions intended only for testing in
194 non-testing code. For now this is just a best-effort implementation
195 that ignores header files and may have some false positives. A
196 better implementation would probably need a proper C++ parser.
198 # We only scan .cc files and the like, as the declaration of
199 # for-testing functions in header files are hard to distinguish from
200 # calls to such functions without a proper C++ parser.
201 platform_specifiers
= r
'(_(android|chromeos|gtk|mac|posix|win))?'
202 source_extensions
= r
'\.(cc|cpp|cxx|mm)$'
203 file_inclusion_pattern
= r
'.+%s' % source_extensions
204 file_exclusion_patterns
= (
205 r
'.*[/\\](fake_|test_|mock_).+%s' % source_extensions
,
206 r
'.+_test_(base|support|util)%s' % source_extensions
,
207 r
'.+_(api|browser|perf|unit|ui)?test%s%s' % (platform_specifiers
,
209 r
'.+profile_sync_service_harness%s' % source_extensions
,
211 path_exclusion_patterns
= (
212 r
'.*[/\\](test|tool(s)?)[/\\].*',
213 # At request of folks maintaining this folder.
214 r
'chrome[/\\]browser[/\\]automation[/\\].*',
217 base_function_pattern
= r
'ForTest(ing)?|for_test(ing)?'
218 inclusion_pattern
= input_api
.re
.compile(r
'(%s)\s*\(' % base_function_pattern
)
219 exclusion_pattern
= input_api
.re
.compile(
220 r
'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
221 base_function_pattern
, base_function_pattern
))
223 def FilterFile(affected_file
):
224 black_list
= (file_exclusion_patterns
+ path_exclusion_patterns
+
225 _EXCLUDED_PATHS
+ input_api
.DEFAULT_BLACK_LIST
)
226 return input_api
.FilterSourceFile(
228 white_list
=(file_inclusion_pattern
, ),
229 black_list
=black_list
)
232 for f
in input_api
.AffectedSourceFiles(FilterFile
):
233 local_path
= f
.LocalPath()
234 lines
= input_api
.ReadFile(f
).splitlines()
237 if (inclusion_pattern
.search(line
) and
238 not exclusion_pattern
.search(line
)):
240 '%s:%d\n %s' % (local_path
, line_number
, line
.strip()))
244 if not input_api
.is_committing
:
245 return [output_api
.PresubmitPromptWarning(_TEST_ONLY_WARNING
, problems
)]
247 # We don't warn on commit, to avoid stopping commits going through CQ.
248 return [output_api
.PresubmitNotifyResult(_TEST_ONLY_WARNING
, problems
)]
253 def _CheckNoIOStreamInHeaders(input_api
, output_api
):
254 """Checks to make sure no .h files include <iostream>."""
256 pattern
= input_api
.re
.compile(r
'^#include\s*<iostream>',
257 input_api
.re
.MULTILINE
)
258 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
259 if not f
.LocalPath().endswith('.h'):
261 contents
= input_api
.ReadFile(f
)
262 if pattern
.search(contents
):
266 return [ output_api
.PresubmitError(
267 'Do not #include <iostream> in header files, since it inserts static '
268 'initialization into every file including the header. Instead, '
269 '#include <ostream>. See http://crbug.com/94794',
274 def _CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
):
275 """Checks to make sure no source files use UNIT_TEST"""
277 for f
in input_api
.AffectedFiles():
278 if (not f
.LocalPath().endswith(('.cc', '.mm'))):
281 for line_num
, line
in f
.ChangedContents():
282 if 'UNIT_TEST' in line
:
283 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
287 return [output_api
.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
288 '\n'.join(problems
))]
291 def _CheckNoNewWStrings(input_api
, output_api
):
292 """Checks to make sure we don't introduce use of wstrings."""
294 for f
in input_api
.AffectedFiles():
295 if (not f
.LocalPath().endswith(('.cc', '.h')) or
296 f
.LocalPath().endswith('test.cc')):
300 for line_num
, line
in f
.ChangedContents():
301 if 'presubmit: allow wstring' in line
:
303 elif not allowWString
and 'wstring' in line
:
304 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
311 return [output_api
.PresubmitPromptWarning('New code should not use wstrings.'
312 ' If you are calling a cross-platform API that accepts a wstring, '
314 '\n'.join(problems
))]
317 def _CheckNoDEPSGIT(input_api
, output_api
):
318 """Make sure .DEPS.git is never modified manually."""
319 if any(f
.LocalPath().endswith('.DEPS.git') for f
in
320 input_api
.AffectedFiles()):
321 return [output_api
.PresubmitError(
322 'Never commit changes to .DEPS.git. This file is maintained by an\n'
323 'automated system based on what\'s in DEPS and your changes will be\n'
325 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
326 'for more information')]
330 def _CheckNoBannedFunctions(input_api
, output_api
):
331 """Make sure that banned functions are not used."""
335 file_filter
= lambda f
: f
.LocalPath().endswith(('.mm', '.m', '.h'))
336 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
337 for line_num
, line
in f
.ChangedContents():
338 for func_name
, message
, error
in _BANNED_OBJC_FUNCTIONS
:
339 if func_name
in line
:
343 problems
.append(' %s:%d:' % (f
.LocalPath(), line_num
))
344 for message_line
in message
:
345 problems
.append(' %s' % message_line
)
347 file_filter
= lambda f
: f
.LocalPath().endswith(('.cc', '.mm', '.h'))
348 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
349 for line_num
, line
in f
.ChangedContents():
350 for func_name
, message
, error
in _BANNED_CPP_FUNCTIONS
:
351 if func_name
in line
:
355 problems
.append(' %s:%d:' % (f
.LocalPath(), line_num
))
356 for message_line
in message
:
357 problems
.append(' %s' % message_line
)
361 result
.append(output_api
.PresubmitPromptWarning(
362 'Banned functions were used.\n' + '\n'.join(warnings
)))
364 result
.append(output_api
.PresubmitError(
365 'Banned functions were used.\n' + '\n'.join(errors
)))
369 def _CheckNoPragmaOnce(input_api
, output_api
):
370 """Make sure that banned functions are not used."""
372 pattern
= input_api
.re
.compile(r
'^#pragma\s+once',
373 input_api
.re
.MULTILINE
)
374 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
375 if not f
.LocalPath().endswith('.h'):
377 contents
= input_api
.ReadFile(f
)
378 if pattern
.search(contents
):
382 return [output_api
.PresubmitError(
383 'Do not use #pragma once in header files.\n'
384 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
389 def _CheckNoTrinaryTrueFalse(input_api
, output_api
):
390 """Checks to make sure we don't introduce use of foo ? true : false."""
392 pattern
= input_api
.re
.compile(r
'\?\s*(true|false)\s*:\s*(true|false)')
393 for f
in input_api
.AffectedFiles():
394 if not f
.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
397 for line_num
, line
in f
.ChangedContents():
398 if pattern
.match(line
):
399 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
403 return [output_api
.PresubmitPromptWarning(
404 'Please consider avoiding the "? true : false" pattern if possible.\n' +
405 '\n'.join(problems
))]
408 def _CheckUnwantedDependencies(input_api
, output_api
):
409 """Runs checkdeps on #include statements added in this
410 change. Breaking - rules is an error, breaking ! rules is a
413 # We need to wait until we have an input_api object and use this
414 # roundabout construct to import checkdeps because this file is
415 # eval-ed and thus doesn't have __file__.
416 original_sys_path
= sys
.path
418 sys
.path
= sys
.path
+ [input_api
.os_path
.join(
419 input_api
.PresubmitLocalPath(), 'tools', 'checkdeps')]
421 from cpp_checker
import CppChecker
422 from rules
import Rule
424 # Restore sys.path to what it was before.
425 sys
.path
= original_sys_path
428 for f
in input_api
.AffectedFiles():
429 if not CppChecker
.IsCppFile(f
.LocalPath()):
432 changed_lines
= [line
for line_num
, line
in f
.ChangedContents()]
433 added_includes
.append([f
.LocalPath(), changed_lines
])
435 deps_checker
= checkdeps
.DepsChecker()
437 error_descriptions
= []
438 warning_descriptions
= []
439 for path
, rule_type
, rule_description
in deps_checker
.CheckAddedCppIncludes(
441 description_with_path
= '%s\n %s' % (path
, rule_description
)
442 if rule_type
== Rule
.DISALLOW
:
443 error_descriptions
.append(description_with_path
)
445 warning_descriptions
.append(description_with_path
)
448 if error_descriptions
:
449 results
.append(output_api
.PresubmitError(
450 'You added one or more #includes that violate checkdeps rules.',
452 if warning_descriptions
:
453 if not input_api
.is_committing
:
454 warning_factory
= output_api
.PresubmitPromptWarning
456 # We don't want to block use of the CQ when there is a warning
457 # of this kind, so we only show a message when committing.
458 warning_factory
= output_api
.PresubmitNotifyResult
459 results
.append(warning_factory(
460 'You added one or more #includes of files that are temporarily\n'
461 'allowed but being removed. Can you avoid introducing the\n'
462 '#include? See relevant DEPS file(s) for details and contacts.',
463 warning_descriptions
))
467 def _CheckFilePermissions(input_api
, output_api
):
468 """Check that all files have their permissions properly set."""
469 args
= [sys
.executable
, 'tools/checkperms/checkperms.py', '--root',
470 input_api
.change
.RepositoryRoot()]
471 for f
in input_api
.AffectedFiles():
472 args
+= ['--file', f
.LocalPath()]
474 (errors
, stderrdata
) = subprocess
.Popen(args
).communicate()
478 results
.append(output_api
.PresubmitError('checkperms.py failed.',
483 def _CheckNoAuraWindowPropertyHInHeaders(input_api
, output_api
):
484 """Makes sure we don't include ui/aura/window_property.h
487 pattern
= input_api
.re
.compile(r
'^#include\s*"ui/aura/window_property.h"')
489 for f
in input_api
.AffectedFiles():
490 if not f
.LocalPath().endswith('.h'):
492 for line_num
, line
in f
.ChangedContents():
493 if pattern
.match(line
):
494 errors
.append(' %s:%d' % (f
.LocalPath(), line_num
))
498 results
.append(output_api
.PresubmitError(
499 'Header files should not include ui/aura/window_property.h', errors
))
503 def _CommonChecks(input_api
, output_api
):
504 """Checks common to both upload and commit."""
506 results
.extend(input_api
.canned_checks
.PanProjectChecks(
507 input_api
, output_api
, excluded_paths
=_EXCLUDED_PATHS
))
508 results
.extend(_CheckAuthorizedAuthor(input_api
, output_api
))
510 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
))
511 results
.extend(_CheckNoIOStreamInHeaders(input_api
, output_api
))
512 results
.extend(_CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
))
513 results
.extend(_CheckNoNewWStrings(input_api
, output_api
))
514 results
.extend(_CheckNoDEPSGIT(input_api
, output_api
))
515 results
.extend(_CheckNoBannedFunctions(input_api
, output_api
))
516 results
.extend(_CheckNoPragmaOnce(input_api
, output_api
))
517 results
.extend(_CheckNoTrinaryTrueFalse(input_api
, output_api
))
518 results
.extend(_CheckUnwantedDependencies(input_api
, output_api
))
519 results
.extend(_CheckFilePermissions(input_api
, output_api
))
520 results
.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api
, output_api
))
524 def _CheckSubversionConfig(input_api
, output_api
):
525 """Verifies the subversion config file is correctly setup.
527 Checks that autoprops are enabled, returns an error otherwise.
529 join
= input_api
.os_path
.join
530 if input_api
.platform
== 'win32':
531 appdata
= input_api
.environ
.get('APPDATA', '')
533 return [output_api
.PresubmitError('%APPDATA% is not configured.')]
534 path
= join(appdata
, 'Subversion', 'config')
536 home
= input_api
.environ
.get('HOME', '')
538 return [output_api
.PresubmitError('$HOME is not configured.')]
539 path
= join(home
, '.subversion', 'config')
542 'Please look at http://dev.chromium.org/developers/coding-style to\n'
543 'configure your subversion configuration file. This enables automatic\n'
544 'properties to simplify the project maintenance.\n'
545 'Pro-tip: just download and install\n'
546 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
549 lines
= open(path
, 'r').read().splitlines()
550 # Make sure auto-props is enabled and check for 2 Chromium standard
552 if (not '*.cc = svn:eol-style=LF' in lines
or
553 not '*.pdf = svn:mime-type=application/pdf' in lines
or
554 not 'enable-auto-props = yes' in lines
):
556 output_api
.PresubmitNotifyResult(
557 'It looks like you have not configured your subversion config '
558 'file or it is not up-to-date.\n' + error_msg
)
560 except (OSError, IOError):
562 output_api
.PresubmitNotifyResult(
563 'Can\'t find your subversion config file.\n' + error_msg
)
568 def _CheckAuthorizedAuthor(input_api
, output_api
):
569 """For non-googler/chromites committers, verify the author's email address is
572 # TODO(maruel): Add it to input_api?
575 author
= input_api
.change
.author_email
577 input_api
.logging
.info('No author, skipping AUTHOR check')
579 authors_path
= input_api
.os_path
.join(
580 input_api
.PresubmitLocalPath(), 'AUTHORS')
582 input_api
.re
.match(r
'[^#]+\s+\<(.+?)\>\s*$', line
)
583 for line
in open(authors_path
))
584 valid_authors
= [item
.group(1).lower() for item
in valid_authors
if item
]
585 if input_api
.verbose
:
586 print 'Valid authors are %s' % ', '.join(valid_authors
)
587 if not any(fnmatch
.fnmatch(author
.lower(), valid
) for valid
in valid_authors
):
588 return [output_api
.PresubmitPromptWarning(
589 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
591 'http://www.chromium.org/developers/contributing-code and read the '
593 'If you are a chromite, verify the contributor signed the CLA.') %
598 def CheckChangeOnUpload(input_api
, output_api
):
600 results
.extend(_CommonChecks(input_api
, output_api
))
604 def CheckChangeOnCommit(input_api
, output_api
):
606 results
.extend(_CommonChecks(input_api
, output_api
))
607 # TODO(thestig) temporarily disabled, doesn't work in third_party/
608 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
609 # input_api, output_api, sources))
610 # Make sure the tree is 'open'.
611 results
.extend(input_api
.canned_checks
.CheckTreeIsOpen(
614 json_url
='http://chromium-status.appspot.com/current?format=json'))
615 results
.extend(input_api
.canned_checks
.CheckRietveldTryJobExecution(input_api
,
616 output_api
, 'http://codereview.chromium.org',
617 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
618 'tryserver@chromium.org'))
620 results
.extend(input_api
.canned_checks
.CheckChangeHasBugField(
621 input_api
, output_api
))
622 results
.extend(input_api
.canned_checks
.CheckChangeHasDescription(
623 input_api
, output_api
))
624 results
.extend(_CheckSubversionConfig(input_api
, output_api
))
628 def GetPreferredTrySlaves(project
, change
):
629 files
= change
.LocalPaths()
634 if all(re
.search('\.(m|mm)$|(^|[/_])mac[/_.]', f
) for f
in files
):
635 return ['mac_rel', 'mac_asan']
636 if all(re
.search('(^|[/_])win[/_.]', f
) for f
in files
):
638 if all(re
.search('(^|[/_])android[/_.]', f
) for f
in files
):
639 return ['android_dbg']
640 if all(re
.search('^native_client_sdk', f
) for f
in files
):
641 return ['linux_nacl_sdk', 'win_nacl_sdk', 'mac_nacl_sdk']
642 if all(re
.search('[/_]ios[/_.]', f
) for f
in files
):
643 return ['ios_rel_device', 'ios_dbg_simulator']
645 trybots
= ['win_rel', 'linux_rel', 'mac_rel', 'linux_clang:compile',
646 'linux_chromeos', 'android_dbg', 'linux_asan', 'mac_asan',
647 'ios_rel_device', 'ios_dbg_simulator']
649 # Match things like path/aura/file.cc and path/file_aura.cc.
650 # Same for ash and chromeos.
651 if any(re
.search('[/_](ash|aura)', f
) for f
in files
):
652 trybots
+= ['linux_chromeos', 'linux_chromeos_clang:compile', 'win_aura',
653 'linux_chromeos_asan']
655 if any(re
.search('[/_]chromeos', f
) for f
in files
):
656 trybots
+= ['linux_chromeos', 'linux_chromeos_clang:compile',
657 'linux_chromeos_asan']