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
"^native_client_sdk[\\\/]src[\\\/]tools[\\\/].*.mk",
22 r
"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
27 r
".+[\\\/]pnacl_shim\.c$",
30 # Fragment of a regular expression that matches C++ and Objective-C++
31 # implementation files.
32 _IMPLEMENTATION_EXTENSIONS
= r
'\.(cc|cpp|cxx|mm)$'
34 # Regular expression that matches code only used for test binaries
36 _TEST_CODE_EXCLUDED_PATHS
= (
37 r
'.*[/\\](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS
,
38 r
'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS
,
39 r
'.+_(api|browser|perf|pixel|unit|ui)?test(_[a-z]+)?%s' %
40 _IMPLEMENTATION_EXTENSIONS
,
41 r
'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS
,
42 r
'.*[/\\](test|tool(s)?)[/\\].*',
43 # content_shell is used for running layout tests.
44 r
'content[/\\]shell[/\\].*',
45 # At request of folks maintaining this folder.
46 r
'chrome[/\\]browser[/\\]automation[/\\].*',
49 _TEST_ONLY_WARNING
= (
50 'You might be calling functions intended only for testing from\n'
51 'production code. It is OK to ignore this warning if you know what\n'
52 'you are doing, as the heuristics used to detect the situation are\n'
53 'not perfect. The commit queue will not block on this warning.\n'
54 'Email joi@chromium.org if you have questions.')
57 _INCLUDE_ORDER_WARNING
= (
58 'Your #include order seems to be broken. Send mail to\n'
59 'marja@chromium.org if this is not the case.')
62 _BANNED_OBJC_FUNCTIONS
= (
66 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
67 'prohibited. Please use CrTrackingArea instead.',
68 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
75 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
77 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
82 'convertPointFromBase:',
84 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
85 'Please use |convertPoint:(point) fromView:nil| instead.',
86 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
91 'convertPointToBase:',
93 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
94 'Please use |convertPoint:(point) toView:nil| instead.',
95 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
100 'convertRectFromBase:',
102 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
103 'Please use |convertRect:(point) fromView:nil| instead.',
104 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
109 'convertRectToBase:',
111 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
112 'Please use |convertRect:(point) toView:nil| instead.',
113 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
118 'convertSizeFromBase:',
120 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
121 'Please use |convertSize:(point) fromView:nil| instead.',
122 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
127 'convertSizeToBase:',
129 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
130 'Please use |convertSize:(point) toView:nil| instead.',
131 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
138 _BANNED_CPP_FUNCTIONS
= (
139 # Make sure that gtest's FRIEND_TEST() macro is not used; the
140 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
141 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
145 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
146 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
154 'New code should not use ScopedAllowIO. Post a task to the blocking',
155 'pool or the FILE thread instead.',
159 r
"^content[\\\/]shell[\\\/]shell_browser_main\.cc$",
160 r
"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
166 'The use of SkRefPtr is prohibited. ',
167 'Please use skia::RefPtr instead.'
175 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
176 'Please use skia::RefPtr instead.'
184 'The use of SkAutoTUnref is dangerous because it implicitly ',
185 'converts to a raw pointer. Please use skia::RefPtr instead.'
193 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
194 'because it implicitly converts to a raw pointer. ',
195 'Please use skia::RefPtr instead.'
204 # Please keep sorted.
207 'OS_CAT', # For testing.
221 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
):
222 """Attempts to prevent use of functions intended only for testing in
223 non-testing code. For now this is just a best-effort implementation
224 that ignores header files and may have some false positives. A
225 better implementation would probably need a proper C++ parser.
227 # We only scan .cc files and the like, as the declaration of
228 # for-testing functions in header files are hard to distinguish from
229 # calls to such functions without a proper C++ parser.
230 file_inclusion_pattern
= r
'.+%s' % _IMPLEMENTATION_EXTENSIONS
232 base_function_pattern
= r
'ForTest(ing)?|for_test(ing)?'
233 inclusion_pattern
= input_api
.re
.compile(r
'(%s)\s*\(' % base_function_pattern
)
234 exclusion_pattern
= input_api
.re
.compile(
235 r
'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
236 base_function_pattern
, base_function_pattern
))
238 def FilterFile(affected_file
):
239 black_list
= (_EXCLUDED_PATHS
+
240 _TEST_CODE_EXCLUDED_PATHS
+
241 input_api
.DEFAULT_BLACK_LIST
)
242 return input_api
.FilterSourceFile(
244 white_list
=(file_inclusion_pattern
, ),
245 black_list
=black_list
)
248 for f
in input_api
.AffectedSourceFiles(FilterFile
):
249 local_path
= f
.LocalPath()
250 lines
= input_api
.ReadFile(f
).splitlines()
253 if (inclusion_pattern
.search(line
) and
254 not exclusion_pattern
.search(line
)):
256 '%s:%d\n %s' % (local_path
, line_number
, line
.strip()))
260 return [output_api
.PresubmitPromptOrNotify(_TEST_ONLY_WARNING
, problems
)]
265 def _CheckNoIOStreamInHeaders(input_api
, output_api
):
266 """Checks to make sure no .h files include <iostream>."""
268 pattern
= input_api
.re
.compile(r
'^#include\s*<iostream>',
269 input_api
.re
.MULTILINE
)
270 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
271 if not f
.LocalPath().endswith('.h'):
273 contents
= input_api
.ReadFile(f
)
274 if pattern
.search(contents
):
278 return [ output_api
.PresubmitError(
279 'Do not #include <iostream> in header files, since it inserts static '
280 'initialization into every file including the header. Instead, '
281 '#include <ostream>. See http://crbug.com/94794',
286 def _CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
):
287 """Checks to make sure no source files use UNIT_TEST"""
289 for f
in input_api
.AffectedFiles():
290 if (not f
.LocalPath().endswith(('.cc', '.mm'))):
293 for line_num
, line
in f
.ChangedContents():
294 if 'UNIT_TEST' in line
:
295 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
299 return [output_api
.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
300 '\n'.join(problems
))]
303 def _CheckNoNewWStrings(input_api
, output_api
):
304 """Checks to make sure we don't introduce use of wstrings."""
306 for f
in input_api
.AffectedFiles():
307 if (not f
.LocalPath().endswith(('.cc', '.h')) or
308 f
.LocalPath().endswith('test.cc')):
312 for line_num
, line
in f
.ChangedContents():
313 if 'presubmit: allow wstring' in line
:
315 elif not allowWString
and 'wstring' in line
:
316 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
323 return [output_api
.PresubmitPromptWarning('New code should not use wstrings.'
324 ' If you are calling a cross-platform API that accepts a wstring, '
326 '\n'.join(problems
))]
329 def _CheckNoDEPSGIT(input_api
, output_api
):
330 """Make sure .DEPS.git is never modified manually."""
331 if any(f
.LocalPath().endswith('.DEPS.git') for f
in
332 input_api
.AffectedFiles()):
333 return [output_api
.PresubmitError(
334 'Never commit changes to .DEPS.git. This file is maintained by an\n'
335 'automated system based on what\'s in DEPS and your changes will be\n'
337 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
338 'for more information')]
342 def _CheckNoBannedFunctions(input_api
, output_api
):
343 """Make sure that banned functions are not used."""
347 file_filter
= lambda f
: f
.LocalPath().endswith(('.mm', '.m', '.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_OBJC_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
)
359 file_filter
= lambda f
: f
.LocalPath().endswith(('.cc', '.mm', '.h'))
360 for f
in input_api
.AffectedFiles(file_filter
=file_filter
):
361 for line_num
, line
in f
.ChangedContents():
362 for func_name
, message
, error
, excluded_paths
in _BANNED_CPP_FUNCTIONS
:
363 def IsBlacklisted(affected_file
, blacklist
):
364 local_path
= affected_file
.LocalPath()
365 for item
in blacklist
:
366 if input_api
.re
.match(item
, local_path
):
369 if IsBlacklisted(f
, excluded_paths
):
371 if func_name
in line
:
375 problems
.append(' %s:%d:' % (f
.LocalPath(), line_num
))
376 for message_line
in message
:
377 problems
.append(' %s' % message_line
)
381 result
.append(output_api
.PresubmitPromptWarning(
382 'Banned functions were used.\n' + '\n'.join(warnings
)))
384 result
.append(output_api
.PresubmitError(
385 'Banned functions were used.\n' + '\n'.join(errors
)))
389 def _CheckNoPragmaOnce(input_api
, output_api
):
390 """Make sure that banned functions are not used."""
392 pattern
= input_api
.re
.compile(r
'^#pragma\s+once',
393 input_api
.re
.MULTILINE
)
394 for f
in input_api
.AffectedSourceFiles(input_api
.FilterSourceFile
):
395 if not f
.LocalPath().endswith('.h'):
397 contents
= input_api
.ReadFile(f
)
398 if pattern
.search(contents
):
402 return [output_api
.PresubmitError(
403 'Do not use #pragma once in header files.\n'
404 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
409 def _CheckNoTrinaryTrueFalse(input_api
, output_api
):
410 """Checks to make sure we don't introduce use of foo ? true : false."""
412 pattern
= input_api
.re
.compile(r
'\?\s*(true|false)\s*:\s*(true|false)')
413 for f
in input_api
.AffectedFiles():
414 if not f
.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
417 for line_num
, line
in f
.ChangedContents():
418 if pattern
.match(line
):
419 problems
.append(' %s:%d' % (f
.LocalPath(), line_num
))
423 return [output_api
.PresubmitPromptWarning(
424 'Please consider avoiding the "? true : false" pattern if possible.\n' +
425 '\n'.join(problems
))]
428 def _CheckUnwantedDependencies(input_api
, output_api
):
429 """Runs checkdeps on #include statements added in this
430 change. Breaking - rules is an error, breaking ! rules is a
433 # We need to wait until we have an input_api object and use this
434 # roundabout construct to import checkdeps because this file is
435 # eval-ed and thus doesn't have __file__.
436 original_sys_path
= sys
.path
438 sys
.path
= sys
.path
+ [input_api
.os_path
.join(
439 input_api
.PresubmitLocalPath(), 'tools', 'checkdeps')]
441 from cpp_checker
import CppChecker
442 from rules
import Rule
444 # Restore sys.path to what it was before.
445 sys
.path
= original_sys_path
448 for f
in input_api
.AffectedFiles():
449 if not CppChecker
.IsCppFile(f
.LocalPath()):
452 changed_lines
= [line
for line_num
, line
in f
.ChangedContents()]
453 added_includes
.append([f
.LocalPath(), changed_lines
])
455 deps_checker
= checkdeps
.DepsChecker(input_api
.PresubmitLocalPath())
457 error_descriptions
= []
458 warning_descriptions
= []
459 for path
, rule_type
, rule_description
in deps_checker
.CheckAddedCppIncludes(
461 description_with_path
= '%s\n %s' % (path
, rule_description
)
462 if rule_type
== Rule
.DISALLOW
:
463 error_descriptions
.append(description_with_path
)
465 warning_descriptions
.append(description_with_path
)
468 if error_descriptions
:
469 results
.append(output_api
.PresubmitError(
470 'You added one or more #includes that violate checkdeps rules.',
472 if warning_descriptions
:
473 results
.append(output_api
.PresubmitPromptOrNotify(
474 'You added one or more #includes of files that are temporarily\n'
475 'allowed but being removed. Can you avoid introducing the\n'
476 '#include? See relevant DEPS file(s) for details and contacts.',
477 warning_descriptions
))
481 def _CheckFilePermissions(input_api
, output_api
):
482 """Check that all files have their permissions properly set."""
483 args
= [sys
.executable
, 'tools/checkperms/checkperms.py', '--root',
484 input_api
.change
.RepositoryRoot()]
485 for f
in input_api
.AffectedFiles():
486 args
+= ['--file', f
.LocalPath()]
488 (errors
, stderrdata
) = subprocess
.Popen(args
).communicate()
492 results
.append(output_api
.PresubmitError('checkperms.py failed.',
497 def _CheckNoAuraWindowPropertyHInHeaders(input_api
, output_api
):
498 """Makes sure we don't include ui/aura/window_property.h
501 pattern
= input_api
.re
.compile(r
'^#include\s*"ui/aura/window_property.h"')
503 for f
in input_api
.AffectedFiles():
504 if not f
.LocalPath().endswith('.h'):
506 for line_num
, line
in f
.ChangedContents():
507 if pattern
.match(line
):
508 errors
.append(' %s:%d' % (f
.LocalPath(), line_num
))
512 results
.append(output_api
.PresubmitError(
513 'Header files should not include ui/aura/window_property.h', errors
))
517 def _CheckIncludeOrderForScope(scope
, input_api
, file_path
, changed_linenums
):
518 """Checks that the lines in scope occur in the right order.
520 1. C system files in alphabetical order
521 2. C++ system files in alphabetical order
522 3. Project's .h files
525 c_system_include_pattern
= input_api
.re
.compile(r
'\s*#include <.*\.h>')
526 cpp_system_include_pattern
= input_api
.re
.compile(r
'\s*#include <.*>')
527 custom_include_pattern
= input_api
.re
.compile(r
'\s*#include ".*')
529 C_SYSTEM_INCLUDES
, CPP_SYSTEM_INCLUDES
, CUSTOM_INCLUDES
= range(3)
531 state
= C_SYSTEM_INCLUDES
534 previous_line_num
= 0
535 problem_linenums
= []
536 for line_num
, line
in scope
:
537 if c_system_include_pattern
.match(line
):
538 if state
!= C_SYSTEM_INCLUDES
:
539 problem_linenums
.append((line_num
, previous_line_num
))
540 elif previous_line
and previous_line
> line
:
541 problem_linenums
.append((line_num
, previous_line_num
))
542 elif cpp_system_include_pattern
.match(line
):
543 if state
== C_SYSTEM_INCLUDES
:
544 state
= CPP_SYSTEM_INCLUDES
545 elif state
== CUSTOM_INCLUDES
:
546 problem_linenums
.append((line_num
, previous_line_num
))
547 elif previous_line
and previous_line
> line
:
548 problem_linenums
.append((line_num
, previous_line_num
))
549 elif custom_include_pattern
.match(line
):
550 if state
!= CUSTOM_INCLUDES
:
551 state
= CUSTOM_INCLUDES
552 elif previous_line
and previous_line
> line
:
553 problem_linenums
.append((line_num
, previous_line_num
))
555 problem_linenums
.append(line_num
)
557 previous_line_num
= line_num
560 for (line_num
, previous_line_num
) in problem_linenums
:
561 if line_num
in changed_linenums
or previous_line_num
in changed_linenums
:
562 warnings
.append(' %s:%d' % (file_path
, line_num
))
566 def _CheckIncludeOrderInFile(input_api
, f
, changed_linenums
):
567 """Checks the #include order for the given file f."""
569 system_include_pattern
= input_api
.re
.compile(r
'\s*#include \<.*')
570 # Exclude #include <.../...> includes from the check; e.g., <sys/...> includes
571 # often need to appear in a specific order.
572 excluded_include_pattern
= input_api
.re
.compile(r
'\s*#include \<.*/.*')
573 custom_include_pattern
= input_api
.re
.compile(r
'\s*#include "(?P<FILE>.*)"')
574 if_pattern
= input_api
.re
.compile(
575 r
'\s*#\s*(if|elif|else|endif|define|undef).*')
576 # Some files need specialized order of includes; exclude such files from this
578 uncheckable_includes_pattern
= input_api
.re
.compile(
580 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
582 contents
= f
.NewContents()
586 # Handle the special first include. If the first include file is
587 # some/path/file.h, the corresponding including file can be some/path/file.cc,
588 # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
589 # etc. It's also possible that no special first include exists.
590 for line
in contents
:
592 if system_include_pattern
.match(line
):
593 # No special first include -> process the line again along with normal
597 match
= custom_include_pattern
.match(line
)
599 match_dict
= match
.groupdict()
600 header_basename
= input_api
.os_path
.basename(
601 match_dict
['FILE']).replace('.h', '')
602 if header_basename
not in input_api
.os_path
.basename(f
.LocalPath()):
603 # No special first include -> process the line again along with normal
608 # Split into scopes: Each region between #if and #endif is its own scope.
611 for line
in contents
[line_num
:]:
613 if uncheckable_includes_pattern
.match(line
):
615 if if_pattern
.match(line
):
616 scopes
.append(current_scope
)
618 elif ((system_include_pattern
.match(line
) or
619 custom_include_pattern
.match(line
)) and
620 not excluded_include_pattern
.match(line
)):
621 current_scope
.append((line_num
, line
))
622 scopes
.append(current_scope
)
625 warnings
.extend(_CheckIncludeOrderForScope(scope
, input_api
, f
.LocalPath(),
630 def _CheckIncludeOrder(input_api
, output_api
):
631 """Checks that the #include order is correct.
633 1. The corresponding header for source files.
634 2. C system files in alphabetical order
635 3. C++ system files in alphabetical order
636 4. Project's .h files in alphabetical order
638 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
639 these rules separately.
643 for f
in input_api
.AffectedFiles():
644 if f
.LocalPath().endswith(('.cc', '.h')):
645 changed_linenums
= set(line_num
for line_num
, _
in f
.ChangedContents())
646 warnings
.extend(_CheckIncludeOrderInFile(input_api
, f
, changed_linenums
))
650 results
.append(output_api
.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING
,
655 def _CheckForVersionControlConflictsInFile(input_api
, f
):
656 pattern
= input_api
.re
.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
658 for line_num
, line
in f
.ChangedContents():
659 if pattern
.match(line
):
660 errors
.append(' %s:%d %s' % (f
.LocalPath(), line_num
, line
))
664 def _CheckForVersionControlConflicts(input_api
, output_api
):
665 """Usually this is not intentional and will cause a compile failure."""
667 for f
in input_api
.AffectedFiles():
668 errors
.extend(_CheckForVersionControlConflictsInFile(input_api
, f
))
672 results
.append(output_api
.PresubmitError(
673 'Version control conflict markers found, please resolve.', errors
))
677 def _CheckHardcodedGoogleHostsInLowerLayers(input_api
, output_api
):
678 def FilterFile(affected_file
):
679 """Filter function for use with input_api.AffectedSourceFiles,
680 below. This filters out everything except non-test files from
681 top-level directories that generally speaking should not hard-code
682 service URLs (e.g. src/android_webview/, src/content/ and others).
684 return input_api
.FilterSourceFile(
686 white_list
=(r
'^(android_webview|base|content|net)[\\\/].*', ),
687 black_list
=(_EXCLUDED_PATHS
+
688 _TEST_CODE_EXCLUDED_PATHS
+
689 input_api
.DEFAULT_BLACK_LIST
))
691 pattern
= input_api
.re
.compile('"[^"]*google\.com[^"]*"')
692 problems
= [] # items are (filename, line_number, line)
693 for f
in input_api
.AffectedSourceFiles(FilterFile
):
694 for line_num
, line
in f
.ChangedContents():
695 if pattern
.search(line
):
696 problems
.append((f
.LocalPath(), line_num
, line
))
699 return [output_api
.PresubmitPromptOrNotify(
700 'Most layers below src/chrome/ should not hardcode service URLs.\n'
701 'Are you sure this is correct? (Contact: joi@chromium.org)',
703 problem
[0], problem
[1], problem
[2]) for problem
in problems
])]
708 def _CheckNoAbbreviationInPngFileName(input_api
, output_api
):
709 """Makes sure there are no abbreviations in the name of PNG files.
711 pattern
= input_api
.re
.compile(r
'.*_[a-z]_.*\.png$|.*_[a-z]\.png$')
713 for f
in input_api
.AffectedFiles(include_deletes
=False):
714 if pattern
.match(f
.LocalPath()):
715 errors
.append(' %s' % f
.LocalPath())
719 results
.append(output_api
.PresubmitError(
720 'The name of PNG files should not have abbreviations. \n'
721 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
722 'Contact oshima@chromium.org if you have questions.', errors
))
726 def _CheckAddedDepsHaveTargetApprovals(input_api
, output_api
):
727 """When a dependency prefixed with + is added to a DEPS file, we
728 want to make sure that the change is reviewed by an OWNER of the
729 target file or directory, to avoid layering violations from being
730 introduced. This check verifies that this happens.
732 changed_lines
= set()
733 for f
in input_api
.AffectedFiles():
734 filename
= input_api
.os_path
.basename(f
.LocalPath())
735 if filename
== 'DEPS':
736 changed_lines |
= set(line
.strip()
738 in f
.ChangedContents())
739 if not changed_lines
:
742 virtual_depended_on_files
= set()
743 # This pattern grabs the path without basename in the first
744 # parentheses, and the basename (if present) in the second. It
745 # relies on the simple heuristic that if there is a basename it will
746 # be a header file ending in ".h".
747 pattern
= input_api
.re
.compile(
748 r
"""['"]\+([^'"]+?)(/[a-zA-Z0-9_]+\.h)?['"].*""")
749 for changed_line
in changed_lines
:
750 m
= pattern
.match(changed_line
)
752 virtual_depended_on_files
.add('%s/DEPS' % m
.group(1))
754 if not virtual_depended_on_files
:
757 if input_api
.is_committing
:
759 return [output_api
.PresubmitNotifyResult(
760 '--tbr was specified, skipping OWNERS check for DEPS additions')]
761 if not input_api
.change
.issue
:
762 return [output_api
.PresubmitError(
763 "DEPS approval by OWNERS check failed: this change has "
764 "no Rietveld issue number, so we can't check it for approvals.")]
765 output
= output_api
.PresubmitError
767 output
= output_api
.PresubmitNotifyResult
769 owners_db
= input_api
.owners_db
770 owner_email
, reviewers
= input_api
.canned_checks
._RietveldOwnerAndReviewers
(
772 owners_db
.email_regexp
,
773 approval_needed
=input_api
.is_committing
)
775 owner_email
= owner_email
or input_api
.change
.author_email
777 reviewers_plus_owner
= set([owner_email
]).union(reviewers
)
778 missing_files
= owners_db
.files_not_covered_by(virtual_depended_on_files
,
779 reviewers_plus_owner
)
780 unapproved_dependencies
= ["'+%s'," % path
[:-len('/DEPS')]
781 for path
in missing_files
]
783 if unapproved_dependencies
:
785 output('Missing LGTM from OWNERS of directories added to DEPS:\n %s' %
786 '\n '.join(sorted(unapproved_dependencies
)))]
787 if not input_api
.is_committing
:
788 suggested_owners
= owners_db
.reviewers_for(missing_files
, owner_email
)
789 output_list
.append(output(
790 'Suggested missing target path OWNERS:\n %s' %
791 '\n '.join(suggested_owners
or [])))
797 def _CommonChecks(input_api
, output_api
):
798 """Checks common to both upload and commit."""
800 results
.extend(input_api
.canned_checks
.PanProjectChecks(
801 input_api
, output_api
, excluded_paths
=_EXCLUDED_PATHS
))
802 results
.extend(_CheckAuthorizedAuthor(input_api
, output_api
))
804 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api
, output_api
))
805 results
.extend(_CheckNoIOStreamInHeaders(input_api
, output_api
))
806 results
.extend(_CheckNoUNIT_TESTInSourceFiles(input_api
, output_api
))
807 results
.extend(_CheckNoNewWStrings(input_api
, output_api
))
808 results
.extend(_CheckNoDEPSGIT(input_api
, output_api
))
809 results
.extend(_CheckNoBannedFunctions(input_api
, output_api
))
810 results
.extend(_CheckNoPragmaOnce(input_api
, output_api
))
811 results
.extend(_CheckNoTrinaryTrueFalse(input_api
, output_api
))
812 results
.extend(_CheckUnwantedDependencies(input_api
, output_api
))
813 results
.extend(_CheckFilePermissions(input_api
, output_api
))
814 results
.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api
, output_api
))
815 results
.extend(_CheckIncludeOrder(input_api
, output_api
))
816 results
.extend(_CheckForVersionControlConflicts(input_api
, output_api
))
817 results
.extend(_CheckPatchFiles(input_api
, output_api
))
818 results
.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api
, output_api
))
819 results
.extend(_CheckNoAbbreviationInPngFileName(input_api
, output_api
))
820 results
.extend(_CheckForInvalidOSMacros(input_api
, output_api
))
821 results
.extend(_CheckAddedDepsHaveTargetApprovals(input_api
, output_api
))
823 if any('PRESUBMIT.py' == f
.LocalPath() for f
in input_api
.AffectedFiles()):
824 results
.extend(input_api
.canned_checks
.RunUnitTestsInDirectory(
825 input_api
, output_api
,
826 input_api
.PresubmitLocalPath(),
827 whitelist
=[r
'^PRESUBMIT_test\.py$']))
831 def _CheckSubversionConfig(input_api
, output_api
):
832 """Verifies the subversion config file is correctly setup.
834 Checks that autoprops are enabled, returns an error otherwise.
836 join
= input_api
.os_path
.join
837 if input_api
.platform
== 'win32':
838 appdata
= input_api
.environ
.get('APPDATA', '')
840 return [output_api
.PresubmitError('%APPDATA% is not configured.')]
841 path
= join(appdata
, 'Subversion', 'config')
843 home
= input_api
.environ
.get('HOME', '')
845 return [output_api
.PresubmitError('$HOME is not configured.')]
846 path
= join(home
, '.subversion', 'config')
849 'Please look at http://dev.chromium.org/developers/coding-style to\n'
850 'configure your subversion configuration file. This enables automatic\n'
851 'properties to simplify the project maintenance.\n'
852 'Pro-tip: just download and install\n'
853 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
856 lines
= open(path
, 'r').read().splitlines()
857 # Make sure auto-props is enabled and check for 2 Chromium standard
859 if (not '*.cc = svn:eol-style=LF' in lines
or
860 not '*.pdf = svn:mime-type=application/pdf' in lines
or
861 not 'enable-auto-props = yes' in lines
):
863 output_api
.PresubmitNotifyResult(
864 'It looks like you have not configured your subversion config '
865 'file or it is not up-to-date.\n' + error_msg
)
867 except (OSError, IOError):
869 output_api
.PresubmitNotifyResult(
870 'Can\'t find your subversion config file.\n' + error_msg
)
875 def _CheckAuthorizedAuthor(input_api
, output_api
):
876 """For non-googler/chromites committers, verify the author's email address is
879 # TODO(maruel): Add it to input_api?
882 author
= input_api
.change
.author_email
884 input_api
.logging
.info('No author, skipping AUTHOR check')
886 authors_path
= input_api
.os_path
.join(
887 input_api
.PresubmitLocalPath(), 'AUTHORS')
889 input_api
.re
.match(r
'[^#]+\s+\<(.+?)\>\s*$', line
)
890 for line
in open(authors_path
))
891 valid_authors
= [item
.group(1).lower() for item
in valid_authors
if item
]
892 if not any(fnmatch
.fnmatch(author
.lower(), valid
) for valid
in valid_authors
):
893 input_api
.logging
.info('Valid authors are %s', ', '.join(valid_authors
))
894 return [output_api
.PresubmitPromptWarning(
895 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
897 'http://www.chromium.org/developers/contributing-code and read the '
899 'If you are a chromite, verify the contributor signed the CLA.') %
904 def _CheckPatchFiles(input_api
, output_api
):
905 problems
= [f
.LocalPath() for f
in input_api
.AffectedFiles()
906 if f
.LocalPath().endswith(('.orig', '.rej'))]
908 return [output_api
.PresubmitError(
909 "Don't commit .rej and .orig files.", problems
)]
914 def _DidYouMeanOSMacro(bad_macro
):
916 return {'A': 'OS_ANDROID',
926 'W': 'OS_WIN'}[bad_macro
[3].upper()]
931 def _CheckForInvalidOSMacrosInFile(input_api
, f
):
932 """Check for sensible looking, totally invalid OS macros."""
933 preprocessor_statement
= input_api
.re
.compile(r
'^\s*#')
934 os_macro
= input_api
.re
.compile(r
'defined\((OS_[^)]+)\)')
936 for lnum
, line
in f
.ChangedContents():
937 if preprocessor_statement
.search(line
):
938 for match
in os_macro
.finditer(line
):
939 if not match
.group(1) in _VALID_OS_MACROS
:
940 good
= _DidYouMeanOSMacro(match
.group(1))
941 did_you_mean
= ' (did you mean %s?)' % good
if good
else ''
942 results
.append(' %s:%d %s%s' % (f
.LocalPath(),
949 def _CheckForInvalidOSMacros(input_api
, output_api
):
950 """Check all affected files for invalid OS macros."""
952 for f
in input_api
.AffectedFiles():
953 if not f
.LocalPath().endswith(('.py', '.js', '.html', '.css')):
954 bad_macros
.extend(_CheckForInvalidOSMacrosInFile(input_api
, f
))
959 return [output_api
.PresubmitError(
960 'Possibly invalid OS macro[s] found. Please fix your code\n'
961 'or add your macro to src/PRESUBMIT.py.', bad_macros
)]
964 def CheckChangeOnUpload(input_api
, output_api
):
966 results
.extend(_CommonChecks(input_api
, output_api
))
970 def CheckChangeOnCommit(input_api
, output_api
):
972 results
.extend(_CommonChecks(input_api
, output_api
))
973 # TODO(thestig) temporarily disabled, doesn't work in third_party/
974 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
975 # input_api, output_api, sources))
976 # Make sure the tree is 'open'.
977 results
.extend(input_api
.canned_checks
.CheckTreeIsOpen(
980 json_url
='http://chromium-status.appspot.com/current?format=json'))
981 results
.extend(input_api
.canned_checks
.CheckRietveldTryJobExecution(input_api
,
982 output_api
, 'http://codereview.chromium.org',
983 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
984 'tryserver@chromium.org'))
986 results
.extend(input_api
.canned_checks
.CheckChangeHasBugField(
987 input_api
, output_api
))
988 results
.extend(input_api
.canned_checks
.CheckChangeHasDescription(
989 input_api
, output_api
))
990 results
.extend(_CheckSubversionConfig(input_api
, output_api
))
994 def GetPreferredTrySlaves(project
, change
):
995 files
= change
.LocalPaths()
997 if not files
or all(re
.search(r
'[\\/]OWNERS$', f
) for f
in files
):
1000 if all(re
.search('\.(m|mm)$|(^|[/_])mac[/_.]', f
) for f
in files
):
1001 return ['mac_rel', 'mac_asan', 'mac:compile']
1002 if all(re
.search('(^|[/_])win[/_.]', f
) for f
in files
):
1003 return ['win_rel', 'win7_aura', 'win:compile']
1004 if all(re
.search('(^|[/_])android[/_.]', f
) for f
in files
):
1005 return ['android_dbg', 'android_clang_dbg']
1006 if all(re
.search('^native_client_sdk', f
) for f
in files
):
1007 return ['linux_nacl_sdk', 'win_nacl_sdk', 'mac_nacl_sdk']
1008 if all(re
.search('[/_]ios[/_.]', f
) for f
in files
):
1009 return ['ios_rel_device', 'ios_dbg_simulator']
1012 'android_clang_dbg',
1014 'ios_dbg_simulator',
1019 'linux_clang:compile',
1029 # Match things like path/aura/file.cc and path/file_aura.cc.
1030 # Same for chromeos.
1031 if any(re
.search('[/_](aura|chromeos)', f
) for f
in files
):
1032 trybots
+= ['linux_chromeos_clang:compile', 'linux_chromeos_asan']