Encode method and error message inside LevelDB::Status message
[chromium-blink-merge.git] / PRESUBMIT.py
blob3418265fffbbabb901e9108a69bdcf43939542f2
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.
9 """
12 import re
13 import subprocess
14 import sys
17 _EXCLUDED_PATHS = (
18 r"^breakpad[\\\/].*",
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[\\\/].*",
23 r"^skia[\\\/].*",
24 r"^v8[\\\/].*",
25 r".*MakeFile$",
26 r".+_autogen\.h$",
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
35 # (best effort).
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 = (
64 'addTrackingRect:',
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',
70 False,
73 'NSTrackingArea',
75 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
76 'instead.',
77 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
79 False,
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',
88 True,
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',
97 True,
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',
106 True,
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',
115 True,
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',
124 True,
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',
133 True,
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.
143 'FRIEND_TEST(',
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.',
148 False,
152 'ScopedAllowIO',
154 'New code should not use ScopedAllowIO. Post a task to the blocking',
155 'pool or the FILE thread instead.',
157 True,
159 r"^content[\\\/]shell[\\\/]shell_browser_main\.cc$",
160 r"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
164 'SkRefPtr',
166 'The use of SkRefPtr is prohibited. ',
167 'Please use skia::RefPtr instead.'
169 True,
173 'SkAutoRef',
175 'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
176 'Please use skia::RefPtr instead.'
178 True,
182 'SkAutoTUnref',
184 'The use of SkAutoTUnref is dangerous because it implicitly ',
185 'converts to a raw pointer. Please use skia::RefPtr instead.'
187 True,
191 'SkAutoUnref',
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.'
197 True,
203 _VALID_OS_MACROS = (
204 # Please keep sorted.
205 'OS_ANDROID',
206 'OS_BSD',
207 'OS_CAT', # For testing.
208 'OS_CHROMEOS',
209 'OS_FREEBSD',
210 'OS_IOS',
211 'OS_LINUX',
212 'OS_MACOSX',
213 'OS_NACL',
214 'OS_OPENBSD',
215 'OS_POSIX',
216 'OS_SOLARIS',
217 'OS_WIN',
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(
243 affected_file,
244 white_list=(file_inclusion_pattern, ),
245 black_list=black_list)
247 problems = []
248 for f in input_api.AffectedSourceFiles(FilterFile):
249 local_path = f.LocalPath()
250 lines = input_api.ReadFile(f).splitlines()
251 line_number = 0
252 for line in lines:
253 if (inclusion_pattern.search(line) and
254 not exclusion_pattern.search(line)):
255 problems.append(
256 '%s:%d\n %s' % (local_path, line_number, line.strip()))
257 line_number += 1
259 if problems:
260 return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
261 else:
262 return []
265 def _CheckNoIOStreamInHeaders(input_api, output_api):
266 """Checks to make sure no .h files include <iostream>."""
267 files = []
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'):
272 continue
273 contents = input_api.ReadFile(f)
274 if pattern.search(contents):
275 files.append(f)
277 if len(files):
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',
282 files) ]
283 return []
286 def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
287 """Checks to make sure no source files use UNIT_TEST"""
288 problems = []
289 for f in input_api.AffectedFiles():
290 if (not f.LocalPath().endswith(('.cc', '.mm'))):
291 continue
293 for line_num, line in f.ChangedContents():
294 if 'UNIT_TEST' in line:
295 problems.append(' %s:%d' % (f.LocalPath(), line_num))
297 if not problems:
298 return []
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."""
305 problems = []
306 for f in input_api.AffectedFiles():
307 if (not f.LocalPath().endswith(('.cc', '.h')) or
308 f.LocalPath().endswith('test.cc')):
309 continue
311 allowWString = False
312 for line_num, line in f.ChangedContents():
313 if 'presubmit: allow wstring' in line:
314 allowWString = True
315 elif not allowWString and 'wstring' in line:
316 problems.append(' %s:%d' % (f.LocalPath(), line_num))
317 allowWString = False
318 else:
319 allowWString = False
321 if not problems:
322 return []
323 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
324 ' If you are calling a cross-platform API that accepts a wstring, '
325 'fix the API.\n' +
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'
336 'overwritten.\n'
337 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
338 'for more information')]
339 return []
342 def _CheckNoBannedFunctions(input_api, output_api):
343 """Make sure that banned functions are not used."""
344 warnings = []
345 errors = []
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:
352 problems = warnings;
353 if error:
354 problems = errors;
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):
367 return True
368 return False
369 if IsBlacklisted(f, excluded_paths):
370 continue
371 if func_name in line:
372 problems = warnings;
373 if error:
374 problems = errors;
375 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
376 for message_line in message:
377 problems.append(' %s' % message_line)
379 result = []
380 if (warnings):
381 result.append(output_api.PresubmitPromptWarning(
382 'Banned functions were used.\n' + '\n'.join(warnings)))
383 if (errors):
384 result.append(output_api.PresubmitError(
385 'Banned functions were used.\n' + '\n'.join(errors)))
386 return result
389 def _CheckNoPragmaOnce(input_api, output_api):
390 """Make sure that banned functions are not used."""
391 files = []
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'):
396 continue
397 contents = input_api.ReadFile(f)
398 if pattern.search(contents):
399 files.append(f)
401 if files:
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',
405 files)]
406 return []
409 def _CheckNoTrinaryTrueFalse(input_api, output_api):
410 """Checks to make sure we don't introduce use of foo ? true : false."""
411 problems = []
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')):
415 continue
417 for line_num, line in f.ChangedContents():
418 if pattern.match(line):
419 problems.append(' %s:%d' % (f.LocalPath(), line_num))
421 if not problems:
422 return []
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
431 warning.
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
437 try:
438 sys.path = sys.path + [input_api.os_path.join(
439 input_api.PresubmitLocalPath(), 'tools', 'checkdeps')]
440 import checkdeps
441 from cpp_checker import CppChecker
442 from rules import Rule
443 finally:
444 # Restore sys.path to what it was before.
445 sys.path = original_sys_path
447 added_includes = []
448 for f in input_api.AffectedFiles():
449 if not CppChecker.IsCppFile(f.LocalPath()):
450 continue
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(
460 added_includes):
461 description_with_path = '%s\n %s' % (path, rule_description)
462 if rule_type == Rule.DISALLOW:
463 error_descriptions.append(description_with_path)
464 else:
465 warning_descriptions.append(description_with_path)
467 results = []
468 if error_descriptions:
469 results.append(output_api.PresubmitError(
470 'You added one or more #includes that violate checkdeps rules.',
471 error_descriptions))
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))
478 return results
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()]
487 errors = []
488 (errors, stderrdata) = subprocess.Popen(args).communicate()
490 results = []
491 if errors:
492 results.append(output_api.PresubmitError('checkperms.py failed.',
493 errors))
494 return results
497 def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
498 """Makes sure we don't include ui/aura/window_property.h
499 in header files.
501 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
502 errors = []
503 for f in input_api.AffectedFiles():
504 if not f.LocalPath().endswith('.h'):
505 continue
506 for line_num, line in f.ChangedContents():
507 if pattern.match(line):
508 errors.append(' %s:%d' % (f.LocalPath(), line_num))
510 results = []
511 if errors:
512 results.append(output_api.PresubmitError(
513 'Header files should not include ui/aura/window_property.h', errors))
514 return results
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
533 previous_line = ''
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))
554 else:
555 problem_linenums.append(line_num)
556 previous_line = line
557 previous_line_num = line_num
559 warnings = []
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))
563 return warnings
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
577 # check.
578 uncheckable_includes_pattern = input_api.re.compile(
579 r'\s*#include '
580 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
582 contents = f.NewContents()
583 warnings = []
584 line_num = 0
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:
591 line_num += 1
592 if system_include_pattern.match(line):
593 # No special first include -> process the line again along with normal
594 # includes.
595 line_num -= 1
596 break
597 match = custom_include_pattern.match(line)
598 if match:
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
604 # includes.
605 line_num -= 1
606 break
608 # Split into scopes: Each region between #if and #endif is its own scope.
609 scopes = []
610 current_scope = []
611 for line in contents[line_num:]:
612 line_num += 1
613 if uncheckable_includes_pattern.match(line):
614 return []
615 if if_pattern.match(line):
616 scopes.append(current_scope)
617 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)
624 for scope in scopes:
625 warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
626 changed_linenums))
627 return warnings
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.
642 warnings = []
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))
648 results = []
649 if warnings:
650 results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
651 warnings))
652 return results
655 def _CheckForVersionControlConflictsInFile(input_api, f):
656 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
657 errors = []
658 for line_num, line in f.ChangedContents():
659 if pattern.match(line):
660 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
661 return errors
664 def _CheckForVersionControlConflicts(input_api, output_api):
665 """Usually this is not intentional and will cause a compile failure."""
666 errors = []
667 for f in input_api.AffectedFiles():
668 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
670 results = []
671 if errors:
672 results.append(output_api.PresubmitError(
673 'Version control conflict markers found, please resolve.', errors))
674 return results
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(
685 affected_file,
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))
698 if problems:
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)',
702 [' %s:%d: %s' % (
703 problem[0], problem[1], problem[2]) for problem in problems])]
704 else:
705 return []
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$')
712 errors = []
713 for f in input_api.AffectedFiles(include_deletes=False):
714 if pattern.match(f.LocalPath()):
715 errors.append(' %s' % f.LocalPath())
717 results = []
718 if errors:
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))
723 return results
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()
737 for line_num, line
738 in f.ChangedContents())
739 if not changed_lines:
740 return []
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)
751 if m:
752 virtual_depended_on_files.add('%s/DEPS' % m.group(1))
754 if not virtual_depended_on_files:
755 return []
757 if input_api.is_committing:
758 if input_api.tbr:
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
766 else:
767 output = output_api.PresubmitNotifyResult
769 owners_db = input_api.owners_db
770 owner_email, reviewers = input_api.canned_checks._RietveldOwnerAndReviewers(
771 input_api,
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:
784 output_list = [
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 [])))
792 return output_list
794 return []
797 def _CommonChecks(input_api, output_api):
798 """Checks common to both upload and commit."""
799 results = []
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))
803 results.extend(
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$']))
828 return results
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', '')
839 if not appdata:
840 return [output_api.PresubmitError('%APPDATA% is not configured.')]
841 path = join(appdata, 'Subversion', 'config')
842 else:
843 home = input_api.environ.get('HOME', '')
844 if not home:
845 return [output_api.PresubmitError('$HOME is not configured.')]
846 path = join(home, '.subversion', 'config')
848 error_msg = (
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')
855 try:
856 lines = open(path, 'r').read().splitlines()
857 # Make sure auto-props is enabled and check for 2 Chromium standard
858 # auto-prop.
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):
862 return [
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):
868 return [
869 output_api.PresubmitNotifyResult(
870 'Can\'t find your subversion config file.\n' + error_msg)
872 return []
875 def _CheckAuthorizedAuthor(input_api, output_api):
876 """For non-googler/chromites committers, verify the author's email address is
877 in AUTHORS.
879 # TODO(maruel): Add it to input_api?
880 import fnmatch
882 author = input_api.change.author_email
883 if not author:
884 input_api.logging.info('No author, skipping AUTHOR check')
885 return []
886 authors_path = input_api.os_path.join(
887 input_api.PresubmitLocalPath(), 'AUTHORS')
888 valid_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'
896 '\n'
897 'http://www.chromium.org/developers/contributing-code and read the '
898 '"Legal" section\n'
899 'If you are a chromite, verify the contributor signed the CLA.') %
900 author)]
901 return []
904 def _CheckPatchFiles(input_api, output_api):
905 problems = [f.LocalPath() for f in input_api.AffectedFiles()
906 if f.LocalPath().endswith(('.orig', '.rej'))]
907 if problems:
908 return [output_api.PresubmitError(
909 "Don't commit .rej and .orig files.", problems)]
910 else:
911 return []
914 def _DidYouMeanOSMacro(bad_macro):
915 try:
916 return {'A': 'OS_ANDROID',
917 'B': 'OS_BSD',
918 'C': 'OS_CHROMEOS',
919 'F': 'OS_FREEBSD',
920 'L': 'OS_LINUX',
921 'M': 'OS_MACOSX',
922 'N': 'OS_NACL',
923 'O': 'OS_OPENBSD',
924 'P': 'OS_POSIX',
925 'S': 'OS_SOLARIS',
926 'W': 'OS_WIN'}[bad_macro[3].upper()]
927 except KeyError:
928 return ''
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_[^)]+)\)')
935 results = []
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(),
943 lnum,
944 match.group(1),
945 did_you_mean))
946 return results
949 def _CheckForInvalidOSMacros(input_api, output_api):
950 """Check all affected files for invalid OS macros."""
951 bad_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))
956 if not bad_macros:
957 return []
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):
965 results = []
966 results.extend(_CommonChecks(input_api, output_api))
967 return results
970 def CheckChangeOnCommit(input_api, output_api):
971 results = []
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(
978 input_api,
979 output_api,
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))
991 return results
994 def GetPreferredTrySlaves(project, change):
995 files = change.LocalPaths()
997 if not files or all(re.search(r'[\\/]OWNERS$', f) for f in files):
998 return []
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']
1011 trybots = [
1012 'android_clang_dbg',
1013 'android_dbg',
1014 'ios_dbg_simulator',
1015 'ios_rel_device',
1016 'linux_asan',
1017 'linux_aura',
1018 'linux_chromeos',
1019 'linux_clang:compile',
1020 'linux_rel',
1021 'mac_asan',
1022 'mac_rel',
1023 'mac:compile',
1024 'win7_aura',
1025 'win_rel',
1026 'win: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']
1034 return trybots