All instances of addresses as void* are changed to uintptr_t in
[chromium-blink-merge.git] / PRESUBMIT.py
blobeb06a2aed805e499baf9966e24c8d74103208247
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 depot_tools.
9 """
12 _EXCLUDED_PATHS = (
13 r"^breakpad[\\\/].*",
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[\\\/].*",
18 r"^skia[\\\/].*",
19 r"^v8[\\\/].*",
20 r".*MakeFile$",
21 r".+_autogen\.h$",
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.
30 _TESTRUNNER_PATHS = (
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
39 # (best effort).
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. Remember to use the right '
66 'collation (LC_COLLATE=C) and check\nhttps://google-styleguide.googlecode'
67 '.com/svn/trunk/cppguide.html#Names_and_Order_of_Includes')
69 _BANNED_OBJC_FUNCTIONS = (
71 'addTrackingRect:',
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',
77 False,
80 r'/NSTrackingArea\W',
82 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
83 'instead.',
84 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
86 False,
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',
95 True,
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',
104 True,
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',
113 True,
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',
122 True,
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',
131 True,
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',
140 True,
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.
150 'FRIEND_TEST(',
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.',
155 False,
159 'ScopedAllowIO',
161 'New code should not use ScopedAllowIO. Post a task to the blocking',
162 'pool or the FILE thread instead.',
164 True,
166 r"^base[\\\/]process[\\\/]process_metrics_linux\.cc$",
167 r"^chrome[\\\/]browser[\\\/]chromeos[\\\/]boot_times_recorder\.cc$",
168 r"^chrome[\\\/]browser[\\\/]chromeos[\\\/]"
169 "customization_document_browsertest\.cc$",
170 r"^components[\\\/]crash[\\\/]app[\\\/]breakpad_mac\.mm$",
171 r"^content[\\\/]shell[\\\/]browser[\\\/]shell_browser_main\.cc$",
172 r"^content[\\\/]shell[\\\/]browser[\\\/]shell_message_filter\.cc$",
173 r"^mojo[\\\/]edk[\\\/]embedder[\\\/]" +
174 r"simple_platform_shared_buffer_posix\.cc$",
175 r"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
176 r"^net[\\\/]url_request[\\\/]test_url_fetcher_factory\.cc$",
177 r"^remoting[\\\/]host[\\\/]gnubby_auth_handler_posix\.cc$",
178 r"^ui[\\\/]ozone[\\\/]platform[\\\/]drm[\\\/]host[\\\/]"
179 "drm_display_host_manager\.cc$",
183 'SkRefPtr',
185 'The use of SkRefPtr is prohibited. ',
186 'Please use skia::RefPtr instead.'
188 True,
192 'SkAutoRef',
194 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
195 'Please use skia::RefPtr instead.'
197 True,
201 'SkAutoTUnref',
203 'The use of SkAutoTUnref is dangerous because it implicitly ',
204 'converts to a raw pointer. Please use skia::RefPtr instead.'
206 True,
210 'SkAutoUnref',
212 'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
213 'because it implicitly converts to a raw pointer. ',
214 'Please use skia::RefPtr instead.'
216 True,
220 r'/HANDLE_EINTR\(.*close',
222 'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
223 'descriptor will be closed, and it is incorrect to retry the close.',
224 'Either call close directly and ignore its return value, or wrap close',
225 'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
227 True,
231 r'/IGNORE_EINTR\((?!.*close)',
233 'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
234 'calls, use HANDLE_EINTR. See http://crbug.com/269623',
236 True,
238 # Files that #define IGNORE_EINTR.
239 r'^base[\\\/]posix[\\\/]eintr_wrapper\.h$',
240 r'^ppapi[\\\/]tests[\\\/]test_broker\.cc$',
244 r'/v8::Extension\(',
246 'Do not introduce new v8::Extensions into the code base, use',
247 'gin::Wrappable instead. See http://crbug.com/334679',
249 True,
251 r'extensions[\\\/]renderer[\\\/]safe_builtins\.*',
255 '\<MessageLoopProxy\>',
257 'MessageLoopProxy is deprecated. ',
258 'Please use SingleThreadTaskRunner or ThreadTaskRunnerHandle instead.'
260 True,
262 # Internal message_loop related code may still use it.
263 r'^base[\\\/]message_loop[\\\/].*',
268 _IPC_ENUM_TRAITS_DEPRECATED = (
269 'You are using IPC_ENUM_TRAITS() in your code. It has been deprecated.\n'
270 'See http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc')
273 _VALID_OS_MACROS = (
274 # Please keep sorted.
275 'OS_ANDROID',
276 'OS_ANDROID_HOST',
277 'OS_BSD',
278 'OS_CAT', # For testing.
279 'OS_CHROMEOS',
280 'OS_FREEBSD',
281 'OS_IOS',
282 'OS_LINUX',
283 'OS_MACOSX',
284 'OS_NACL',
285 'OS_NACL_NONSFI',
286 'OS_NACL_SFI',
287 'OS_OPENBSD',
288 'OS_POSIX',
289 'OS_QNX',
290 'OS_SOLARIS',
291 'OS_WIN',
295 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
296 """Attempts to prevent use of functions intended only for testing in
297 non-testing code. For now this is just a best-effort implementation
298 that ignores header files and may have some false positives. A
299 better implementation would probably need a proper C++ parser.
301 # We only scan .cc files and the like, as the declaration of
302 # for-testing functions in header files are hard to distinguish from
303 # calls to such functions without a proper C++ parser.
304 file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
306 base_function_pattern = r'[ :]test::[^\s]+|ForTest(s|ing)?|for_test(s|ing)?'
307 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
308 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_function_pattern)
309 exclusion_pattern = input_api.re.compile(
310 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
311 base_function_pattern, base_function_pattern))
313 def FilterFile(affected_file):
314 black_list = (_EXCLUDED_PATHS +
315 _TEST_CODE_EXCLUDED_PATHS +
316 input_api.DEFAULT_BLACK_LIST)
317 return input_api.FilterSourceFile(
318 affected_file,
319 white_list=(file_inclusion_pattern, ),
320 black_list=black_list)
322 problems = []
323 for f in input_api.AffectedSourceFiles(FilterFile):
324 local_path = f.LocalPath()
325 for line_number, line in f.ChangedContents():
326 if (inclusion_pattern.search(line) and
327 not comment_pattern.search(line) and
328 not exclusion_pattern.search(line)):
329 problems.append(
330 '%s:%d\n %s' % (local_path, line_number, line.strip()))
332 if problems:
333 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
334 else:
335 return []
338 def _CheckNoIOStreamInHeaders(input_api, output_api):
339 """Checks to make sure no .h files include <iostream>."""
340 files = []
341 pattern = input_api.re.compile(r'^#include\s*<iostream>',
342 input_api.re.MULTILINE)
343 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
344 if not f.LocalPath().endswith('.h'):
345 continue
346 contents = input_api.ReadFile(f)
347 if pattern.search(contents):
348 files.append(f)
350 if len(files):
351 return [ output_api.PresubmitError(
352 'Do not #include <iostream> in header files, since it inserts static '
353 'initialization into every file including the header. Instead, '
354 '#include <ostream>. See http://crbug.com/94794',
355 files) ]
356 return []
359 def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
360 """Checks to make sure no source files use UNIT_TEST"""
361 problems = []
362 for f in input_api.AffectedFiles():
363 if (not f.LocalPath().endswith(('.cc', '.mm'))):
364 continue
366 for line_num, line in f.ChangedContents():
367 if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
368 problems.append(' %s:%d' % (f.LocalPath(), line_num))
370 if not problems:
371 return []
372 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
373 '\n'.join(problems))]
376 def _FindHistogramNameInLine(histogram_name, line):
377 """Tries to find a histogram name or prefix in a line."""
378 if not "affected-histogram" in line:
379 return histogram_name in line
380 # A histogram_suffixes tag type has an affected-histogram name as a prefix of
381 # the histogram_name.
382 if not '"' in line:
383 return False
384 histogram_prefix = line.split('\"')[1]
385 return histogram_prefix in histogram_name
388 def _CheckUmaHistogramChanges(input_api, output_api):
389 """Check that UMA histogram names in touched lines can still be found in other
390 lines of the patch or in histograms.xml. Note that this check would not catch
391 the reverse: changes in histograms.xml not matched in the code itself."""
392 touched_histograms = []
393 histograms_xml_modifications = []
394 pattern = input_api.re.compile('UMA_HISTOGRAM.*\("(.*)"')
395 for f in input_api.AffectedFiles():
396 # If histograms.xml itself is modified, keep the modified lines for later.
397 if f.LocalPath().endswith(('histograms.xml')):
398 histograms_xml_modifications = f.ChangedContents()
399 continue
400 if not f.LocalPath().endswith(('cc', 'mm', 'cpp')):
401 continue
402 for line_num, line in f.ChangedContents():
403 found = pattern.search(line)
404 if found:
405 touched_histograms.append([found.group(1), f, line_num])
407 # Search for the touched histogram names in the local modifications to
408 # histograms.xml, and, if not found, on the base histograms.xml file.
409 unmatched_histograms = []
410 for histogram_info in touched_histograms:
411 histogram_name_found = False
412 for line_num, line in histograms_xml_modifications:
413 histogram_name_found = _FindHistogramNameInLine(histogram_info[0], line)
414 if histogram_name_found:
415 break
416 if not histogram_name_found:
417 unmatched_histograms.append(histogram_info)
419 histograms_xml_path = 'tools/metrics/histograms/histograms.xml'
420 problems = []
421 if unmatched_histograms:
422 with open(histograms_xml_path) as histograms_xml:
423 for histogram_name, f, line_num in unmatched_histograms:
424 histograms_xml.seek(0)
425 histogram_name_found = False
426 for line in histograms_xml:
427 histogram_name_found = _FindHistogramNameInLine(histogram_name, line)
428 if histogram_name_found:
429 break
430 if not histogram_name_found:
431 problems.append(' [%s:%d] %s' %
432 (f.LocalPath(), line_num, histogram_name))
434 if not problems:
435 return []
436 return [output_api.PresubmitPromptWarning('Some UMA_HISTOGRAM lines have '
437 'been modified and the associated histogram name has no match in either '
438 '%s or the modifications of it:' % (histograms_xml_path), problems)]
441 def _CheckNoNewWStrings(input_api, output_api):
442 """Checks to make sure we don't introduce use of wstrings."""
443 problems = []
444 for f in input_api.AffectedFiles():
445 if (not f.LocalPath().endswith(('.cc', '.h')) or
446 f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h')) or
447 '/win/' in f.LocalPath()):
448 continue
450 allowWString = False
451 for line_num, line in f.ChangedContents():
452 if 'presubmit: allow wstring' in line:
453 allowWString = True
454 elif not allowWString and 'wstring' in line:
455 problems.append(' %s:%d' % (f.LocalPath(), line_num))
456 allowWString = False
457 else:
458 allowWString = False
460 if not problems:
461 return []
462 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
463 ' If you are calling a cross-platform API that accepts a wstring, '
464 'fix the API.\n' +
465 '\n'.join(problems))]
468 def _CheckNoDEPSGIT(input_api, output_api):
469 """Make sure .DEPS.git is never modified manually."""
470 if any(f.LocalPath().endswith('.DEPS.git') for f in
471 input_api.AffectedFiles()):
472 return [output_api.PresubmitError(
473 'Never commit changes to .DEPS.git. This file is maintained by an\n'
474 'automated system based on what\'s in DEPS and your changes will be\n'
475 'overwritten.\n'
476 'See https://sites.google.com/a/chromium.org/dev/developers/how-tos/get-the-code#Rolling_DEPS\n'
477 'for more information')]
478 return []
481 def _CheckValidHostsInDEPS(input_api, output_api):
482 """Checks that DEPS file deps are from allowed_hosts."""
483 # Run only if DEPS file has been modified to annoy fewer bystanders.
484 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
485 return []
486 # Outsource work to gclient verify
487 try:
488 input_api.subprocess.check_output(['gclient', 'verify'])
489 return []
490 except input_api.subprocess.CalledProcessError, error:
491 return [output_api.PresubmitError(
492 'DEPS file must have only git dependencies.',
493 long_text=error.output)]
496 def _CheckNoBannedFunctions(input_api, output_api):
497 """Make sure that banned functions are not used."""
498 warnings = []
499 errors = []
501 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
502 for f in input_api.AffectedFiles(file_filter=file_filter):
503 for line_num, line in f.ChangedContents():
504 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
505 matched = False
506 if func_name[0:1] == '/':
507 regex = func_name[1:]
508 if input_api.re.search(regex, line):
509 matched = True
510 elif func_name in line:
511 matched = True
512 if matched:
513 problems = warnings;
514 if error:
515 problems = errors;
516 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
517 for message_line in message:
518 problems.append(' %s' % message_line)
520 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
521 for f in input_api.AffectedFiles(file_filter=file_filter):
522 for line_num, line in f.ChangedContents():
523 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
524 def IsBlacklisted(affected_file, blacklist):
525 local_path = affected_file.LocalPath()
526 for item in blacklist:
527 if input_api.re.match(item, local_path):
528 return True
529 return False
530 if IsBlacklisted(f, excluded_paths):
531 continue
532 matched = False
533 if func_name[0:1] == '/':
534 regex = func_name[1:]
535 if input_api.re.search(regex, line):
536 matched = True
537 elif func_name in line:
538 matched = True
539 if matched:
540 problems = warnings;
541 if error:
542 problems = errors;
543 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
544 for message_line in message:
545 problems.append(' %s' % message_line)
547 result = []
548 if (warnings):
549 result.append(output_api.PresubmitPromptWarning(
550 'Banned functions were used.\n' + '\n'.join(warnings)))
551 if (errors):
552 result.append(output_api.PresubmitError(
553 'Banned functions were used.\n' + '\n'.join(errors)))
554 return result
557 def _CheckNoPragmaOnce(input_api, output_api):
558 """Make sure that banned functions are not used."""
559 files = []
560 pattern = input_api.re.compile(r'^#pragma\s+once',
561 input_api.re.MULTILINE)
562 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
563 if not f.LocalPath().endswith('.h'):
564 continue
565 contents = input_api.ReadFile(f)
566 if pattern.search(contents):
567 files.append(f)
569 if files:
570 return [output_api.PresubmitError(
571 'Do not use #pragma once in header files.\n'
572 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
573 files)]
574 return []
577 def _CheckNoTrinaryTrueFalse(input_api, output_api):
578 """Checks to make sure we don't introduce use of foo ? true : false."""
579 problems = []
580 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
581 for f in input_api.AffectedFiles():
582 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
583 continue
585 for line_num, line in f.ChangedContents():
586 if pattern.match(line):
587 problems.append(' %s:%d' % (f.LocalPath(), line_num))
589 if not problems:
590 return []
591 return [output_api.PresubmitPromptWarning(
592 'Please consider avoiding the "? true : false" pattern if possible.\n' +
593 '\n'.join(problems))]
596 def _CheckUnwantedDependencies(input_api, output_api):
597 """Runs checkdeps on #include statements added in this
598 change. Breaking - rules is an error, breaking ! rules is a
599 warning.
601 import sys
602 # We need to wait until we have an input_api object and use this
603 # roundabout construct to import checkdeps because this file is
604 # eval-ed and thus doesn't have __file__.
605 original_sys_path = sys.path
606 try:
607 sys.path = sys.path + [input_api.os_path.join(
608 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
609 import checkdeps
610 from cpp_checker import CppChecker
611 from rules import Rule
612 finally:
613 # Restore sys.path to what it was before.
614 sys.path = original_sys_path
616 added_includes = []
617 for f in input_api.AffectedFiles():
618 if not CppChecker.IsCppFile(f.LocalPath()):
619 continue
621 changed_lines = [line for line_num, line in f.ChangedContents()]
622 added_includes.append([f.LocalPath(), changed_lines])
624 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
626 error_descriptions = []
627 warning_descriptions = []
628 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
629 added_includes):
630 description_with_path = '%s\n %s' % (path, rule_description)
631 if rule_type == Rule.DISALLOW:
632 error_descriptions.append(description_with_path)
633 else:
634 warning_descriptions.append(description_with_path)
636 results = []
637 if error_descriptions:
638 results.append(output_api.PresubmitError(
639 'You added one or more #includes that violate checkdeps rules.',
640 error_descriptions))
641 if warning_descriptions:
642 results.append(output_api.PresubmitPromptOrNotify(
643 'You added one or more #includes of files that are temporarily\n'
644 'allowed but being removed. Can you avoid introducing the\n'
645 '#include? See relevant DEPS file(s) for details and contacts.',
646 warning_descriptions))
647 return results
650 def _CheckFilePermissions(input_api, output_api):
651 """Check that all files have their permissions properly set."""
652 if input_api.platform == 'win32':
653 return []
654 args = [input_api.python_executable, 'tools/checkperms/checkperms.py',
655 '--root', input_api.change.RepositoryRoot()]
656 for f in input_api.AffectedFiles():
657 args += ['--file', f.LocalPath()]
658 checkperms = input_api.subprocess.Popen(args,
659 stdout=input_api.subprocess.PIPE)
660 errors = checkperms.communicate()[0].strip()
661 if errors:
662 return [output_api.PresubmitError('checkperms.py failed.',
663 errors.splitlines())]
664 return []
667 def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
668 """Makes sure we don't include ui/aura/window_property.h
669 in header files.
671 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
672 errors = []
673 for f in input_api.AffectedFiles():
674 if not f.LocalPath().endswith('.h'):
675 continue
676 for line_num, line in f.ChangedContents():
677 if pattern.match(line):
678 errors.append(' %s:%d' % (f.LocalPath(), line_num))
680 results = []
681 if errors:
682 results.append(output_api.PresubmitError(
683 'Header files should not include ui/aura/window_property.h', errors))
684 return results
687 def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
688 """Checks that the lines in scope occur in the right order.
690 1. C system files in alphabetical order
691 2. C++ system files in alphabetical order
692 3. Project's .h files
695 c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
696 cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
697 custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
699 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
701 state = C_SYSTEM_INCLUDES
703 previous_line = ''
704 previous_line_num = 0
705 problem_linenums = []
706 out_of_order = " - line belongs before previous line"
707 for line_num, line in scope:
708 if c_system_include_pattern.match(line):
709 if state != C_SYSTEM_INCLUDES:
710 problem_linenums.append((line_num, previous_line_num,
711 " - C system include file in wrong block"))
712 elif previous_line and previous_line > line:
713 problem_linenums.append((line_num, previous_line_num,
714 out_of_order))
715 elif cpp_system_include_pattern.match(line):
716 if state == C_SYSTEM_INCLUDES:
717 state = CPP_SYSTEM_INCLUDES
718 elif state == CUSTOM_INCLUDES:
719 problem_linenums.append((line_num, previous_line_num,
720 " - c++ system include file in wrong block"))
721 elif previous_line and previous_line > line:
722 problem_linenums.append((line_num, previous_line_num, out_of_order))
723 elif custom_include_pattern.match(line):
724 if state != CUSTOM_INCLUDES:
725 state = CUSTOM_INCLUDES
726 elif previous_line and previous_line > line:
727 problem_linenums.append((line_num, previous_line_num, out_of_order))
728 else:
729 problem_linenums.append((line_num, previous_line_num,
730 "Unknown include type"))
731 previous_line = line
732 previous_line_num = line_num
734 warnings = []
735 for (line_num, previous_line_num, failure_type) in problem_linenums:
736 if line_num in changed_linenums or previous_line_num in changed_linenums:
737 warnings.append(' %s:%d:%s' % (file_path, line_num, failure_type))
738 return warnings
741 def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
742 """Checks the #include order for the given file f."""
744 system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
745 # Exclude the following includes from the check:
746 # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
747 # specific order.
748 # 2) <atlbase.h>, "build/build_config.h"
749 excluded_include_pattern = input_api.re.compile(
750 r'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
751 custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
752 # Match the final or penultimate token if it is xxxtest so we can ignore it
753 # when considering the special first include.
754 test_file_tag_pattern = input_api.re.compile(
755 r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
756 if_pattern = input_api.re.compile(
757 r'\s*#\s*(if|elif|else|endif|define|undef).*')
758 # Some files need specialized order of includes; exclude such files from this
759 # check.
760 uncheckable_includes_pattern = input_api.re.compile(
761 r'\s*#include '
762 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
764 contents = f.NewContents()
765 warnings = []
766 line_num = 0
768 # Handle the special first include. If the first include file is
769 # some/path/file.h, the corresponding including file can be some/path/file.cc,
770 # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
771 # etc. It's also possible that no special first include exists.
772 # If the included file is some/path/file_platform.h the including file could
773 # also be some/path/file_xxxtest_platform.h.
774 including_file_base_name = test_file_tag_pattern.sub(
775 '', input_api.os_path.basename(f.LocalPath()))
777 for line in contents:
778 line_num += 1
779 if system_include_pattern.match(line):
780 # No special first include -> process the line again along with normal
781 # includes.
782 line_num -= 1
783 break
784 match = custom_include_pattern.match(line)
785 if match:
786 match_dict = match.groupdict()
787 header_basename = test_file_tag_pattern.sub(
788 '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '')
790 if header_basename not in including_file_base_name:
791 # No special first include -> process the line again along with normal
792 # includes.
793 line_num -= 1
794 break
796 # Split into scopes: Each region between #if and #endif is its own scope.
797 scopes = []
798 current_scope = []
799 for line in contents[line_num:]:
800 line_num += 1
801 if uncheckable_includes_pattern.match(line):
802 continue
803 if if_pattern.match(line):
804 scopes.append(current_scope)
805 current_scope = []
806 elif ((system_include_pattern.match(line) or
807 custom_include_pattern.match(line)) and
808 not excluded_include_pattern.match(line)):
809 current_scope.append((line_num, line))
810 scopes.append(current_scope)
812 for scope in scopes:
813 warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
814 changed_linenums))
815 return warnings
818 def _CheckIncludeOrder(input_api, output_api):
819 """Checks that the #include order is correct.
821 1. The corresponding header for source files.
822 2. C system files in alphabetical order
823 3. C++ system files in alphabetical order
824 4. Project's .h files in alphabetical order
826 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
827 these rules separately.
829 def FileFilterIncludeOrder(affected_file):
830 black_list = (_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
831 return input_api.FilterSourceFile(affected_file, black_list=black_list)
833 warnings = []
834 for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder):
835 if f.LocalPath().endswith(('.cc', '.h', '.mm')):
836 changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
837 warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
839 results = []
840 if warnings:
841 results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
842 warnings))
843 return results
846 def _CheckForVersionControlConflictsInFile(input_api, f):
847 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
848 errors = []
849 for line_num, line in f.ChangedContents():
850 if f.LocalPath().endswith('.md'):
851 # First-level headers in markdown look a lot like version control
852 # conflict markers. http://daringfireball.net/projects/markdown/basics
853 continue
854 if pattern.match(line):
855 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
856 return errors
859 def _CheckForVersionControlConflicts(input_api, output_api):
860 """Usually this is not intentional and will cause a compile failure."""
861 errors = []
862 for f in input_api.AffectedFiles():
863 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
865 results = []
866 if errors:
867 results.append(output_api.PresubmitError(
868 'Version control conflict markers found, please resolve.', errors))
869 return results
872 def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
873 def FilterFile(affected_file):
874 """Filter function for use with input_api.AffectedSourceFiles,
875 below. This filters out everything except non-test files from
876 top-level directories that generally speaking should not hard-code
877 service URLs (e.g. src/android_webview/, src/content/ and others).
879 return input_api.FilterSourceFile(
880 affected_file,
881 white_list=(r'^(android_webview|base|content|net)[\\\/].*', ),
882 black_list=(_EXCLUDED_PATHS +
883 _TEST_CODE_EXCLUDED_PATHS +
884 input_api.DEFAULT_BLACK_LIST))
886 base_pattern = '"[^"]*google\.com[^"]*"'
887 comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
888 pattern = input_api.re.compile(base_pattern)
889 problems = [] # items are (filename, line_number, line)
890 for f in input_api.AffectedSourceFiles(FilterFile):
891 for line_num, line in f.ChangedContents():
892 if not comment_pattern.search(line) and pattern.search(line):
893 problems.append((f.LocalPath(), line_num, line))
895 if problems:
896 return [output_api.PresubmitPromptOrNotify(
897 'Most layers below src/chrome/ should not hardcode service URLs.\n'
898 'Are you sure this is correct?',
899 [' %s:%d: %s' % (
900 problem[0], problem[1], problem[2]) for problem in problems])]
901 else:
902 return []
905 def _CheckNoAbbreviationInPngFileName(input_api, output_api):
906 """Makes sure there are no abbreviations in the name of PNG files.
907 The native_client_sdk directory is excluded because it has auto-generated PNG
908 files for documentation.
910 errors = []
911 white_list = (r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$',)
912 black_list = (r'^native_client_sdk[\\\/]',)
913 file_filter = lambda f: input_api.FilterSourceFile(
914 f, white_list=white_list, black_list=black_list)
915 for f in input_api.AffectedFiles(include_deletes=False,
916 file_filter=file_filter):
917 errors.append(' %s' % f.LocalPath())
919 results = []
920 if errors:
921 results.append(output_api.PresubmitError(
922 'The name of PNG files should not have abbreviations. \n'
923 'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
924 'Contact oshima@chromium.org if you have questions.', errors))
925 return results
928 def _FilesToCheckForIncomingDeps(re, changed_lines):
929 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
930 a set of DEPS entries that we should look up.
932 For a directory (rather than a specific filename) we fake a path to
933 a specific filename by adding /DEPS. This is chosen as a file that
934 will seldom or never be subject to per-file include_rules.
936 # We ignore deps entries on auto-generated directories.
937 AUTO_GENERATED_DIRS = ['grit', 'jni']
939 # This pattern grabs the path without basename in the first
940 # parentheses, and the basename (if present) in the second. It
941 # relies on the simple heuristic that if there is a basename it will
942 # be a header file ending in ".h".
943 pattern = re.compile(
944 r"""['"]\+([^'"]+?)(/[a-zA-Z0-9_]+\.h)?['"].*""")
945 results = set()
946 for changed_line in changed_lines:
947 m = pattern.match(changed_line)
948 if m:
949 path = m.group(1)
950 if path.split('/')[0] not in AUTO_GENERATED_DIRS:
951 if m.group(2):
952 results.add('%s%s' % (path, m.group(2)))
953 else:
954 results.add('%s/DEPS' % path)
955 return results
958 def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
959 """When a dependency prefixed with + is added to a DEPS file, we
960 want to make sure that the change is reviewed by an OWNER of the
961 target file or directory, to avoid layering violations from being
962 introduced. This check verifies that this happens.
964 changed_lines = set()
965 for f in input_api.AffectedFiles():
966 filename = input_api.os_path.basename(f.LocalPath())
967 if filename == 'DEPS':
968 changed_lines |= set(line.strip()
969 for line_num, line
970 in f.ChangedContents())
971 if not changed_lines:
972 return []
974 virtual_depended_on_files = _FilesToCheckForIncomingDeps(input_api.re,
975 changed_lines)
976 if not virtual_depended_on_files:
977 return []
979 if input_api.is_committing:
980 if input_api.tbr:
981 return [output_api.PresubmitNotifyResult(
982 '--tbr was specified, skipping OWNERS check for DEPS additions')]
983 if not input_api.change.issue:
984 return [output_api.PresubmitError(
985 "DEPS approval by OWNERS check failed: this change has "
986 "no Rietveld issue number, so we can't check it for approvals.")]
987 output = output_api.PresubmitError
988 else:
989 output = output_api.PresubmitNotifyResult
991 owners_db = input_api.owners_db
992 owner_email, reviewers = input_api.canned_checks._RietveldOwnerAndReviewers(
993 input_api,
994 owners_db.email_regexp,
995 approval_needed=input_api.is_committing)
997 owner_email = owner_email or input_api.change.author_email
999 reviewers_plus_owner = set(reviewers)
1000 if owner_email:
1001 reviewers_plus_owner.add(owner_email)
1002 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1003 reviewers_plus_owner)
1005 # We strip the /DEPS part that was added by
1006 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1007 # directory.
1008 def StripDeps(path):
1009 start_deps = path.rfind('/DEPS')
1010 if start_deps != -1:
1011 return path[:start_deps]
1012 else:
1013 return path
1014 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1015 for path in missing_files]
1017 if unapproved_dependencies:
1018 output_list = [
1019 output('Missing LGTM from OWNERS of dependencies added to DEPS:\n %s' %
1020 '\n '.join(sorted(unapproved_dependencies)))]
1021 if not input_api.is_committing:
1022 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1023 output_list.append(output(
1024 'Suggested missing target path OWNERS:\n %s' %
1025 '\n '.join(suggested_owners or [])))
1026 return output_list
1028 return []
1031 def _CheckSpamLogging(input_api, output_api):
1032 file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
1033 black_list = (_EXCLUDED_PATHS +
1034 _TEST_CODE_EXCLUDED_PATHS +
1035 input_api.DEFAULT_BLACK_LIST +
1036 (r"^base[\\\/]logging\.h$",
1037 r"^base[\\\/]logging\.cc$",
1038 r"^chrome[\\\/]app[\\\/]chrome_main_delegate\.cc$",
1039 r"^chrome[\\\/]browser[\\\/]chrome_browser_main\.cc$",
1040 r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
1041 r"startup_browser_creator\.cc$",
1042 r"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
1043 r"chrome[\\\/]browser[\\\/]diagnostics[\\\/]" +
1044 r"diagnostics_writer\.cc$",
1045 r"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$",
1046 r"^chromecast[\\\/]",
1047 r"^cloud_print[\\\/]",
1048 r"^components[\\\/]html_viewer[\\\/]"
1049 r"web_test_delegate_impl\.cc$",
1050 r"^content[\\\/]common[\\\/]gpu[\\\/]client[\\\/]"
1051 r"gl_helper_benchmark\.cc$",
1052 r"^courgette[\\\/]courgette_tool\.cc$",
1053 r"^extensions[\\\/]renderer[\\\/]logging_native_handler\.cc$",
1054 r"^ipc[\\\/]ipc_logging\.cc$",
1055 r"^native_client_sdk[\\\/]",
1056 r"^remoting[\\\/]base[\\\/]logging\.h$",
1057 r"^remoting[\\\/]host[\\\/].*",
1058 r"^sandbox[\\\/]linux[\\\/].*",
1059 r"^tools[\\\/]",
1060 r"^ui[\\\/]aura[\\\/]bench[\\\/]bench_main\.cc$",
1061 r"^storage[\\\/]browser[\\\/]fileapi[\\\/]" +
1062 r"dump_file_system.cc$",))
1063 source_file_filter = lambda x: input_api.FilterSourceFile(
1064 x, white_list=(file_inclusion_pattern,), black_list=black_list)
1066 log_info = []
1067 printf = []
1069 for f in input_api.AffectedSourceFiles(source_file_filter):
1070 contents = input_api.ReadFile(f, 'rb')
1071 if input_api.re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", contents):
1072 log_info.append(f.LocalPath())
1073 elif input_api.re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", contents):
1074 log_info.append(f.LocalPath())
1076 if input_api.re.search(r"\bprintf\(", contents):
1077 printf.append(f.LocalPath())
1078 elif input_api.re.search(r"\bfprintf\((stdout|stderr)", contents):
1079 printf.append(f.LocalPath())
1081 if log_info:
1082 return [output_api.PresubmitError(
1083 'These files spam the console log with LOG(INFO):',
1084 items=log_info)]
1085 if printf:
1086 return [output_api.PresubmitError(
1087 'These files spam the console log with printf/fprintf:',
1088 items=printf)]
1089 return []
1092 def _CheckForAnonymousVariables(input_api, output_api):
1093 """These types are all expected to hold locks while in scope and
1094 so should never be anonymous (which causes them to be immediately
1095 destroyed)."""
1096 they_who_must_be_named = [
1097 'base::AutoLock',
1098 'base::AutoReset',
1099 'base::AutoUnlock',
1100 'SkAutoAlphaRestore',
1101 'SkAutoBitmapShaderInstall',
1102 'SkAutoBlitterChoose',
1103 'SkAutoBounderCommit',
1104 'SkAutoCallProc',
1105 'SkAutoCanvasRestore',
1106 'SkAutoCommentBlock',
1107 'SkAutoDescriptor',
1108 'SkAutoDisableDirectionCheck',
1109 'SkAutoDisableOvalCheck',
1110 'SkAutoFree',
1111 'SkAutoGlyphCache',
1112 'SkAutoHDC',
1113 'SkAutoLockColors',
1114 'SkAutoLockPixels',
1115 'SkAutoMalloc',
1116 'SkAutoMaskFreeImage',
1117 'SkAutoMutexAcquire',
1118 'SkAutoPathBoundsUpdate',
1119 'SkAutoPDFRelease',
1120 'SkAutoRasterClipValidate',
1121 'SkAutoRef',
1122 'SkAutoTime',
1123 'SkAutoTrace',
1124 'SkAutoUnref',
1126 anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
1127 # bad: base::AutoLock(lock.get());
1128 # not bad: base::AutoLock lock(lock.get());
1129 bad_pattern = input_api.re.compile(anonymous)
1130 # good: new base::AutoLock(lock.get())
1131 good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
1132 errors = []
1134 for f in input_api.AffectedFiles():
1135 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1136 continue
1137 for linenum, line in f.ChangedContents():
1138 if bad_pattern.search(line) and not good_pattern.search(line):
1139 errors.append('%s:%d' % (f.LocalPath(), linenum))
1141 if errors:
1142 return [output_api.PresubmitError(
1143 'These lines create anonymous variables that need to be named:',
1144 items=errors)]
1145 return []
1148 def _CheckCygwinShell(input_api, output_api):
1149 source_file_filter = lambda x: input_api.FilterSourceFile(
1150 x, white_list=(r'.+\.(gyp|gypi)$',))
1151 cygwin_shell = []
1153 for f in input_api.AffectedSourceFiles(source_file_filter):
1154 for linenum, line in f.ChangedContents():
1155 if 'msvs_cygwin_shell' in line:
1156 cygwin_shell.append(f.LocalPath())
1157 break
1159 if cygwin_shell:
1160 return [output_api.PresubmitError(
1161 'These files should not use msvs_cygwin_shell (the default is 0):',
1162 items=cygwin_shell)]
1163 return []
1166 def _CheckUserActionUpdate(input_api, output_api):
1167 """Checks if any new user action has been added."""
1168 if any('actions.xml' == input_api.os_path.basename(f) for f in
1169 input_api.LocalPaths()):
1170 # If actions.xml is already included in the changelist, the PRESUBMIT
1171 # for actions.xml will do a more complete presubmit check.
1172 return []
1174 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm'))
1175 action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
1176 current_actions = None
1177 for f in input_api.AffectedFiles(file_filter=file_filter):
1178 for line_num, line in f.ChangedContents():
1179 match = input_api.re.search(action_re, line)
1180 if match:
1181 # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1182 # loaded only once.
1183 if not current_actions:
1184 with open('tools/metrics/actions/actions.xml') as actions_f:
1185 current_actions = actions_f.read()
1186 # Search for the matched user action name in |current_actions|.
1187 for action_name in match.groups():
1188 action = 'name="{0}"'.format(action_name)
1189 if action not in current_actions:
1190 return [output_api.PresubmitPromptWarning(
1191 'File %s line %d: %s is missing in '
1192 'tools/metrics/actions/actions.xml. Please run '
1193 'tools/metrics/actions/extract_actions.py to update.'
1194 % (f.LocalPath(), line_num, action_name))]
1195 return []
1198 def _GetJSONParseError(input_api, filename, eat_comments=True):
1199 try:
1200 contents = input_api.ReadFile(filename)
1201 if eat_comments:
1202 import sys
1203 original_sys_path = sys.path
1204 try:
1205 sys.path = sys.path + [input_api.os_path.join(
1206 input_api.PresubmitLocalPath(),
1207 'tools', 'json_comment_eater')]
1208 import json_comment_eater
1209 finally:
1210 sys.path = original_sys_path
1211 contents = json_comment_eater.Nom(contents)
1213 input_api.json.loads(contents)
1214 except ValueError as e:
1215 return e
1216 return None
1219 def _GetIDLParseError(input_api, filename):
1220 try:
1221 contents = input_api.ReadFile(filename)
1222 idl_schema = input_api.os_path.join(
1223 input_api.PresubmitLocalPath(),
1224 'tools', 'json_schema_compiler', 'idl_schema.py')
1225 process = input_api.subprocess.Popen(
1226 [input_api.python_executable, idl_schema],
1227 stdin=input_api.subprocess.PIPE,
1228 stdout=input_api.subprocess.PIPE,
1229 stderr=input_api.subprocess.PIPE,
1230 universal_newlines=True)
1231 (_, error) = process.communicate(input=contents)
1232 return error or None
1233 except ValueError as e:
1234 return e
1237 def _CheckParseErrors(input_api, output_api):
1238 """Check that IDL and JSON files do not contain syntax errors."""
1239 actions = {
1240 '.idl': _GetIDLParseError,
1241 '.json': _GetJSONParseError,
1243 # These paths contain test data and other known invalid JSON files.
1244 excluded_patterns = [
1245 r'test[\\\/]data[\\\/]',
1246 r'^components[\\\/]policy[\\\/]resources[\\\/]policy_templates\.json$',
1248 # Most JSON files are preprocessed and support comments, but these do not.
1249 json_no_comments_patterns = [
1250 r'^testing[\\\/]',
1252 # Only run IDL checker on files in these directories.
1253 idl_included_patterns = [
1254 r'^chrome[\\\/]common[\\\/]extensions[\\\/]api[\\\/]',
1255 r'^extensions[\\\/]common[\\\/]api[\\\/]',
1258 def get_action(affected_file):
1259 filename = affected_file.LocalPath()
1260 return actions.get(input_api.os_path.splitext(filename)[1])
1262 def MatchesFile(patterns, path):
1263 for pattern in patterns:
1264 if input_api.re.search(pattern, path):
1265 return True
1266 return False
1268 def FilterFile(affected_file):
1269 action = get_action(affected_file)
1270 if not action:
1271 return False
1272 path = affected_file.LocalPath()
1274 if MatchesFile(excluded_patterns, path):
1275 return False
1277 if (action == _GetIDLParseError and
1278 not MatchesFile(idl_included_patterns, path)):
1279 return False
1280 return True
1282 results = []
1283 for affected_file in input_api.AffectedFiles(
1284 file_filter=FilterFile, include_deletes=False):
1285 action = get_action(affected_file)
1286 kwargs = {}
1287 if (action == _GetJSONParseError and
1288 MatchesFile(json_no_comments_patterns, affected_file.LocalPath())):
1289 kwargs['eat_comments'] = False
1290 parse_error = action(input_api,
1291 affected_file.AbsoluteLocalPath(),
1292 **kwargs)
1293 if parse_error:
1294 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
1295 (affected_file.LocalPath(), parse_error)))
1296 return results
1299 def _CheckJavaStyle(input_api, output_api):
1300 """Runs checkstyle on changed java files and returns errors if any exist."""
1301 import sys
1302 original_sys_path = sys.path
1303 try:
1304 sys.path = sys.path + [input_api.os_path.join(
1305 input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1306 import checkstyle
1307 finally:
1308 # Restore sys.path to what it was before.
1309 sys.path = original_sys_path
1311 return checkstyle.RunCheckstyle(
1312 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml',
1313 black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
1316 def _CheckAndroidToastUsage(input_api, output_api):
1317 """Checks that code uses org.chromium.ui.widget.Toast instead of
1318 android.widget.Toast (Chromium Toast doesn't force hardware
1319 acceleration on low-end devices, saving memory).
1321 toast_import_pattern = input_api.re.compile(
1322 r'^import android\.widget\.Toast;$')
1324 errors = []
1326 sources = lambda affected_file: input_api.FilterSourceFile(
1327 affected_file,
1328 black_list=(_EXCLUDED_PATHS +
1329 _TEST_CODE_EXCLUDED_PATHS +
1330 input_api.DEFAULT_BLACK_LIST +
1331 (r'^chromecast[\\\/].*',
1332 r'^remoting[\\\/].*')),
1333 white_list=(r'.*\.java$',))
1335 for f in input_api.AffectedSourceFiles(sources):
1336 for line_num, line in f.ChangedContents():
1337 if toast_import_pattern.search(line):
1338 errors.append("%s:%d" % (f.LocalPath(), line_num))
1340 results = []
1342 if errors:
1343 results.append(output_api.PresubmitError(
1344 'android.widget.Toast usage is detected. Android toasts use hardware'
1345 ' acceleration, and can be\ncostly on low-end devices. Please use'
1346 ' org.chromium.ui.widget.Toast instead.\n'
1347 'Contact dskiba@chromium.org if you have any questions.',
1348 errors))
1350 return results
1353 def _CheckAndroidCrLogUsage(input_api, output_api):
1354 """Checks that new logs using org.chromium.base.Log:
1355 - Are using 'TAG' as variable name for the tags (warn)
1356 - Are using the suggested name format for the tags: "cr.<PackageTag>" (warn)
1357 - Are using a tag that is shorter than 23 characters (error)
1359 cr_log_import_pattern = input_api.re.compile(
1360 r'^import org\.chromium\.base\.Log;$', input_api.re.MULTILINE)
1361 class_in_base_pattern = input_api.re.compile(
1362 r'^package org\.chromium\.base;$', input_api.re.MULTILINE)
1363 has_some_log_import_pattern = input_api.re.compile(
1364 r'^import .*\.Log;$', input_api.re.MULTILINE)
1365 # Extract the tag from lines like `Log.d(TAG, "*");` or `Log.d("TAG", "*");`
1366 log_call_pattern = input_api.re.compile(r'^\s*Log\.\w\((?P<tag>\"?\w+\"?)\,')
1367 log_decl_pattern = input_api.re.compile(
1368 r'^\s*private static final String TAG = "(?P<name>(.*)")',
1369 input_api.re.MULTILINE)
1370 log_name_pattern = input_api.re.compile(r'^cr[.\w]*')
1372 REF_MSG = ('See docs/android_logging.md '
1373 'or contact dgn@chromium.org for more info.')
1374 sources = lambda x: input_api.FilterSourceFile(x, white_list=(r'.*\.java$',))
1376 tag_decl_errors = []
1377 tag_length_errors = []
1378 tag_errors = []
1379 util_log_errors = []
1381 for f in input_api.AffectedSourceFiles(sources):
1382 file_content = input_api.ReadFile(f)
1383 has_modified_logs = False
1385 # Per line checks
1386 if (cr_log_import_pattern.search(file_content) or
1387 (class_in_base_pattern.search(file_content) and
1388 not has_some_log_import_pattern.search(file_content))):
1389 # Checks to run for files using cr log
1390 for line_num, line in f.ChangedContents():
1392 # Check if the new line is doing some logging
1393 match = log_call_pattern.search(line)
1394 if match:
1395 has_modified_logs = True
1397 # Make sure it uses "TAG"
1398 if not match.group('tag') == 'TAG':
1399 tag_errors.append("%s:%d" % (f.LocalPath(), line_num))
1400 else:
1401 # Report non cr Log function calls in changed lines
1402 for line_num, line in f.ChangedContents():
1403 if log_call_pattern.search(line):
1404 util_log_errors.append("%s:%d" % (f.LocalPath(), line_num))
1406 # Per file checks
1407 if has_modified_logs:
1408 # Make sure the tag is using the "cr" prefix and is not too long
1409 match = log_decl_pattern.search(file_content)
1410 tag_name = match.group('name') if match else ''
1411 if not log_name_pattern.search(tag_name ):
1412 tag_decl_errors.append(f.LocalPath())
1413 if len(tag_name) > 23:
1414 tag_length_errors.append(f.LocalPath())
1416 results = []
1417 if tag_decl_errors:
1418 results.append(output_api.PresubmitPromptWarning(
1419 'Please define your tags using the suggested format: .\n'
1420 '"private static final String TAG = "cr.<package tag>".\n' + REF_MSG,
1421 tag_decl_errors))
1423 if tag_length_errors:
1424 results.append(output_api.PresubmitError(
1425 'The tag length is restricted by the system to be at most '
1426 '23 characters.\n' + REF_MSG,
1427 tag_length_errors))
1429 if tag_errors:
1430 results.append(output_api.PresubmitPromptWarning(
1431 'Please use a variable named "TAG" for your log tags.\n' + REF_MSG,
1432 tag_errors))
1434 if util_log_errors:
1435 results.append(output_api.PresubmitPromptWarning(
1436 'Please use org.chromium.base.Log for new logs.\n' + REF_MSG,
1437 util_log_errors))
1439 return results
1442 def _CheckForCopyrightedCode(input_api, output_api):
1443 """Verifies that newly added code doesn't contain copyrighted material
1444 and is properly licensed under the standard Chromium license.
1446 As there can be false positives, we maintain a whitelist file. This check
1447 also verifies that the whitelist file is up to date.
1449 import sys
1450 original_sys_path = sys.path
1451 try:
1452 sys.path = sys.path + [input_api.os_path.join(
1453 input_api.PresubmitLocalPath(), 'tools')]
1454 from copyright_scanner import copyright_scanner
1455 finally:
1456 # Restore sys.path to what it was before.
1457 sys.path = original_sys_path
1459 return copyright_scanner.ScanAtPresubmit(input_api, output_api)
1462 def _CheckSingletonInHeaders(input_api, output_api):
1463 """Checks to make sure no header files have |Singleton<|."""
1464 def FileFilter(affected_file):
1465 # It's ok for base/memory/singleton.h to have |Singleton<|.
1466 black_list = (_EXCLUDED_PATHS +
1467 input_api.DEFAULT_BLACK_LIST +
1468 (r"^base[\\\/]memory[\\\/]singleton\.h$",))
1469 return input_api.FilterSourceFile(affected_file, black_list=black_list)
1471 pattern = input_api.re.compile(r'(?<!class\sbase::)Singleton\s*<')
1472 files = []
1473 for f in input_api.AffectedSourceFiles(FileFilter):
1474 if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
1475 f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
1476 contents = input_api.ReadFile(f)
1477 for line in contents.splitlines(False):
1478 if (not input_api.re.match(r'//', line) and # Strip C++ comment.
1479 pattern.search(line)):
1480 files.append(f)
1481 break
1483 if files:
1484 return [ output_api.PresubmitError(
1485 'Found base::Singleton<T> in the following header files.\n' +
1486 'Please move them to an appropriate source file so that the ' +
1487 'template gets instantiated in a single compilation unit.',
1488 files) ]
1489 return []
1492 _DEPRECATED_CSS = [
1493 # Values
1494 ( "-webkit-box", "flex" ),
1495 ( "-webkit-inline-box", "inline-flex" ),
1496 ( "-webkit-flex", "flex" ),
1497 ( "-webkit-inline-flex", "inline-flex" ),
1498 ( "-webkit-min-content", "min-content" ),
1499 ( "-webkit-max-content", "max-content" ),
1501 # Properties
1502 ( "-webkit-background-clip", "background-clip" ),
1503 ( "-webkit-background-origin", "background-origin" ),
1504 ( "-webkit-background-size", "background-size" ),
1505 ( "-webkit-box-shadow", "box-shadow" ),
1507 # Functions
1508 ( "-webkit-gradient", "gradient" ),
1509 ( "-webkit-repeating-gradient", "repeating-gradient" ),
1510 ( "-webkit-linear-gradient", "linear-gradient" ),
1511 ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
1512 ( "-webkit-radial-gradient", "radial-gradient" ),
1513 ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
1516 def _CheckNoDeprecatedCSS(input_api, output_api):
1517 """ Make sure that we don't use deprecated CSS
1518 properties, functions or values. Our external
1519 documentation and iOS CSS for dom distiller
1520 (reader mode) are ignored by the hooks as it
1521 needs to be consumed by WebKit. """
1522 results = []
1523 file_inclusion_pattern = (r".+\.css$",)
1524 black_list = (_EXCLUDED_PATHS +
1525 _TEST_CODE_EXCLUDED_PATHS +
1526 input_api.DEFAULT_BLACK_LIST +
1527 (r"^chrome/common/extensions/docs",
1528 r"^chrome/docs",
1529 r"^components/dom_distiller/core/css/distilledpage_ios.css",
1530 r"^native_client_sdk"))
1531 file_filter = lambda f: input_api.FilterSourceFile(
1532 f, white_list=file_inclusion_pattern, black_list=black_list)
1533 for fpath in input_api.AffectedFiles(file_filter=file_filter):
1534 for line_num, line in fpath.ChangedContents():
1535 for (deprecated_value, value) in _DEPRECATED_CSS:
1536 if deprecated_value in line:
1537 results.append(output_api.PresubmitError(
1538 "%s:%d: Use of deprecated CSS %s, use %s instead" %
1539 (fpath.LocalPath(), line_num, deprecated_value, value)))
1540 return results
1543 _DEPRECATED_JS = [
1544 ( "__lookupGetter__", "Object.getOwnPropertyDescriptor" ),
1545 ( "__defineGetter__", "Object.defineProperty" ),
1546 ( "__defineSetter__", "Object.defineProperty" ),
1549 def _CheckNoDeprecatedJS(input_api, output_api):
1550 """Make sure that we don't use deprecated JS in Chrome code."""
1551 results = []
1552 file_inclusion_pattern = (r".+\.js$",) # TODO(dbeam): .html?
1553 black_list = (_EXCLUDED_PATHS + _TEST_CODE_EXCLUDED_PATHS +
1554 input_api.DEFAULT_BLACK_LIST)
1555 file_filter = lambda f: input_api.FilterSourceFile(
1556 f, white_list=file_inclusion_pattern, black_list=black_list)
1557 for fpath in input_api.AffectedFiles(file_filter=file_filter):
1558 for lnum, line in fpath.ChangedContents():
1559 for (deprecated, replacement) in _DEPRECATED_JS:
1560 if deprecated in line:
1561 results.append(output_api.PresubmitError(
1562 "%s:%d: Use of deprecated JS %s, use %s instead" %
1563 (fpath.LocalPath(), lnum, deprecated, replacement)))
1564 return results
1567 def _AndroidSpecificOnUploadChecks(input_api, output_api):
1568 """Groups checks that target android code."""
1569 results = []
1570 results.extend(_CheckAndroidCrLogUsage(input_api, output_api))
1571 results.extend(_CheckAndroidToastUsage(input_api, output_api))
1572 return results
1575 def _CommonChecks(input_api, output_api):
1576 """Checks common to both upload and commit."""
1577 results = []
1578 results.extend(input_api.canned_checks.PanProjectChecks(
1579 input_api, output_api,
1580 excluded_paths=_EXCLUDED_PATHS + _TESTRUNNER_PATHS))
1581 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
1582 results.extend(
1583 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
1584 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
1585 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
1586 results.extend(_CheckNoNewWStrings(input_api, output_api))
1587 results.extend(_CheckNoDEPSGIT(input_api, output_api))
1588 results.extend(_CheckNoBannedFunctions(input_api, output_api))
1589 results.extend(_CheckNoPragmaOnce(input_api, output_api))
1590 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
1591 results.extend(_CheckUnwantedDependencies(input_api, output_api))
1592 results.extend(_CheckFilePermissions(input_api, output_api))
1593 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
1594 results.extend(_CheckIncludeOrder(input_api, output_api))
1595 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
1596 results.extend(_CheckPatchFiles(input_api, output_api))
1597 results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
1598 results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
1599 results.extend(_CheckForInvalidOSMacros(input_api, output_api))
1600 results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api))
1601 # TODO(danakj): Remove this when base/move.h is removed.
1602 results.extend(_CheckForUsingSideEffectsOfPass(input_api, output_api))
1603 results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1604 results.extend(
1605 input_api.canned_checks.CheckChangeHasNoTabs(
1606 input_api,
1607 output_api,
1608 source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
1609 results.extend(_CheckSpamLogging(input_api, output_api))
1610 results.extend(_CheckForAnonymousVariables(input_api, output_api))
1611 results.extend(_CheckCygwinShell(input_api, output_api))
1612 results.extend(_CheckUserActionUpdate(input_api, output_api))
1613 results.extend(_CheckNoDeprecatedCSS(input_api, output_api))
1614 results.extend(_CheckNoDeprecatedJS(input_api, output_api))
1615 results.extend(_CheckParseErrors(input_api, output_api))
1616 results.extend(_CheckForIPCRules(input_api, output_api))
1617 results.extend(_CheckForCopyrightedCode(input_api, output_api))
1618 results.extend(_CheckForWindowsLineEndings(input_api, output_api))
1619 results.extend(_CheckSingletonInHeaders(input_api, output_api))
1621 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
1622 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
1623 input_api, output_api,
1624 input_api.PresubmitLocalPath(),
1625 whitelist=[r'^PRESUBMIT_test\.py$']))
1626 return results
1629 def _CheckAuthorizedAuthor(input_api, output_api):
1630 """For non-googler/chromites committers, verify the author's email address is
1631 in AUTHORS.
1633 # TODO(maruel): Add it to input_api?
1634 import fnmatch
1636 author = input_api.change.author_email
1637 if not author:
1638 input_api.logging.info('No author, skipping AUTHOR check')
1639 return []
1640 authors_path = input_api.os_path.join(
1641 input_api.PresubmitLocalPath(), 'AUTHORS')
1642 valid_authors = (
1643 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
1644 for line in open(authors_path))
1645 valid_authors = [item.group(1).lower() for item in valid_authors if item]
1646 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
1647 input_api.logging.info('Valid authors are %s', ', '.join(valid_authors))
1648 return [output_api.PresubmitPromptWarning(
1649 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
1650 '\n'
1651 'http://www.chromium.org/developers/contributing-code and read the '
1652 '"Legal" section\n'
1653 'If you are a chromite, verify the contributor signed the CLA.') %
1654 author)]
1655 return []
1658 def _CheckPatchFiles(input_api, output_api):
1659 problems = [f.LocalPath() for f in input_api.AffectedFiles()
1660 if f.LocalPath().endswith(('.orig', '.rej'))]
1661 if problems:
1662 return [output_api.PresubmitError(
1663 "Don't commit .rej and .orig files.", problems)]
1664 else:
1665 return []
1668 def _DidYouMeanOSMacro(bad_macro):
1669 try:
1670 return {'A': 'OS_ANDROID',
1671 'B': 'OS_BSD',
1672 'C': 'OS_CHROMEOS',
1673 'F': 'OS_FREEBSD',
1674 'L': 'OS_LINUX',
1675 'M': 'OS_MACOSX',
1676 'N': 'OS_NACL',
1677 'O': 'OS_OPENBSD',
1678 'P': 'OS_POSIX',
1679 'S': 'OS_SOLARIS',
1680 'W': 'OS_WIN'}[bad_macro[3].upper()]
1681 except KeyError:
1682 return ''
1685 def _CheckForInvalidOSMacrosInFile(input_api, f):
1686 """Check for sensible looking, totally invalid OS macros."""
1687 preprocessor_statement = input_api.re.compile(r'^\s*#')
1688 os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
1689 results = []
1690 for lnum, line in f.ChangedContents():
1691 if preprocessor_statement.search(line):
1692 for match in os_macro.finditer(line):
1693 if not match.group(1) in _VALID_OS_MACROS:
1694 good = _DidYouMeanOSMacro(match.group(1))
1695 did_you_mean = ' (did you mean %s?)' % good if good else ''
1696 results.append(' %s:%d %s%s' % (f.LocalPath(),
1697 lnum,
1698 match.group(1),
1699 did_you_mean))
1700 return results
1703 def _CheckForInvalidOSMacros(input_api, output_api):
1704 """Check all affected files for invalid OS macros."""
1705 bad_macros = []
1706 for f in input_api.AffectedFiles():
1707 if not f.LocalPath().endswith(('.py', '.js', '.html', '.css')):
1708 bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
1710 if not bad_macros:
1711 return []
1713 return [output_api.PresubmitError(
1714 'Possibly invalid OS macro[s] found. Please fix your code\n'
1715 'or add your macro to src/PRESUBMIT.py.', bad_macros)]
1718 def _CheckForInvalidIfDefinedMacrosInFile(input_api, f):
1719 """Check all affected files for invalid "if defined" macros."""
1720 ALWAYS_DEFINED_MACROS = (
1721 "TARGET_CPU_PPC",
1722 "TARGET_CPU_PPC64",
1723 "TARGET_CPU_68K",
1724 "TARGET_CPU_X86",
1725 "TARGET_CPU_ARM",
1726 "TARGET_CPU_MIPS",
1727 "TARGET_CPU_SPARC",
1728 "TARGET_CPU_ALPHA",
1729 "TARGET_IPHONE_SIMULATOR",
1730 "TARGET_OS_EMBEDDED",
1731 "TARGET_OS_IPHONE",
1732 "TARGET_OS_MAC",
1733 "TARGET_OS_UNIX",
1734 "TARGET_OS_WIN32",
1736 ifdef_macro = input_api.re.compile(r'^\s*#.*(?:ifdef\s|defined\()([^\s\)]+)')
1737 results = []
1738 for lnum, line in f.ChangedContents():
1739 for match in ifdef_macro.finditer(line):
1740 if match.group(1) in ALWAYS_DEFINED_MACROS:
1741 always_defined = ' %s is always defined. ' % match.group(1)
1742 did_you_mean = 'Did you mean \'#if %s\'?' % match.group(1)
1743 results.append(' %s:%d %s\n\t%s' % (f.LocalPath(),
1744 lnum,
1745 always_defined,
1746 did_you_mean))
1747 return results
1750 def _CheckForInvalidIfDefinedMacros(input_api, output_api):
1751 """Check all affected files for invalid "if defined" macros."""
1752 bad_macros = []
1753 for f in input_api.AffectedFiles():
1754 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
1755 bad_macros.extend(_CheckForInvalidIfDefinedMacrosInFile(input_api, f))
1757 if not bad_macros:
1758 return []
1760 return [output_api.PresubmitError(
1761 'Found ifdef check on always-defined macro[s]. Please fix your code\n'
1762 'or check the list of ALWAYS_DEFINED_MACROS in src/PRESUBMIT.py.',
1763 bad_macros)]
1766 def _CheckForUsingSideEffectsOfPass(input_api, output_api):
1767 """Check all affected files for using side effects of Pass."""
1768 errors = []
1769 for f in input_api.AffectedFiles():
1770 if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')):
1771 for lnum, line in f.ChangedContents():
1772 # Disallow Foo(*my_scoped_thing.Pass()); See crbug.com/418297.
1773 if input_api.re.search(r'\*[a-zA-Z0-9_]+\.Pass\(\)', line):
1774 errors.append(output_api.PresubmitError(
1775 ('%s:%d uses *foo.Pass() to delete the contents of scoped_ptr. ' +
1776 'See crbug.com/418297.') % (f.LocalPath(), lnum)))
1777 return errors
1780 def _CheckForIPCRules(input_api, output_api):
1781 """Check for same IPC rules described in
1782 http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
1784 base_pattern = r'IPC_ENUM_TRAITS\('
1785 inclusion_pattern = input_api.re.compile(r'(%s)' % base_pattern)
1786 comment_pattern = input_api.re.compile(r'//.*(%s)' % base_pattern)
1788 problems = []
1789 for f in input_api.AffectedSourceFiles(None):
1790 local_path = f.LocalPath()
1791 if not local_path.endswith('.h'):
1792 continue
1793 for line_number, line in f.ChangedContents():
1794 if inclusion_pattern.search(line) and not comment_pattern.search(line):
1795 problems.append(
1796 '%s:%d\n %s' % (local_path, line_number, line.strip()))
1798 if problems:
1799 return [output_api.PresubmitPromptWarning(
1800 _IPC_ENUM_TRAITS_DEPRECATED, problems)]
1801 else:
1802 return []
1805 def _CheckForWindowsLineEndings(input_api, output_api):
1806 """Check source code and known ascii text files for Windows style line
1807 endings.
1809 known_text_files = r'.*\.(txt|html|htm|mhtml|py|gyp|gypi|gn|isolate)$'
1811 file_inclusion_pattern = (
1812 known_text_files,
1813 r'.+%s' % _IMPLEMENTATION_EXTENSIONS
1816 filter = lambda f: input_api.FilterSourceFile(
1817 f, white_list=file_inclusion_pattern, black_list=None)
1818 files = [f.LocalPath() for f in
1819 input_api.AffectedSourceFiles(filter)]
1821 problems = []
1823 for file in files:
1824 fp = open(file, 'r')
1825 for line in fp:
1826 if line.endswith('\r\n'):
1827 problems.append(file)
1828 break
1829 fp.close()
1831 if problems:
1832 return [output_api.PresubmitPromptWarning('Are you sure that you want '
1833 'these files to contain Windows style line endings?\n' +
1834 '\n'.join(problems))]
1836 return []
1839 def CheckChangeOnUpload(input_api, output_api):
1840 results = []
1841 results.extend(_CommonChecks(input_api, output_api))
1842 results.extend(_CheckValidHostsInDEPS(input_api, output_api))
1843 results.extend(_CheckJavaStyle(input_api, output_api))
1844 results.extend(
1845 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
1846 results.extend(_CheckUmaHistogramChanges(input_api, output_api))
1847 results.extend(_AndroidSpecificOnUploadChecks(input_api, output_api))
1848 return results
1851 def GetTryServerMasterForBot(bot):
1852 """Returns the Try Server master for the given bot.
1854 It tries to guess the master from the bot name, but may still fail
1855 and return None. There is no longer a default master.
1857 # Potentially ambiguous bot names are listed explicitly.
1858 master_map = {
1859 'chromium_presubmit': 'tryserver.chromium.linux',
1860 'blink_presubmit': 'tryserver.chromium.linux',
1861 'tools_build_presubmit': 'tryserver.chromium.linux',
1863 master = master_map.get(bot)
1864 if not master:
1865 if 'linux' in bot or 'android' in bot or 'presubmit' in bot:
1866 master = 'tryserver.chromium.linux'
1867 elif 'win' in bot:
1868 master = 'tryserver.chromium.win'
1869 elif 'mac' in bot or 'ios' in bot:
1870 master = 'tryserver.chromium.mac'
1871 return master
1874 def GetDefaultTryConfigs(bots):
1875 """Returns a list of ('bot', set(['tests']), filtered by [bots].
1878 builders_and_tests = dict((bot, set(['defaulttests'])) for bot in bots)
1880 # Build up the mapping from tryserver master to bot/test.
1881 out = dict()
1882 for bot, tests in builders_and_tests.iteritems():
1883 out.setdefault(GetTryServerMasterForBot(bot), {})[bot] = tests
1884 return out
1887 def CheckChangeOnCommit(input_api, output_api):
1888 results = []
1889 results.extend(_CommonChecks(input_api, output_api))
1890 # TODO(thestig) temporarily disabled, doesn't work in third_party/
1891 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
1892 # input_api, output_api, sources))
1893 # Make sure the tree is 'open'.
1894 results.extend(input_api.canned_checks.CheckTreeIsOpen(
1895 input_api,
1896 output_api,
1897 json_url='http://chromium-status.appspot.com/current?format=json'))
1899 results.extend(input_api.canned_checks.CheckChangeHasBugField(
1900 input_api, output_api))
1901 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1902 input_api, output_api))
1903 return results
1906 def GetPreferredTryMasters(project, change):
1907 import json
1908 import os.path
1909 import platform
1910 import subprocess
1912 cq_config_path = os.path.join(
1913 change.RepositoryRoot(), 'infra', 'config', 'cq.cfg')
1914 # commit_queue.py below is a script in depot_tools directory, which has a
1915 # 'builders' command to retrieve a list of CQ builders from the CQ config.
1916 is_win = platform.system() == 'Windows'
1917 masters = json.loads(subprocess.check_output(
1918 ['commit_queue', 'builders', cq_config_path], shell=is_win))
1920 try_config = {}
1921 for master in masters:
1922 try_config.setdefault(master, {})
1923 for builder in masters[master]:
1924 # Do not trigger presubmit builders, since they're likely to fail
1925 # (e.g. OWNERS checks before finished code review), and we're
1926 # running local presubmit anyway.
1927 if 'presubmit' not in builder:
1928 try_config[master][builder] = ['defaulttests']
1930 return try_config