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.
14 r
"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
15 r
"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
16 r
"^native_client_sdk[\\\/]src[\\\/]tools[\\\/].*.mk",
17 r
"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
22 r
".+[\\\/]pnacl_shim\.c$",
23 r
"^gpu[\\\/]config[\\\/].*_list_json\.cc$",
24 r
"^chrome[\\\/]browser[\\\/]resources[\\\/]pdf[\\\/]index.js"
27 # The NetscapePlugIn library is excluded from pan-project as it will soon
28 # be deleted together with the rest of the NPAPI and it's not worthwhile to
29 # update the coding style until then.
31 r
"^content[\\\/]shell[\\\/]tools[\\\/]plugin[\\\/].*",
34 # Fragment of a regular expression that matches C++ and Objective-C++
35 # implementation files.
36 _IMPLEMENTATION_EXTENSIONS
= r
'\.(cc|cpp|cxx|mm)$'
38 # Regular expression that matches code only used for test binaries
40 _TEST_CODE_EXCLUDED_PATHS
= (
41 r
'.*[\\\/](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS
,
42 r
'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS
,
43 r
'.+_(api|browser|kif|perf|pixel|unit|ui)?test(_[a-z]+)?%s' %
44 _IMPLEMENTATION_EXTENSIONS
,
45 r
'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS
,
46 r
'.*[\\\/](test|tool(s)?)[\\\/].*',
47 # content_shell is used for running layout tests.
48 r
'content[\\\/]shell[\\\/].*',
49 # At request of folks maintaining this folder.
50 r
'chrome[\\\/]browser[\\\/]automation[\\\/].*',
51 # Non-production example code.
52 r
'mojo[\\\/]examples[\\\/].*',
53 # Launcher for running iOS tests on the simulator.
54 r
'testing[\\\/]iossim[\\\/]iossim\.mm$',
57 _TEST_ONLY_WARNING
= (
58 'You might be calling functions intended only for testing from\n'
59 'production code. It is OK to ignore this warning if you know what\n'
60 'you are doing, as the heuristics used to detect the situation are\n'
61 'not perfect. The commit queue will not block on this warning.')
64 _INCLUDE_ORDER_WARNING
= (
65 'Your #include order seems to be broken. Send mail to\n'
66 'marja@chromium.org if this is not the case.')
69 _BANNED_OBJC_FUNCTIONS
= (
73 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
74 'prohibited. Please use CrTrackingArea instead.',
75 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
82 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
84 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
89 'convertPointFromBase:',
91 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
92 'Please use |convertPoint:(point) fromView:nil| instead.',
93 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
98 'convertPointToBase:',
100 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
101 'Please use |convertPoint:(point) toView:nil| instead.',
102 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
107 'convertRectFromBase:',
109 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
110 'Please use |convertRect:(point) fromView:nil| instead.',
111 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
116 'convertRectToBase:',
118 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
119 'Please use |convertRect:(point) toView:nil| instead.',
120 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
125 'convertSizeFromBase:',
127 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
128 'Please use |convertSize:(point) fromView:nil| instead.',
129 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
134 'convertSizeToBase:',
136 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
137 'Please use |convertSize:(point) toView:nil| instead.',
138 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
145 _BANNED_CPP_FUNCTIONS
= (
146 # Make sure that gtest's FRIEND_TEST() macro is not used; the
147 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
148 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
152 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
153 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
161 'New code should not use ScopedAllowIO. Post a task to the blocking',
162 'pool or the FILE thread instead.',
166 r
"^base[\\\/]process[\\\/]process_metrics_linux\.cc$",
167 r
"^chrome[\\\/]browser[\\\/]chromeos[\\\/]boot_times_loader\.cc$",
168 r
"^components[\\\/]crash[\\\/]app[\\\/]breakpad_mac\.mm$",
169 r
"^content[\\\/]shell[\\\/]browser[\\\/]shell_browser_main\.cc$",
170 r
"^content[\\\/]shell[\\\/]browser[\\\/]shell_message_filter\.cc$",
171 r
"^mojo[\\\/]edk[\\\/]embedder[\\\/]" +
172 r
"simple_platform_shared_buffer_posix\.cc$",
173 r
"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
174 r
"^net[\\\/]url_request[\\\/]test_url_fetcher_factory\.cc$",
180 'The use of SkRefPtr is prohibited. ',
181 'Please use skia::RefPtr instead.'
189 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
190 'Please use skia::RefPtr instead.'
198 'The use of SkAutoTUnref is dangerous because it implicitly ',
199 'converts to a raw pointer. Please use skia::RefPtr instead.'
207 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
208 'because it implicitly converts to a raw pointer. ',
209 'Please use skia::RefPtr instead.'
215 r
'/HANDLE_EINTR\(.*close',
217 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
218 'descriptor will be closed, and it is incorrect to retry the close.',
219 'Either call close directly and ignore its return value, or wrap close',
220 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
226 r
'/IGNORE_EINTR\((?!.*close)',
228 'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
229 'calls, use HANDLE_EINTR. See http://crbug.com/269623',
233 # Files that #define IGNORE_EINTR.
234 r
'^base[\\\/]posix[\\\/]eintr_wrapper\.h$',
235 r
'^ppapi[\\\/]tests[\\\/]test_broker\.cc$',
241 'Do not introduce new v8::Extensions into the code base, use',
242 'gin::Wrappable instead. See http://crbug.com/334679',
246 r
'extensions[\\\/]renderer[\\\/]safe_builtins\.*',
251 _IPC_ENUM_TRAITS_DEPRECATED
= (
252 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
253 'See http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc')
257 # Please keep sorted.
261 'OS_CAT', # For testing.
276 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
):
277 """Attempts to prevent use of functions intended only for testing in
278 non-testing code. For now this is just a best-effort implementation
279 that ignores header files and may have some false positives. A
280 better implementation would probably need a proper C++ parser.
282 # We only scan .cc files and the like, as the declaration of
283 # for-testing functions in header files are hard to distinguish from
284 # calls to such functions without a proper C++ parser.
285 file_inclusion_pattern
= r
'.+%s' % _IMPLEMENTATION_EXTENSIONS
287 base_function_pattern
= r
'[ :]test::[^\s]+|ForTest(ing)?|for_test(ing)?'
288 inclusion_pattern
= input_api
.re
.compile(r
'(%s)\s*\(' % base_function_pattern
)
289 comment_pattern
= input_api
.re
.compile(r
'//.*(%s)' % base_function_pattern
)
290 exclusion_pattern
= input_api
.re
.compile(
291 r
'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
292 base_function_pattern
, base_function_pattern
))
294 def FilterFile(affected_file
):
295 black_list
= (_EXCLUDED_PATHS
+
296 _TEST_CODE_EXCLUDED_PATHS
+
297 input_api
.DEFAULT_BLACK_LIST
)
298 return input_api
.FilterSourceFile(
300 white_list
=(file_inclusion_pattern
, ),
301 black_list
=black_list
)
304 for f
in input_api
.AffectedSourceFiles(FilterFile
):
305 local_path
= f
.LocalPath()
306 for line_number
, line
in f
.ChangedContents():
307 if (inclusion_pattern
.search(line
) and
308 not comment_pattern
.search(line
) and
309 not exclusion_pattern
.search(line
)):
311 '%s:%d\n %s' % (local_path
, line_number
, line
.strip()))
314 return [output_api
.PresubmitPromptOrNotify(_TEST_ONLY_WARNING
, problems
)]
319 def _CheckNoIOStreamInHeaders(input_api
, output_api
):
320 """Checks to make sure no .h files include <iostream>."""
322 pattern
= input_api
.re
.compile(r
'^#include\s*<iostream>',
323 input_api
.re
.MULTILINE
)
324 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
325 if not f
.LocalPath().endswith('.h'):
327 contents
= input_api
.ReadFile(f
)
328 if pattern
.search(contents
):
332 return [ output_api
.PresubmitError(
333 'Do not #include <iostream> in header files, since it inserts static '
334 'initialization into every file including the header. Instead, '
335 '#include <ostream>. See http://crbug.com/94794',
340 def _CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
):
341 """Checks to make sure no source files use UNIT_TEST"""
343 for f
in input_api
.AffectedFiles():
344 if (not f
.LocalPath().endswith(('.cc', '.mm'))):
347 for line_num
, line
in f
.ChangedContents():
348 if 'UNIT_TEST ' in line
or line
.endswith('UNIT_TEST'):
349 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
353 return [output_api
.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
354 '\n'.join(problems
))]
357 def _CheckNoNewWStrings(input_api
, output_api
):
358 """Checks to make sure we don't introduce use of wstrings."""
360 for f
in input_api
.AffectedFiles():
361 if (not f
.LocalPath().endswith(('.cc', '.h')) or
362 f
.LocalPath().endswith(('test.cc', '_win.cc', '_win.h'))):
366 for line_num
, line
in f
.ChangedContents():
367 if 'presubmit: allow wstring' in line
:
369 elif not allowWString
and 'wstring' in line
:
370 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
377 return [output_api
.PresubmitPromptWarning('New code should not use wstrings.'
378 ' If you are calling a cross-platform API that accepts a wstring, '
380 '\n'.join(problems
))]
383 def _CheckNoDEPSGIT(input_api
, output_api
):
384 """Make sure .DEPS.git is never modified manually."""
385 if any(f
.LocalPath().endswith('.DEPS.git') for f
in
386 input_api
.AffectedFiles()):
387 return [output_api
.PresubmitError(
388 'Never commit changes to .DEPS.git. This file is maintained by an\n'
389 'automated system based on what\'s in DEPS and your changes will be\n'
391 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/get-the-code#Rolling_DEPS\n'
392 'for more information')]
396 def _CheckValidHostsInDEPS(input_api
, output_api
):
397 """Checks that DEPS file deps are from allowed_hosts."""
398 # Run only if DEPS file has been modified to annoy fewer bystanders.
399 if all(f
.LocalPath() != 'DEPS' for f
in input_api
.AffectedFiles()):
401 # Outsource work to gclient verify
403 input_api
.subprocess
.check_output(['gclient', 'verify'])
405 except input_api
.subprocess
.CalledProcessError
, error
:
406 return [output_api
.PresubmitError(
407 'DEPS file must have only git dependencies.',
408 long_text
=error
.output
)]
411 def _CheckNoBannedFunctions(input_api
, output_api
):
412 """Make sure that banned functions are not used."""
416 file_filter
= lambda f
: f
.LocalPath().endswith(('.mm', '.m', '.h'))
417 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
418 for line_num
, line
in f
.ChangedContents():
419 for func_name
, message
, error
in _BANNED_OBJC_FUNCTIONS
:
421 if func_name
[0:1] == '/':
422 regex
= func_name
[1:]
423 if input_api
.re
.search(regex
, line
):
425 elif func_name
in line
:
431 problems
.append(' %s:%d:' % (f
.LocalPath(), line_num
))
432 for message_line
in message
:
433 problems
.append(' %s' % message_line
)
435 file_filter
= lambda f
: f
.LocalPath().endswith(('.cc', '.mm', '.h'))
436 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
437 for line_num
, line
in f
.ChangedContents():
438 for func_name
, message
, error
, excluded_paths
in _BANNED_CPP_FUNCTIONS
:
439 def IsBlacklisted(affected_file
, blacklist
):
440 local_path
= affected_file
.LocalPath()
441 for item
in blacklist
:
442 if input_api
.re
.match(item
, local_path
):
445 if IsBlacklisted(f
, excluded_paths
):
448 if func_name
[0:1] == '/':
449 regex
= func_name
[1:]
450 if input_api
.re
.search(regex
, line
):
452 elif func_name
in line
:
458 problems
.append(' %s:%d:' % (f
.LocalPath(), line_num
))
459 for message_line
in message
:
460 problems
.append(' %s' % message_line
)
464 result
.append(output_api
.PresubmitPromptWarning(
465 'Banned functions were used.\n' + '\n'.join(warnings
)))
467 result
.append(output_api
.PresubmitError(
468 'Banned functions were used.\n' + '\n'.join(errors
)))
472 def _CheckNoPragmaOnce(input_api
, output_api
):
473 """Make sure that banned functions are not used."""
475 pattern
= input_api
.re
.compile(r
'^#pragma\s+once',
476 input_api
.re
.MULTILINE
)
477 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
478 if not f
.LocalPath().endswith('.h'):
480 contents
= input_api
.ReadFile(f
)
481 if pattern
.search(contents
):
485 return [output_api
.PresubmitError(
486 'Do not use #pragma once in header files.\n'
487 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
492 def _CheckNoTrinaryTrueFalse(input_api
, output_api
):
493 """Checks to make sure we don't introduce use of foo ? true : false."""
495 pattern
= input_api
.re
.compile(r
'\?\s*(true|false)\s*:\s*(true|false)')
496 for f
in input_api
.AffectedFiles():
497 if not f
.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
500 for line_num
, line
in f
.ChangedContents():
501 if pattern
.match(line
):
502 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
506 return [output_api
.PresubmitPromptWarning(
507 'Please consider avoiding the "? true : false" pattern if possible.\n' +
508 '\n'.join(problems
))]
511 def _CheckUnwantedDependencies(input_api
, output_api
):
512 """Runs checkdeps on #include statements added in this
513 change. Breaking - rules is an error, breaking ! rules is a
517 # We need to wait until we have an input_api object and use this
518 # roundabout construct to import checkdeps because this file is
519 # eval-ed and thus doesn't have __file__.
520 original_sys_path
= sys
.path
522 sys
.path
= sys
.path
+ [input_api
.os_path
.join(
523 input_api
.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
525 from cpp_checker
import CppChecker
526 from rules
import Rule
528 # Restore sys.path to what it was before.
529 sys
.path
= original_sys_path
532 for f
in input_api
.AffectedFiles():
533 if not CppChecker
.IsCppFile(f
.LocalPath()):
536 changed_lines
= [line
for line_num
, line
in f
.ChangedContents()]
537 added_includes
.append([f
.LocalPath(), changed_lines
])
539 deps_checker
= checkdeps
.DepsChecker(input_api
.PresubmitLocalPath())
541 error_descriptions
= []
542 warning_descriptions
= []
543 for path
, rule_type
, rule_description
in deps_checker
.CheckAddedCppIncludes(
545 description_with_path
= '%s\n %s' % (path
, rule_description
)
546 if rule_type
== Rule
.DISALLOW
:
547 error_descriptions
.append(description_with_path
)
549 warning_descriptions
.append(description_with_path
)
552 if error_descriptions
:
553 results
.append(output_api
.PresubmitError(
554 'You added one or more #includes that violate checkdeps rules.',
556 if warning_descriptions
:
557 results
.append(output_api
.PresubmitPromptOrNotify(
558 'You added one or more #includes of files that are temporarily\n'
559 'allowed but being removed. Can you avoid introducing the\n'
560 '#include? See relevant DEPS file(s) for details and contacts.',
561 warning_descriptions
))
565 def _CheckFilePermissions(input_api
, output_api
):
566 """Check that all files have their permissions properly set."""
567 if input_api
.platform
== 'win32':
569 args
= [input_api
.python_executable
, 'tools/checkperms/checkperms.py',
570 '--root', input_api
.change
.RepositoryRoot()]
571 for f
in input_api
.AffectedFiles():
572 args
+= ['--file', f
.LocalPath()]
573 checkperms
= input_api
.subprocess
.Popen(args
,
574 stdout
=input_api
.subprocess
.PIPE
)
575 errors
= checkperms
.communicate()[0].strip()
577 return [output_api
.PresubmitError('checkperms.py failed.',
578 errors
.splitlines())]
582 def _CheckNoAuraWindowPropertyHInHeaders(input_api
, output_api
):
583 """Makes sure we don't include ui/aura/window_property.h
586 pattern
= input_api
.re
.compile(r
'^#include\s*"ui/aura/window_property.h"')
588 for f
in input_api
.AffectedFiles():
589 if not f
.LocalPath().endswith('.h'):
591 for line_num
, line
in f
.ChangedContents():
592 if pattern
.match(line
):
593 errors
.append(' %s:%d' % (f
.LocalPath(), line_num
))
597 results
.append(output_api
.PresubmitError(
598 'Header files should not include ui/aura/window_property.h', errors
))
602 def _CheckIncludeOrderForScope(scope
, input_api
, file_path
, changed_linenums
):
603 """Checks that the lines in scope occur in the right order.
605 1. C system files in alphabetical order
606 2. C++ system files in alphabetical order
607 3. Project's .h files
610 c_system_include_pattern
= input_api
.re
.compile(r
'\s*#include <.*\.h>')
611 cpp_system_include_pattern
= input_api
.re
.compile(r
'\s*#include <.*>')
612 custom_include_pattern
= input_api
.re
.compile(r
'\s*#include ".*')
614 C_SYSTEM_INCLUDES
, CPP_SYSTEM_INCLUDES
, CUSTOM_INCLUDES
= range(3)
616 state
= C_SYSTEM_INCLUDES
619 previous_line_num
= 0
620 problem_linenums
= []
621 for line_num
, line
in scope
:
622 if c_system_include_pattern
.match(line
):
623 if state
!= C_SYSTEM_INCLUDES
:
624 problem_linenums
.append((line_num
, previous_line_num
))
625 elif previous_line
and previous_line
> line
:
626 problem_linenums
.append((line_num
, previous_line_num
))
627 elif cpp_system_include_pattern
.match(line
):
628 if state
== C_SYSTEM_INCLUDES
:
629 state
= CPP_SYSTEM_INCLUDES
630 elif state
== CUSTOM_INCLUDES
:
631 problem_linenums
.append((line_num
, previous_line_num
))
632 elif previous_line
and previous_line
> line
:
633 problem_linenums
.append((line_num
, previous_line_num
))
634 elif custom_include_pattern
.match(line
):
635 if state
!= CUSTOM_INCLUDES
:
636 state
= CUSTOM_INCLUDES
637 elif previous_line
and previous_line
> line
:
638 problem_linenums
.append((line_num
, previous_line_num
))
640 problem_linenums
.append(line_num
)
642 previous_line_num
= line_num
645 for (line_num
, previous_line_num
) in problem_linenums
:
646 if line_num
in changed_linenums
or previous_line_num
in changed_linenums
:
647 warnings
.append(' %s:%d' % (file_path
, line_num
))
651 def _CheckIncludeOrderInFile(input_api
, f
, changed_linenums
):
652 """Checks the #include order for the given file f."""
654 system_include_pattern
= input_api
.re
.compile(r
'\s*#include \<.*')
655 # Exclude the following includes from the check:
656 # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
658 # 2) <atlbase.h>, "build/build_config.h"
659 excluded_include_pattern
= input_api
.re
.compile(
660 r
'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
661 custom_include_pattern
= input_api
.re
.compile(r
'\s*#include "(?P<FILE>.*)"')
662 # Match the final or penultimate token if it is xxxtest so we can ignore it
663 # when considering the special first include.
664 test_file_tag_pattern
= input_api
.re
.compile(
665 r
'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
666 if_pattern
= input_api
.re
.compile(
667 r
'\s*#\s*(if|elif|else|endif|define|undef).*')
668 # Some files need specialized order of includes; exclude such files from this
670 uncheckable_includes_pattern
= input_api
.re
.compile(
672 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
674 contents
= f
.NewContents()
678 # Handle the special first include. If the first include file is
679 # some/path/file.h, the corresponding including file can be some/path/file.cc,
680 # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
681 # etc. It's also possible that no special first include exists.
682 # If the included file is some/path/file_platform.h the including file could
683 # also be some/path/file_xxxtest_platform.h.
684 including_file_base_name
= test_file_tag_pattern
.sub(
685 '', input_api
.os_path
.basename(f
.LocalPath()))
687 for line
in contents
:
689 if system_include_pattern
.match(line
):
690 # No special first include -> process the line again along with normal
694 match
= custom_include_pattern
.match(line
)
696 match_dict
= match
.groupdict()
697 header_basename
= test_file_tag_pattern
.sub(
698 '', input_api
.os_path
.basename(match_dict
['FILE'])).replace('.h', '')
700 if header_basename
not in including_file_base_name
:
701 # No special first include -> process the line again along with normal
706 # Split into scopes: Each region between #if and #endif is its own scope.
709 for line
in contents
[line_num
:]:
711 if uncheckable_includes_pattern
.match(line
):
713 if if_pattern
.match(line
):
714 scopes
.append(current_scope
)
716 elif ((system_include_pattern
.match(line
) or
717 custom_include_pattern
.match(line
)) and
718 not excluded_include_pattern
.match(line
)):
719 current_scope
.append((line_num
, line
))
720 scopes
.append(current_scope
)
723 warnings
.extend(_CheckIncludeOrderForScope(scope
, input_api
, f
.LocalPath(),
728 def _CheckIncludeOrder(input_api
, output_api
):
729 """Checks that the #include order is correct.
731 1. The corresponding header for source files.
732 2. C system files in alphabetical order
733 3. C++ system files in alphabetical order
734 4. Project's .h files in alphabetical order
736 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
737 these rules separately.
739 def FileFilterIncludeOrder(affected_file
):
740 black_list
= (_EXCLUDED_PATHS
+ input_api
.DEFAULT_BLACK_LIST
)
741 return input_api
.FilterSourceFile(affected_file
, black_list
=black_list
)
744 for f
in input_api
.AffectedFiles(file_filter
=FileFilterIncludeOrder
):
745 if f
.LocalPath().endswith(('.cc', '.h')):
746 changed_linenums
= set(line_num
for line_num
, _
in f
.ChangedContents())
747 warnings
.extend(_CheckIncludeOrderInFile(input_api
, f
, changed_linenums
))
751 results
.append(output_api
.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING
,
756 def _CheckForVersionControlConflictsInFile(input_api
, f
):
757 pattern
= input_api
.re
.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
759 for line_num
, line
in f
.ChangedContents():
760 if pattern
.match(line
):
761 errors
.append(' %s:%d %s' % (f
.LocalPath(), line_num
, line
))
765 def _CheckForVersionControlConflicts(input_api
, output_api
):
766 """Usually this is not intentional and will cause a compile failure."""
768 for f
in input_api
.AffectedFiles():
769 errors
.extend(_CheckForVersionControlConflictsInFile(input_api
, f
))
773 results
.append(output_api
.PresubmitError(
774 'Version control conflict markers found, please resolve.', errors
))
778 def _CheckHardcodedGoogleHostsInLowerLayers(input_api
, output_api
):
779 def FilterFile(affected_file
):
780 """Filter function for use with input_api.AffectedSourceFiles,
781 below. This filters out everything except non-test files from
782 top-level directories that generally speaking should not hard-code
783 service URLs (e.g. src/android_webview/, src/content/ and others).
785 return input_api
.FilterSourceFile(
787 white_list
=(r
'^(android_webview|base|content|net)[\\\/].*', ),
788 black_list
=(_EXCLUDED_PATHS
+
789 _TEST_CODE_EXCLUDED_PATHS
+
790 input_api
.DEFAULT_BLACK_LIST
))
792 base_pattern
= '"[^"]*google\.com[^"]*"'
793 comment_pattern
= input_api
.re
.compile('//.*%s' % base_pattern
)
794 pattern
= input_api
.re
.compile(base_pattern
)
795 problems
= [] # items are (filename, line_number, line)
796 for f
in input_api
.AffectedSourceFiles(FilterFile
):
797 for line_num
, line
in f
.ChangedContents():
798 if not comment_pattern
.search(line
) and pattern
.search(line
):
799 problems
.append((f
.LocalPath(), line_num
, line
))
802 return [output_api
.PresubmitPromptOrNotify(
803 'Most layers below src/chrome/ should not hardcode service URLs.\n'
804 'Are you sure this is correct?',
806 problem
[0], problem
[1], problem
[2]) for problem
in problems
])]
811 def _CheckNoAbbreviationInPngFileName(input_api
, output_api
):
812 """Makes sure there are no abbreviations in the name of PNG files.
814 pattern
= input_api
.re
.compile(r
'.*_[a-z]_.*\.png$|.*_[a-z]\.png$')
816 for f
in input_api
.AffectedFiles(include_deletes
=False):
817 if pattern
.match(f
.LocalPath()):
818 errors
.append(' %s' % f
.LocalPath())
822 results
.append(output_api
.PresubmitError(
823 'The name of PNG files should not have abbreviations. \n'
824 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
825 'Contact oshima@chromium.org if you have questions.', errors
))
829 def _FilesToCheckForIncomingDeps(re
, changed_lines
):
830 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
831 a set of DEPS entries that we should look up.
833 For a directory (rather than a specific filename) we fake a path to
834 a specific filename by adding /DEPS. This is chosen as a file that
835 will seldom or never be subject to per-file include_rules.
837 # We ignore deps entries on auto-generated directories.
838 AUTO_GENERATED_DIRS
= ['grit', 'jni']
840 # This pattern grabs the path without basename in the first
841 # parentheses, and the basename (if present) in the second. It
842 # relies on the simple heuristic that if there is a basename it will
843 # be a header file ending in ".h".
844 pattern
= re
.compile(
845 r
"""['"]\+([^'"]+?)(/[a-zA-Z0-9_]+\.h)?['"].*""")
847 for changed_line
in changed_lines
:
848 m
= pattern
.match(changed_line
)
851 if path
.split('/')[0] not in AUTO_GENERATED_DIRS
:
853 results
.add('%s%s' % (path
, m
.group(2)))
855 results
.add('%s/DEPS' % path
)
859 def _CheckAddedDepsHaveTargetApprovals(input_api
, output_api
):
860 """When a dependency prefixed with + is added to a DEPS file, we
861 want to make sure that the change is reviewed by an OWNER of the
862 target file or directory, to avoid layering violations from being
863 introduced. This check verifies that this happens.
865 changed_lines
= set()
866 for f
in input_api
.AffectedFiles():
867 filename
= input_api
.os_path
.basename(f
.LocalPath())
868 if filename
== 'DEPS':
869 changed_lines |
= set(line
.strip()
871 in f
.ChangedContents())
872 if not changed_lines
:
875 virtual_depended_on_files
= _FilesToCheckForIncomingDeps(input_api
.re
,
877 if not virtual_depended_on_files
:
880 if input_api
.is_committing
:
882 return [output_api
.PresubmitNotifyResult(
883 '--tbr was specified, skipping OWNERS check for DEPS additions')]
884 if not input_api
.change
.issue
:
885 return [output_api
.PresubmitError(
886 "DEPS approval by OWNERS check failed: this change has "
887 "no Rietveld issue number, so we can't check it for approvals.")]
888 output
= output_api
.PresubmitError
890 output
= output_api
.PresubmitNotifyResult
892 owners_db
= input_api
.owners_db
893 owner_email
, reviewers
= input_api
.canned_checks
._RietveldOwnerAndReviewers
(
895 owners_db
.email_regexp
,
896 approval_needed
=input_api
.is_committing
)
898 owner_email
= owner_email
or input_api
.change
.author_email
900 reviewers_plus_owner
= set(reviewers
)
902 reviewers_plus_owner
.add(owner_email
)
903 missing_files
= owners_db
.files_not_covered_by(virtual_depended_on_files
,
904 reviewers_plus_owner
)
906 # We strip the /DEPS part that was added by
907 # _FilesToCheckForIncomingDeps to fake a path to a file in a
910 start_deps
= path
.rfind('/DEPS')
912 return path
[:start_deps
]
915 unapproved_dependencies
= ["'+%s'," % StripDeps(path
)
916 for path
in missing_files
]
918 if unapproved_dependencies
:
920 output('Missing LGTM from OWNERS of dependencies added to DEPS:\n %s' %
921 '\n '.join(sorted(unapproved_dependencies
)))]
922 if not input_api
.is_committing
:
923 suggested_owners
= owners_db
.reviewers_for(missing_files
, owner_email
)
924 output_list
.append(output(
925 'Suggested missing target path OWNERS:\n %s' %
926 '\n '.join(suggested_owners
or [])))
932 def _CheckSpamLogging(input_api
, output_api
):
933 file_inclusion_pattern
= r
'.+%s' % _IMPLEMENTATION_EXTENSIONS
934 black_list
= (_EXCLUDED_PATHS
+
935 _TEST_CODE_EXCLUDED_PATHS
+
936 input_api
.DEFAULT_BLACK_LIST
+
937 (r
"^base[\\\/]logging\.h$",
938 r
"^base[\\\/]logging\.cc$",
939 r
"^chrome[\\\/]app[\\\/]chrome_main_delegate\.cc$",
940 r
"^chrome[\\\/]browser[\\\/]chrome_browser_main\.cc$",
941 r
"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
942 r
"startup_browser_creator\.cc$",
943 r
"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
944 r
"chrome[\\\/]browser[\\\/]diagnostics[\\\/]" +
945 r
"diagnostics_writer\.cc$",
946 r
"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$",
947 r
"^chromecast[\\\/]",
948 r
"^cloud_print[\\\/]",
949 r
"^content[\\\/]common[\\\/]gpu[\\\/]client[\\\/]"
950 r
"gl_helper_benchmark\.cc$",
951 r
"^courgette[\\\/]courgette_tool\.cc$",
952 r
"^extensions[\\\/]renderer[\\\/]logging_native_handler\.cc$",
953 r
"^native_client_sdk[\\\/]",
954 r
"^remoting[\\\/]base[\\\/]logging\.h$",
955 r
"^remoting[\\\/]host[\\\/].*",
956 r
"^sandbox[\\\/]linux[\\\/].*",
958 r
"^ui[\\\/]aura[\\\/]bench[\\\/]bench_main\.cc$",
959 r
"^webkit[\\\/]browser[\\\/]fileapi[\\\/]" +
960 r
"dump_file_system.cc$",))
961 source_file_filter
= lambda x
: input_api
.FilterSourceFile(
962 x
, white_list
=(file_inclusion_pattern
,), black_list
=black_list
)
967 for f
in input_api
.AffectedSourceFiles(source_file_filter
):
968 contents
= input_api
.ReadFile(f
, 'rb')
969 if input_api
.re
.search(r
"\bD?LOG\s*\(\s*INFO\s*\)", contents
):
970 log_info
.append(f
.LocalPath())
971 elif input_api
.re
.search(r
"\bD?LOG_IF\s*\(\s*INFO\s*,", contents
):
972 log_info
.append(f
.LocalPath())
974 if input_api
.re
.search(r
"\bprintf\(", contents
):
975 printf
.append(f
.LocalPath())
976 elif input_api
.re
.search(r
"\bfprintf\((stdout|stderr)", contents
):
977 printf
.append(f
.LocalPath())
980 return [output_api
.PresubmitError(
981 'These files spam the console log with LOG(INFO):',
984 return [output_api
.PresubmitError(
985 'These files spam the console log with printf/fprintf:',
990 def _CheckForAnonymousVariables(input_api
, output_api
):
991 """These types are all expected to hold locks while in scope and
992 so should never be anonymous (which causes them to be immediately
994 they_who_must_be_named
= [
998 'SkAutoAlphaRestore',
999 'SkAutoBitmapShaderInstall',
1000 'SkAutoBlitterChoose',
1001 'SkAutoBounderCommit',
1003 'SkAutoCanvasRestore',
1004 'SkAutoCommentBlock',
1006 'SkAutoDisableDirectionCheck',
1007 'SkAutoDisableOvalCheck',
1014 'SkAutoMaskFreeImage',
1015 'SkAutoMutexAcquire',
1016 'SkAutoPathBoundsUpdate',
1018 'SkAutoRasterClipValidate',
1024 anonymous
= r
'(%s)\s*[({]' % '|'.join(they_who_must_be_named
)
1025 # bad: base::AutoLock(lock.get());
1026 # not bad: base::AutoLock lock(lock.get());
1027 bad_pattern
= input_api
.re
.compile(anonymous
)
1028 # good: new base::AutoLock(lock.get())
1029 good_pattern
= input_api
.re
.compile(r
'\bnew\s*' + anonymous
)
1032 for f
in input_api
.AffectedFiles():
1033 if not f
.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1035 for linenum
, line
in f
.ChangedContents():
1036 if bad_pattern
.search(line
) and not good_pattern
.search(line
):
1037 errors
.append('%s:%d' % (f
.LocalPath(), linenum
))
1040 return [output_api
.PresubmitError(
1041 'These lines create anonymous variables that need to be named:',
1046 def _CheckCygwinShell(input_api
, output_api
):
1047 source_file_filter
= lambda x
: input_api
.FilterSourceFile(
1048 x
, white_list
=(r
'.+\.(gyp|gypi)$',))
1051 for f
in input_api
.AffectedSourceFiles(source_file_filter
):
1052 for linenum
, line
in f
.ChangedContents():
1053 if 'msvs_cygwin_shell' in line
:
1054 cygwin_shell
.append(f
.LocalPath())
1058 return [output_api
.PresubmitError(
1059 'These files should not use msvs_cygwin_shell (the default is 0):',
1060 items
=cygwin_shell
)]
1064 def _CheckUserActionUpdate(input_api
, output_api
):
1065 """Checks if any new user action has been added."""
1066 if any('actions.xml' == input_api
.os_path
.basename(f
) for f
in
1067 input_api
.LocalPaths()):
1068 # If actions.xml is already included in the changelist, the PRESUBMIT
1069 # for actions.xml will do a more complete presubmit check.
1072 file_filter
= lambda f
: f
.LocalPath().endswith(('.cc', '.mm'))
1073 action_re
= r
'[^a-zA-Z]UserMetricsAction\("([^"]*)'
1074 current_actions
= None
1075 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
1076 for line_num
, line
in f
.ChangedContents():
1077 match
= input_api
.re
.search(action_re
, line
)
1079 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1081 if not current_actions
:
1082 with
open('tools/metrics/actions/actions.xml') as actions_f
:
1083 current_actions
= actions_f
.read()
1084 # Search for the matched user action name in |current_actions|.
1085 for action_name
in match
.groups():
1086 action
= 'name="{0}"'.format(action_name
)
1087 if action
not in current_actions
:
1088 return [output_api
.PresubmitPromptWarning(
1089 'File %s line %d: %s is missing in '
1090 'tools/metrics/actions/actions.xml. Please run '
1091 'tools/metrics/actions/extract_actions.py to update.'
1092 % (f
.LocalPath(), line_num
, action_name
))]
1096 def _GetJSONParseError(input_api
, filename
, eat_comments
=True):
1098 contents
= input_api
.ReadFile(filename
)
1100 json_comment_eater
= input_api
.os_path
.join(
1101 input_api
.PresubmitLocalPath(),
1102 'tools', 'json_comment_eater', 'json_comment_eater.py')
1103 process
= input_api
.subprocess
.Popen(
1104 [input_api
.python_executable
, json_comment_eater
],
1105 stdin
=input_api
.subprocess
.PIPE
,
1106 stdout
=input_api
.subprocess
.PIPE
,
1107 universal_newlines
=True)
1108 (contents
, _
) = process
.communicate(input=contents
)
1110 input_api
.json
.loads(contents
)
1111 except ValueError as e
:
1116 def _GetIDLParseError(input_api
, filename
):
1118 contents
= input_api
.ReadFile(filename
)
1119 idl_schema
= input_api
.os_path
.join(
1120 input_api
.PresubmitLocalPath(),
1121 'tools', 'json_schema_compiler', 'idl_schema.py')
1122 process
= input_api
.subprocess
.Popen(
1123 [input_api
.python_executable
, idl_schema
],
1124 stdin
=input_api
.subprocess
.PIPE
,
1125 stdout
=input_api
.subprocess
.PIPE
,
1126 stderr
=input_api
.subprocess
.PIPE
,
1127 universal_newlines
=True)
1128 (_
, error
) = process
.communicate(input=contents
)
1129 return error
or None
1130 except ValueError as e
:
1134 def _CheckParseErrors(input_api
, output_api
):
1135 """Check that IDL and JSON files do not contain syntax errors."""
1137 '.idl': _GetIDLParseError
,
1138 '.json': _GetJSONParseError
,
1140 # These paths contain test data and other known invalid JSON files.
1141 excluded_patterns
= [
1142 r
'test[\\\/]data[\\\/]',
1143 r
'^components[\\\/]policy[\\\/]resources[\\\/]policy_templates\.json$',
1145 # Most JSON files are preprocessed and support comments, but these do not.
1146 json_no_comments_patterns
= [
1149 # Only run IDL checker on files in these directories.
1150 idl_included_patterns
= [
1151 r
'^chrome[\\\/]common[\\\/]extensions[\\\/]api[\\\/]',
1152 r
'^extensions[\\\/]common[\\\/]api[\\\/]',
1155 def get_action(affected_file
):
1156 filename
= affected_file
.LocalPath()
1157 return actions
.get(input_api
.os_path
.splitext(filename
)[1])
1159 def MatchesFile(patterns
, path
):
1160 for pattern
in patterns
:
1161 if input_api
.re
.search(pattern
, path
):
1165 def FilterFile(affected_file
):
1166 action
= get_action(affected_file
)
1169 path
= affected_file
.LocalPath()
1171 if MatchesFile(excluded_patterns
, path
):
1174 if (action
== _GetIDLParseError
and
1175 not MatchesFile(idl_included_patterns
, path
)):
1180 for affected_file
in input_api
.AffectedFiles(
1181 file_filter
=FilterFile
, include_deletes
=False):
1182 action
= get_action(affected_file
)
1184 if (action
== _GetJSONParseError
and
1185 MatchesFile(json_no_comments_patterns
, affected_file
.LocalPath())):
1186 kwargs
['eat_comments'] = False
1187 parse_error
= action(input_api
,
1188 affected_file
.AbsoluteLocalPath(),
1191 results
.append(output_api
.PresubmitError('%s could not be parsed: %s' %
1192 (affected_file
.LocalPath(), parse_error
)))
1196 def _CheckJavaStyle(input_api
, output_api
):
1197 """Runs checkstyle on changed java files and returns errors if any exist."""
1199 original_sys_path
= sys
.path
1201 sys
.path
= sys
.path
+ [input_api
.os_path
.join(
1202 input_api
.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1205 # Restore sys.path to what it was before.
1206 sys
.path
= original_sys_path
1208 return checkstyle
.RunCheckstyle(
1209 input_api
, output_api
, 'tools/android/checkstyle/chromium-style-5.0.xml')
1214 ( "-webkit-box", "flex" ),
1215 ( "-webkit-inline-box", "inline-flex" ),
1216 ( "-webkit-flex", "flex" ),
1217 ( "-webkit-inline-flex", "inline-flex" ),
1218 ( "-webkit-min-content", "min-content" ),
1219 ( "-webkit-max-content", "max-content" ),
1222 ( "-webkit-background-clip", "background-clip" ),
1223 ( "-webkit-background-origin", "background-origin" ),
1224 ( "-webkit-background-size", "background-size" ),
1225 ( "-webkit-box-shadow", "box-shadow" ),
1228 ( "-webkit-gradient", "gradient" ),
1229 ( "-webkit-repeating-gradient", "repeating-gradient" ),
1230 ( "-webkit-linear-gradient", "linear-gradient" ),
1231 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
1232 ( "-webkit-radial-gradient", "radial-gradient" ),
1233 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
1236 def _CheckNoDeprecatedCSS(input_api
, output_api
):
1237 """ Make sure that we don't use deprecated CSS
1238 properties, functions or values. Our external
1239 documentation is ignored by the hooks as it
1240 needs to be consumed by WebKit. """
1242 file_inclusion_pattern
= (r
".+\.css$",)
1243 black_list
= (_EXCLUDED_PATHS
+
1244 _TEST_CODE_EXCLUDED_PATHS
+
1245 input_api
.DEFAULT_BLACK_LIST
+
1246 (r
"^chrome/common/extensions/docs",
1248 r
"^native_client_sdk"))
1249 file_filter
= lambda f
: input_api
.FilterSourceFile(
1250 f
, white_list
=file_inclusion_pattern
, black_list
=black_list
)
1251 for fpath
in input_api
.AffectedFiles(file_filter
=file_filter
):
1252 for line_num
, line
in fpath
.ChangedContents():
1253 for (deprecated_value
, value
) in _DEPRECATED_CSS
:
1254 if deprecated_value
in line
:
1255 results
.append(output_api
.PresubmitError(
1256 "%s:%d: Use of deprecated CSS %s, use %s instead" %
1257 (fpath
.LocalPath(), line_num
, deprecated_value
, value
)))
1262 ( "__lookupGetter__", "Object.getOwnPropertyDescriptor" ),
1263 ( "__defineGetter__", "Object.defineProperty" ),
1264 ( "__defineSetter__", "Object.defineProperty" ),
1267 def _CheckNoDeprecatedJS(input_api
, output_api
):
1268 """Make sure that we don't use deprecated JS in Chrome code."""
1270 file_inclusion_pattern
= (r
".+\.js$",) # TODO(dbeam): .html?
1271 black_list
= (_EXCLUDED_PATHS
+ _TEST_CODE_EXCLUDED_PATHS
+
1272 input_api
.DEFAULT_BLACK_LIST
)
1273 file_filter
= lambda f
: input_api
.FilterSourceFile(
1274 f
, white_list
=file_inclusion_pattern
, black_list
=black_list
)
1275 for fpath
in input_api
.AffectedFiles(file_filter
=file_filter
):
1276 for lnum
, line
in fpath
.ChangedContents():
1277 for (deprecated
, replacement
) in _DEPRECATED_JS
:
1278 if deprecated
in line
:
1279 results
.append(output_api
.PresubmitError(
1280 "%s:%d: Use of deprecated JS %s, use %s instead" %
1281 (fpath
.LocalPath(), lnum
, deprecated
, replacement
)))
1285 def _CommonChecks(input_api
, output_api
):
1286 """Checks common to both upload and commit."""
1288 results
.extend(input_api
.canned_checks
.PanProjectChecks(
1289 input_api
, output_api
,
1290 excluded_paths
=_EXCLUDED_PATHS
+ _TESTRUNNER_PATHS
))
1291 results
.extend(_CheckAuthorizedAuthor(input_api
, output_api
))
1293 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
))
1294 results
.extend(_CheckNoIOStreamInHeaders(input_api
, output_api
))
1295 results
.extend(_CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
))
1296 results
.extend(_CheckNoNewWStrings(input_api
, output_api
))
1297 results
.extend(_CheckNoDEPSGIT(input_api
, output_api
))
1298 results
.extend(_CheckNoBannedFunctions(input_api
, output_api
))
1299 results
.extend(_CheckNoPragmaOnce(input_api
, output_api
))
1300 results
.extend(_CheckNoTrinaryTrueFalse(input_api
, output_api
))
1301 results
.extend(_CheckUnwantedDependencies(input_api
, output_api
))
1302 results
.extend(_CheckFilePermissions(input_api
, output_api
))
1303 results
.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api
, output_api
))
1304 results
.extend(_CheckIncludeOrder(input_api
, output_api
))
1305 results
.extend(_CheckForVersionControlConflicts(input_api
, output_api
))
1306 results
.extend(_CheckPatchFiles(input_api
, output_api
))
1307 results
.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api
, output_api
))
1308 results
.extend(_CheckNoAbbreviationInPngFileName(input_api
, output_api
))
1309 results
.extend(_CheckForInvalidOSMacros(input_api
, output_api
))
1310 results
.extend(_CheckForInvalidIfDefinedMacros(input_api
, output_api
))
1311 # TODO(danakj): Remove this when base/move.h is removed.
1312 results
.extend(_CheckForUsingSideEffectsOfPass(input_api
, output_api
))
1313 results
.extend(_CheckAddedDepsHaveTargetApprovals(input_api
, output_api
))
1315 input_api
.canned_checks
.CheckChangeHasNoTabs(
1318 source_file_filter
=lambda x
: x
.LocalPath().endswith('.grd')))
1319 results
.extend(_CheckSpamLogging(input_api
, output_api
))
1320 results
.extend(_CheckForAnonymousVariables(input_api
, output_api
))
1321 results
.extend(_CheckCygwinShell(input_api
, output_api
))
1322 results
.extend(_CheckUserActionUpdate(input_api
, output_api
))
1323 results
.extend(_CheckNoDeprecatedCSS(input_api
, output_api
))
1324 results
.extend(_CheckNoDeprecatedJS(input_api
, output_api
))
1325 results
.extend(_CheckParseErrors(input_api
, output_api
))
1326 results
.extend(_CheckForIPCRules(input_api
, output_api
))
1328 if any('PRESUBMIT.py' == f
.LocalPath() for f
in input_api
.AffectedFiles()):
1329 results
.extend(input_api
.canned_checks
.RunUnitTestsInDirectory(
1330 input_api
, output_api
,
1331 input_api
.PresubmitLocalPath(),
1332 whitelist
=[r
'^PRESUBMIT_test\.py$']))
1336 def _CheckAuthorizedAuthor(input_api
, output_api
):
1337 """For non-googler/chromites committers, verify the author's email address is
1340 # TODO(maruel): Add it to input_api?
1343 author
= input_api
.change
.author_email
1345 input_api
.logging
.info('No author, skipping AUTHOR check')
1347 authors_path
= input_api
.os_path
.join(
1348 input_api
.PresubmitLocalPath(), 'AUTHORS')
1350 input_api
.re
.match(r
'[^#]+\s+\<(.+?)\>\s*$', line
)
1351 for line
in open(authors_path
))
1352 valid_authors
= [item
.group(1).lower() for item
in valid_authors
if item
]
1353 if not any(fnmatch
.fnmatch(author
.lower(), valid
) for valid
in valid_authors
):
1354 input_api
.logging
.info('Valid authors are %s', ', '.join(valid_authors
))
1355 return [output_api
.PresubmitPromptWarning(
1356 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
1358 'http://www.chromium.org/developers/contributing-code and read the '
1360 'If you are a chromite, verify the contributor signed the CLA.') %
1365 def _CheckPatchFiles(input_api
, output_api
):
1366 problems
= [f
.LocalPath() for f
in input_api
.AffectedFiles()
1367 if f
.LocalPath().endswith(('.orig', '.rej'))]
1369 return [output_api
.PresubmitError(
1370 "Don't commit .rej and .orig files.", problems
)]
1375 def _DidYouMeanOSMacro(bad_macro
):
1377 return {'A': 'OS_ANDROID',
1387 'W': 'OS_WIN'}[bad_macro
[3].upper()]
1392 def _CheckForInvalidOSMacrosInFile(input_api
, f
):
1393 """Check for sensible looking, totally invalid OS macros."""
1394 preprocessor_statement
= input_api
.re
.compile(r
'^\s*#')
1395 os_macro
= input_api
.re
.compile(r
'defined\((OS_[^)]+)\)')
1397 for lnum
, line
in f
.ChangedContents():
1398 if preprocessor_statement
.search(line
):
1399 for match
in os_macro
.finditer(line
):
1400 if not match
.group(1) in _VALID_OS_MACROS
:
1401 good
= _DidYouMeanOSMacro(match
.group(1))
1402 did_you_mean
= ' (did you mean %s?)' % good
if good
else ''
1403 results
.append(' %s:%d %s%s' % (f
.LocalPath(),
1410 def _CheckForInvalidOSMacros(input_api
, output_api
):
1411 """Check all affected files for invalid OS macros."""
1413 for f
in input_api
.AffectedFiles():
1414 if not f
.LocalPath().endswith(('.py', '.js', '.html', '.css')):
1415 bad_macros
.extend(_CheckForInvalidOSMacrosInFile(input_api
, f
))
1420 return [output_api
.PresubmitError(
1421 'Possibly invalid OS macro[s] found. Please fix your code\n'
1422 'or add your macro to src/PRESUBMIT.py.', bad_macros
)]
1425 def _CheckForInvalidIfDefinedMacrosInFile(input_api
, f
):
1426 """Check all affected files for invalid "if defined" macros."""
1427 ALWAYS_DEFINED_MACROS
= (
1436 "TARGET_IPHONE_SIMULATOR",
1437 "TARGET_OS_EMBEDDED",
1443 ifdef_macro
= input_api
.re
.compile(r
'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
1445 for lnum
, line
in f
.ChangedContents():
1446 for match
in ifdef_macro
.finditer(line
):
1447 if match
.group(1) in ALWAYS_DEFINED_MACROS
:
1448 always_defined
= ' %s is always defined. ' % match
.group(1)
1449 did_you_mean
= 'Did you mean \'#if %s\'?' % match
.group(1)
1450 results
.append(' %s:%d %s\n\t%s' % (f
.LocalPath(),
1457 def _CheckForInvalidIfDefinedMacros(input_api
, output_api
):
1458 """Check all affected files for invalid "if defined" macros."""
1460 for f
in input_api
.AffectedFiles():
1461 if f
.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
1462 bad_macros
.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api
, f
))
1467 return [output_api
.PresubmitError(
1468 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
1469 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
1473 def _CheckForUsingSideEffectsOfPass(input_api
, output_api
):
1474 """Check all affected files for using side effects of Pass."""
1476 for f
in input_api
.AffectedFiles():
1477 if f
.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
1478 for lnum
, line
in f
.ChangedContents():
1479 # Disallow Foo(*my_scoped_thing.Pass()); See crbug.com/418297.
1480 if input_api
.re
.search(r
'\*[a-zA-Z0-9_]+\.Pass\(\)', line
):
1481 errors
.append(output_api
.PresubmitError(
1482 ('%s:%d uses *foo.Pass() to delete the contents of scoped_ptr. ' +
1483 'See crbug.com/418297.') % (f
.LocalPath(), lnum
)))
1487 def _CheckForIPCRules(input_api
, output_api
):
1488 """Check for same IPC rules described in
1489 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
1491 base_pattern
= r
'IPC_ENUM_TRAITS\('
1492 inclusion_pattern
= input_api
.re
.compile(r
'(%s)' % base_pattern
)
1493 comment_pattern
= input_api
.re
.compile(r
'//.*(%s)' % base_pattern
)
1496 for f
in input_api
.AffectedSourceFiles(None):
1497 local_path
= f
.LocalPath()
1498 if not local_path
.endswith('.h'):
1500 for line_number
, line
in f
.ChangedContents():
1501 if inclusion_pattern
.search(line
) and not comment_pattern
.search(line
):
1503 '%s:%d\n %s' % (local_path
, line_number
, line
.strip()))
1506 return [output_api
.PresubmitPromptWarning(
1507 _IPC_ENUM_TRAITS_DEPRECATED
, problems
)]
1512 def CheckChangeOnUpload(input_api
, output_api
):
1514 results
.extend(_CommonChecks(input_api
, output_api
))
1515 results
.extend(_CheckValidHostsInDEPS(input_api
, output_api
))
1516 results
.extend(_CheckJavaStyle(input_api
, output_api
))
1520 def GetTryServerMasterForBot(bot
):
1521 """Returns the Try Server master for the given bot.
1523 It tries to guess the master from the bot name, but may still fail
1524 and return None. There is no longer a default master.
1526 # Potentially ambiguous bot names are listed explicitly.
1528 'linux_gpu': 'tryserver.chromium.gpu',
1529 'mac_gpu': 'tryserver.chromium.gpu',
1530 'win_gpu': 'tryserver.chromium.gpu',
1531 'chromium_presubmit': 'tryserver.chromium.linux',
1532 'blink_presubmit': 'tryserver.chromium.linux',
1533 'tools_build_presubmit': 'tryserver.chromium.linux',
1535 master
= master_map
.get(bot
)
1538 master
= 'tryserver.chromium.gpu'
1539 elif 'linux' in bot
or 'android' in bot
or 'presubmit' in bot
:
1540 master
= 'tryserver.chromium.linux'
1542 master
= 'tryserver.chromium.win'
1543 elif 'mac' in bot
or 'ios' in bot
:
1544 master
= 'tryserver.chromium.mac'
1548 def GetDefaultTryConfigs(bots
=None):
1549 """Returns a list of ('bot', set(['tests']), optionally filtered by [bots].
1551 To add tests to this list, they MUST be in the the corresponding master's
1552 gatekeeper config. For example, anything on master.chromium would be closed by
1553 tools/build/masters/master.chromium/master_gatekeeper_cfg.py.
1555 If 'bots' is specified, will only return configurations for bots in that list.
1561 'cacheinvalidation_unittests',
1564 'content_browsertests',
1565 'content_unittests',
1568 'interactive_ui_tests',
1574 'printing_unittests',
1578 # Broken in release.
1580 #'webkit_unit_tests',
1583 builders_and_tests
= {
1584 # TODO(maruel): Figure out a way to run 'sizes' where people can
1585 # effectively update the perf expectation correctly. This requires a
1586 # clobber=True build running 'sizes'. 'sizes' is not accurate with
1587 # incremental build. Reference:
1588 # http://chromium.org/developers/tree-sheriffs/perf-sheriffs.
1589 # TODO(maruel): An option would be to run 'sizes' but not count a failure
1590 # of this step as a try job failure.
1591 'android_aosp': ['compile'],
1592 'android_arm64_dbg_recipe': ['slave_steps'],
1593 'android_chromium_gn_compile_dbg': ['compile'],
1594 'android_chromium_gn_compile_rel': ['compile'],
1595 'android_clang_dbg': ['slave_steps'],
1596 'android_clang_dbg_recipe': ['slave_steps'],
1597 'android_dbg_tests_recipe': ['slave_steps'],
1598 'cros_x86': ['defaulttests'],
1599 'ios_dbg_simulator': [
1602 'content_unittests',
1607 'ui_base_unittests',
1610 'ios_rel_device': ['compile'],
1611 'ios_rel_device_ninja': ['compile'],
1612 'mac_asan': ['compile'],
1613 #TODO(stip): Change the name of this builder to reflect that it's release.
1614 'linux_gtk': standard_tests
,
1615 'linux_chromeos_asan': ['compile'],
1616 'linux_chromium_chromeos_clang_dbg': ['defaulttests'],
1617 'linux_chromium_chromeos_rel': ['defaulttests'],
1618 'linux_chromium_compile_dbg': ['defaulttests'],
1619 'linux_chromium_gn_dbg': ['compile'],
1620 'linux_chromium_gn_rel': ['defaulttests'],
1621 'linux_chromium_rel': ['defaulttests'],
1622 'linux_chromium_clang_dbg': ['defaulttests'],
1623 'linux_gpu': ['defaulttests'],
1624 'linux_nacl_sdk_build': ['compile'],
1625 'mac_chromium_compile_dbg': ['defaulttests'],
1626 'mac_chromium_rel': ['defaulttests'],
1627 'mac_gpu': ['defaulttests'],
1628 'mac_nacl_sdk_build': ['compile'],
1629 'win_chromium_compile_dbg': ['defaulttests'],
1630 'win_chromium_dbg': ['defaulttests'],
1631 'win_chromium_rel': ['defaulttests'],
1632 'win_chromium_x64_rel': ['defaulttests'],
1633 'win_gpu': ['defaulttests'],
1634 'win_nacl_sdk_build': ['compile'],
1635 'win8_chromium_rel': ['defaulttests'],
1639 filtered_builders_and_tests
= dict((bot
, set(builders_and_tests
[bot
]))
1642 filtered_builders_and_tests
= dict(
1644 for bot
, tests
in builders_and_tests
.iteritems())
1646 # Build up the mapping from tryserver master to bot/test.
1648 for bot
, tests
in filtered_builders_and_tests
.iteritems():
1649 out
.setdefault(GetTryServerMasterForBot(bot
), {})[bot
] = tests
1653 def CheckChangeOnCommit(input_api
, output_api
):
1655 results
.extend(_CommonChecks(input_api
, output_api
))
1656 # TODO(thestig) temporarily disabled, doesn't work in third_party/
1657 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
1658 # input_api, output_api, sources))
1659 # Make sure the tree is 'open'.
1660 results
.extend(input_api
.canned_checks
.CheckTreeIsOpen(
1663 json_url
='http://chromium-status.appspot.com/current?format=json'))
1665 results
.extend(input_api
.canned_checks
.CheckChangeHasBugField(
1666 input_api
, output_api
))
1667 results
.extend(input_api
.canned_checks
.CheckChangeHasDescription(
1668 input_api
, output_api
))
1672 def GetPreferredTryMasters(project
, change
):
1674 files
= change
.LocalPaths()
1676 if not files
or all(re
.search(r
'[\\\/]OWNERS$', f
) for f
in files
):
1679 if all(re
.search(r
'\.(m|mm)$|(^|[\\\/_])mac[\\\/_.]', f
) for f
in files
):
1680 return GetDefaultTryConfigs([
1681 'mac_chromium_compile_dbg',
1684 if all(re
.search('(^|[/_])win[/_.]', f
) for f
in files
):
1685 return GetDefaultTryConfigs([
1688 'win8_chromium_rel',
1690 if all(re
.search(r
'(^|[\\\/_])android[\\\/_.]', f
) for f
in files
):
1691 return GetDefaultTryConfigs([
1693 'android_clang_dbg',
1694 'android_dbg_tests_recipe',
1696 if all(re
.search(r
'[\\\/_]ios[\\\/_.]', f
) for f
in files
):
1697 return GetDefaultTryConfigs(['ios_rel_device', 'ios_dbg_simulator'])
1700 'android_arm64_dbg_recipe',
1701 'android_chromium_gn_compile_rel',
1702 'android_chromium_gn_compile_dbg',
1703 'android_clang_dbg',
1704 'android_clang_dbg_recipe',
1705 'android_dbg_tests_recipe',
1706 'ios_dbg_simulator',
1708 'ios_rel_device_ninja',
1709 'linux_chromium_chromeos_rel',
1710 'linux_chromium_clang_dbg',
1711 'linux_chromium_gn_dbg',
1712 'linux_chromium_gn_rel',
1713 'linux_chromium_rel',
1715 'mac_chromium_compile_dbg',
1718 'win_chromium_compile_dbg',
1720 'win_chromium_x64_rel',
1722 'win8_chromium_rel',
1725 # Match things like path/aura/file.cc and path/file_aura.cc.
1726 # Same for chromeos.
1727 if any(re
.search(r
'[\\\/_](aura|chromeos)', f
) for f
in files
):
1729 'linux_chromeos_asan',
1730 'linux_chromium_chromeos_clang_dbg'
1733 # If there are gyp changes to base, build, or chromeos, run a full cros build
1734 # in addition to the shorter linux_chromeos build. Changes to high level gyp
1735 # files have a much higher chance of breaking the cros build, which is
1736 # differnt from the linux_chromeos build that most chrome developers test
1738 if any(re
.search('^(base|build|chromeos).*\.gypi?$', f
) for f
in files
):
1739 builders
.extend(['cros_x86'])
1741 # The AOSP bot doesn't build the chrome/ layer, so ignore any changes to it
1742 # unless they're .gyp(i) files as changes to those files can break the gyp
1744 if (not all(re
.search('^chrome', f
) for f
in files
) or
1745 any(re
.search('\.gypi?$', f
) for f
in files
)):
1746 builders
.extend(['android_aosp'])
1748 return GetDefaultTryConfigs(builders
)