Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / PRESUBMIT.py
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.
4
5 """Top-level presubmit script for Chromium.
6
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8 for more details about the presubmit API built into gcl.
9 """
10
11
12 import re
13 import sys
14
15
16 _EXCLUDED_PATHS = (
17     r"^breakpad[\\\/].*",
18     r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
19     r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
20     r"^native_client_sdk[\\\/]src[\\\/]tools[\\\/].*.mk",
21     r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
22     r"^skia[\\\/].*",
23     r"^v8[\\\/].*",
24     r".*MakeFile$",
25     r".+_autogen\.h$",
26     r".+[\\\/]pnacl_shim\.c$",
27     r"^gpu[\\\/]config[\\\/].*_list_json\.cc$",
28     r"^chrome[\\\/]browser[\\\/]resources[\\\/]pdf[\\\/]index.js"
29 )
30
31 # TestRunner and NetscapePlugIn library is temporarily excluded from pan-project
32 # checks until it's transitioned to chromium coding style.
33 _TESTRUNNER_PATHS = (
34     r"^content[\\\/]shell[\\\/]renderer[\\\/]test_runner[\\\/].*",
35     r"^content[\\\/]shell[\\\/]tools[\\\/]plugin[\\\/].*",
36 )
37
38 # Fragment of a regular expression that matches C++ and Objective-C++
39 # implementation files.
40 _IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
41
42 # Regular expression that matches code only used for test binaries
43 # (best effort).
44 _TEST_CODE_EXCLUDED_PATHS = (
45     r'.*[/\\](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
46     r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
47     r'.+_(api|browser|kif|perf|pixel|unit|ui)?test(_[a-z]+)?%s' %
48         _IMPLEMENTATION_EXTENSIONS,
49     r'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS,
50     r'.*[/\\](test|tool(s)?)[/\\].*',
51     # content_shell is used for running layout tests.
52     r'content[/\\]shell[/\\].*',
53     # At request of folks maintaining this folder.
54     r'chrome[/\\]browser[/\\]automation[/\\].*',
55     # Non-production example code.
56     r'mojo[/\\]examples[/\\].*',
57 )
58
59 _TEST_ONLY_WARNING = (
60     'You might be calling functions intended only for testing from\n'
61     'production code.  It is OK to ignore this warning if you know what\n'
62     'you are doing, as the heuristics used to detect the situation are\n'
63     'not perfect.  The commit queue will not block on this warning.')
64
65
66 _INCLUDE_ORDER_WARNING = (
67     'Your #include order seems to be broken. Send mail to\n'
68     'marja@chromium.org if this is not the case.')
69
70
71 _BANNED_OBJC_FUNCTIONS = (
72     (
73       'addTrackingRect:',
74       (
75        'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
76        'prohibited. Please use CrTrackingArea instead.',
77        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
78       ),
79       False,
80     ),
81     (
82       r'/NSTrackingArea\W',
83       (
84        'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
85        'instead.',
86        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
87       ),
88       False,
89     ),
90     (
91       'convertPointFromBase:',
92       (
93        'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
94        'Please use |convertPoint:(point) fromView:nil| instead.',
95        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
96       ),
97       True,
98     ),
99     (
100       'convertPointToBase:',
101       (
102        'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
103        'Please use |convertPoint:(point) toView:nil| instead.',
104        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
105       ),
106       True,
107     ),
108     (
109       'convertRectFromBase:',
110       (
111        'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
112        'Please use |convertRect:(point) fromView:nil| instead.',
113        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
114       ),
115       True,
116     ),
117     (
118       'convertRectToBase:',
119       (
120        'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
121        'Please use |convertRect:(point) toView:nil| instead.',
122        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
123       ),
124       True,
125     ),
126     (
127       'convertSizeFromBase:',
128       (
129        'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
130        'Please use |convertSize:(point) fromView:nil| instead.',
131        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
132       ),
133       True,
134     ),
135     (
136       'convertSizeToBase:',
137       (
138        'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
139        'Please use |convertSize:(point) toView:nil| instead.',
140        'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
141       ),
142       True,
143     ),
144 )
145
146
147 _BANNED_CPP_FUNCTIONS = (
148     # Make sure that gtest's FRIEND_TEST() macro is not used; the
149     # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
150     # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
151     (
152       'FRIEND_TEST(',
153       (
154        'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
155        'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
156       ),
157       False,
158       (),
159     ),
160     (
161       'ScopedAllowIO',
162       (
163        'New code should not use ScopedAllowIO. Post a task to the blocking',
164        'pool or the FILE thread instead.',
165       ),
166       True,
167       (
168         r"^components[\\\/]breakpad[\\\/]app[\\\/]breakpad_mac\.mm$",
169         r"^content[\\\/]shell[\\\/]browser[\\\/]shell_browser_main\.cc$",
170         r"^content[\\\/]shell[\\\/]browser[\\\/]shell_message_filter\.cc$",
171         r"^mojo[\\\/]system[\\\/]raw_shared_buffer_posix\.cc$",
172         r"^net[\\\/]disk_cache[\\\/]cache_util\.cc$",
173       ),
174     ),
175     (
176       'SkRefPtr',
177       (
178         'The use of SkRefPtr is prohibited. ',
179         'Please use skia::RefPtr instead.'
180       ),
181       True,
182       (),
183     ),
184     (
185       'SkAutoRef',
186       (
187         'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
188         'Please use skia::RefPtr instead.'
189       ),
190       True,
191       (),
192     ),
193     (
194       'SkAutoTUnref',
195       (
196         'The use of SkAutoTUnref is dangerous because it implicitly ',
197         'converts to a raw pointer. Please use skia::RefPtr instead.'
198       ),
199       True,
200       (),
201     ),
202     (
203       'SkAutoUnref',
204       (
205         'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
206         'because it implicitly converts to a raw pointer. ',
207         'Please use skia::RefPtr instead.'
208       ),
209       True,
210       (),
211     ),
212     (
213       r'/HANDLE_EINTR\(.*close',
214       (
215        'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
216        'descriptor will be closed, and it is incorrect to retry the close.',
217        'Either call close directly and ignore its return value, or wrap close',
218        'in IGNORE_EINTR to use its return value. See http://crbug.com/269623'
219       ),
220       True,
221       (),
222     ),
223     (
224       r'/IGNORE_EINTR\((?!.*close)',
225       (
226        'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
227        'calls, use HANDLE_EINTR. See http://crbug.com/269623',
228       ),
229       True,
230       (
231         # Files that #define IGNORE_EINTR.
232         r'^base[\\\/]posix[\\\/]eintr_wrapper\.h$',
233         r'^ppapi[\\\/]tests[\\\/]test_broker\.cc$',
234       ),
235     ),
236     (
237       r'/v8::Extension\(',
238       (
239         'Do not introduce new v8::Extensions into the code base, use',
240         'gin::Wrappable instead. See http://crbug.com/334679',
241       ),
242       True,
243       (
244         r'extensions[/\\]renderer[/\\]safe_builtins\.*',
245       ),
246     ),
247 )
248
249
250 _VALID_OS_MACROS = (
251     # Please keep sorted.
252     'OS_ANDROID',
253     'OS_ANDROID_HOST',
254     'OS_BSD',
255     'OS_CAT',       # For testing.
256     'OS_CHROMEOS',
257     'OS_FREEBSD',
258     'OS_IOS',
259     'OS_LINUX',
260     'OS_MACOSX',
261     'OS_NACL',
262     'OS_OPENBSD',
263     'OS_POSIX',
264     'OS_QNX',
265     'OS_SOLARIS',
266     'OS_WIN',
267 )
268
269
270 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
271   """Attempts to prevent use of functions intended only for testing in
272   non-testing code. For now this is just a best-effort implementation
273   that ignores header files and may have some false positives. A
274   better implementation would probably need a proper C++ parser.
275   """
276   # We only scan .cc files and the like, as the declaration of
277   # for-testing functions in header files are hard to distinguish from
278   # calls to such functions without a proper C++ parser.
279   file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
280
281   base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
282   inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
283   comment_pattern = input_api.re.compile(r'//.*%s' % base_function_pattern)
284   exclusion_pattern = input_api.re.compile(
285     r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
286       base_function_pattern, base_function_pattern))
287
288   def FilterFile(affected_file):
289     black_list = (_EXCLUDED_PATHS +
290                   _TEST_CODE_EXCLUDED_PATHS +
291                   input_api.DEFAULT_BLACK_LIST)
292     return input_api.FilterSourceFile(
293       affected_file,
294       white_list=(file_inclusion_pattern, ),
295       black_list=black_list)
296
297   problems = []
298   for f in input_api.AffectedSourceFiles(FilterFile):
299     local_path = f.LocalPath()
300     for line_number, line in f.ChangedContents():
301       if (inclusion_pattern.search(line) and
302           not comment_pattern.search(line) and
303           not exclusion_pattern.search(line)):
304         problems.append(
305           '%s:%d\n    %s' % (local_path, line_number, line.strip()))
306
307   if problems:
308     return [output_api.PresubmitPromptOrNotify(_TEST_ONLY_WARNING, problems)]
309   else:
310     return []
311
312
313 def _CheckNoIOStreamInHeaders(input_api, output_api):
314   """Checks to make sure no .h files include <iostream>."""
315   files = []
316   pattern = input_api.re.compile(r'^#include\s*<iostream>',
317                                  input_api.re.MULTILINE)
318   for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
319     if not f.LocalPath().endswith('.h'):
320       continue
321     contents = input_api.ReadFile(f)
322     if pattern.search(contents):
323       files.append(f)
324
325   if len(files):
326     return [ output_api.PresubmitError(
327         'Do not #include <iostream> in header files, since it inserts static '
328         'initialization into every file including the header. Instead, '
329         '#include <ostream>. See http://crbug.com/94794',
330         files) ]
331   return []
332
333
334 def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
335   """Checks to make sure no source files use UNIT_TEST"""
336   problems = []
337   for f in input_api.AffectedFiles():
338     if (not f.LocalPath().endswith(('.cc', '.mm'))):
339       continue
340
341     for line_num, line in f.ChangedContents():
342       if 'UNIT_TEST ' in line or line.endswith('UNIT_TEST'):
343         problems.append('    %s:%d' % (f.LocalPath(), line_num))
344
345   if not problems:
346     return []
347   return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
348       '\n'.join(problems))]
349
350
351 def _CheckNoNewWStrings(input_api, output_api):
352   """Checks to make sure we don't introduce use of wstrings."""
353   problems = []
354   for f in input_api.AffectedFiles():
355     if (not f.LocalPath().endswith(('.cc', '.h')) or
356         f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h'))):
357       continue
358
359     allowWString = False
360     for line_num, line in f.ChangedContents():
361       if 'presubmit: allow wstring' in line:
362         allowWString = True
363       elif not allowWString and 'wstring' in line:
364         problems.append('    %s:%d' % (f.LocalPath(), line_num))
365         allowWString = False
366       else:
367         allowWString = False
368
369   if not problems:
370     return []
371   return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
372       '  If you are calling a cross-platform API that accepts a wstring, '
373       'fix the API.\n' +
374       '\n'.join(problems))]
375
376
377 def _CheckNoDEPSGIT(input_api, output_api):
378   """Make sure .DEPS.git is never modified manually."""
379   if any(f.LocalPath().endswith('.DEPS.git') for f in
380       input_api.AffectedFiles()):
381     return [output_api.PresubmitError(
382       'Never commit changes to .DEPS.git. This file is maintained by an\n'
383       'automated system based on what\'s in DEPS and your changes will be\n'
384       'overwritten.\n'
385       'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
386       'for more information')]
387   return []
388
389
390 def _CheckNoBannedFunctions(input_api, output_api):
391   """Make sure that banned functions are not used."""
392   warnings = []
393   errors = []
394
395   file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
396   for f in input_api.AffectedFiles(file_filter=file_filter):
397     for line_num, line in f.ChangedContents():
398       for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
399         matched = False
400         if func_name[0:1] == '/':
401           regex = func_name[1:]
402           if input_api.re.search(regex, line):
403             matched = True
404         elif func_name in line:
405             matched = True
406         if matched:
407           problems = warnings;
408           if error:
409             problems = errors;
410           problems.append('    %s:%d:' % (f.LocalPath(), line_num))
411           for message_line in message:
412             problems.append('      %s' % message_line)
413
414   file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
415   for f in input_api.AffectedFiles(file_filter=file_filter):
416     for line_num, line in f.ChangedContents():
417       for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
418         def IsBlacklisted(affected_file, blacklist):
419           local_path = affected_file.LocalPath()
420           for item in blacklist:
421             if input_api.re.match(item, local_path):
422               return True
423           return False
424         if IsBlacklisted(f, excluded_paths):
425           continue
426         matched = False
427         if func_name[0:1] == '/':
428           regex = func_name[1:]
429           if input_api.re.search(regex, line):
430             matched = True
431         elif func_name in line:
432             matched = True
433         if matched:
434           problems = warnings;
435           if error:
436             problems = errors;
437           problems.append('    %s:%d:' % (f.LocalPath(), line_num))
438           for message_line in message:
439             problems.append('      %s' % message_line)
440
441   result = []
442   if (warnings):
443     result.append(output_api.PresubmitPromptWarning(
444         'Banned functions were used.\n' + '\n'.join(warnings)))
445   if (errors):
446     result.append(output_api.PresubmitError(
447         'Banned functions were used.\n' + '\n'.join(errors)))
448   return result
449
450
451 def _CheckNoPragmaOnce(input_api, output_api):
452   """Make sure that banned functions are not used."""
453   files = []
454   pattern = input_api.re.compile(r'^#pragma\s+once',
455                                  input_api.re.MULTILINE)
456   for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
457     if not f.LocalPath().endswith('.h'):
458       continue
459     contents = input_api.ReadFile(f)
460     if pattern.search(contents):
461       files.append(f)
462
463   if files:
464     return [output_api.PresubmitError(
465         'Do not use #pragma once in header files.\n'
466         'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
467         files)]
468   return []
469
470
471 def _CheckNoTrinaryTrueFalse(input_api, output_api):
472   """Checks to make sure we don't introduce use of foo ? true : false."""
473   problems = []
474   pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
475   for f in input_api.AffectedFiles():
476     if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
477       continue
478
479     for line_num, line in f.ChangedContents():
480       if pattern.match(line):
481         problems.append('    %s:%d' % (f.LocalPath(), line_num))
482
483   if not problems:
484     return []
485   return [output_api.PresubmitPromptWarning(
486       'Please consider avoiding the "? true : false" pattern if possible.\n' +
487       '\n'.join(problems))]
488
489
490 def _CheckUnwantedDependencies(input_api, output_api):
491   """Runs checkdeps on #include statements added in this
492   change. Breaking - rules is an error, breaking ! rules is a
493   warning.
494   """
495   # We need to wait until we have an input_api object and use this
496   # roundabout construct to import checkdeps because this file is
497   # eval-ed and thus doesn't have __file__.
498   original_sys_path = sys.path
499   try:
500     sys.path = sys.path + [input_api.os_path.join(
501         input_api.PresubmitLocalPath(), 'tools', 'checkdeps')]
502     import checkdeps
503     from cpp_checker import CppChecker
504     from rules import Rule
505   finally:
506     # Restore sys.path to what it was before.
507     sys.path = original_sys_path
508
509   added_includes = []
510   for f in input_api.AffectedFiles():
511     if not CppChecker.IsCppFile(f.LocalPath()):
512       continue
513
514     changed_lines = [line for line_num, line in f.ChangedContents()]
515     added_includes.append([f.LocalPath(), changed_lines])
516
517   deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
518
519   error_descriptions = []
520   warning_descriptions = []
521   for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
522       added_includes):
523     description_with_path = '%s\n    %s' % (path, rule_description)
524     if rule_type == Rule.DISALLOW:
525       error_descriptions.append(description_with_path)
526     else:
527       warning_descriptions.append(description_with_path)
528
529   results = []
530   if error_descriptions:
531     results.append(output_api.PresubmitError(
532         'You added one or more #includes that violate checkdeps rules.',
533         error_descriptions))
534   if warning_descriptions:
535     results.append(output_api.PresubmitPromptOrNotify(
536         'You added one or more #includes of files that are temporarily\n'
537         'allowed but being removed. Can you avoid introducing the\n'
538         '#include? See relevant DEPS file(s) for details and contacts.',
539         warning_descriptions))
540   return results
541
542
543 def _CheckFilePermissions(input_api, output_api):
544   """Check that all files have their permissions properly set."""
545   if input_api.platform == 'win32':
546     return []
547   args = [sys.executable, 'tools/checkperms/checkperms.py', '--root',
548           input_api.change.RepositoryRoot()]
549   for f in input_api.AffectedFiles():
550     args += ['--file', f.LocalPath()]
551   checkperms = input_api.subprocess.Popen(args,
552                                           stdout=input_api.subprocess.PIPE)
553   errors = checkperms.communicate()[0].strip()
554   if errors:
555     return [output_api.PresubmitError('checkperms.py failed.',
556                                       errors.splitlines())]
557   return []
558
559
560 def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
561   """Makes sure we don't include ui/aura/window_property.h
562   in header files.
563   """
564   pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
565   errors = []
566   for f in input_api.AffectedFiles():
567     if not f.LocalPath().endswith('.h'):
568       continue
569     for line_num, line in f.ChangedContents():
570       if pattern.match(line):
571         errors.append('    %s:%d' % (f.LocalPath(), line_num))
572
573   results = []
574   if errors:
575     results.append(output_api.PresubmitError(
576       'Header files should not include ui/aura/window_property.h', errors))
577   return results
578
579
580 def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
581   """Checks that the lines in scope occur in the right order.
582
583   1. C system files in alphabetical order
584   2. C++ system files in alphabetical order
585   3. Project's .h files
586   """
587
588   c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
589   cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
590   custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
591
592   C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
593
594   state = C_SYSTEM_INCLUDES
595
596   previous_line = ''
597   previous_line_num = 0
598   problem_linenums = []
599   for line_num, line in scope:
600     if c_system_include_pattern.match(line):
601       if state != C_SYSTEM_INCLUDES:
602         problem_linenums.append((line_num, previous_line_num))
603       elif previous_line and previous_line > line:
604         problem_linenums.append((line_num, previous_line_num))
605     elif cpp_system_include_pattern.match(line):
606       if state == C_SYSTEM_INCLUDES:
607         state = CPP_SYSTEM_INCLUDES
608       elif state == CUSTOM_INCLUDES:
609         problem_linenums.append((line_num, previous_line_num))
610       elif previous_line and previous_line > line:
611         problem_linenums.append((line_num, previous_line_num))
612     elif custom_include_pattern.match(line):
613       if state != CUSTOM_INCLUDES:
614         state = CUSTOM_INCLUDES
615       elif previous_line and previous_line > line:
616         problem_linenums.append((line_num, previous_line_num))
617     else:
618       problem_linenums.append(line_num)
619     previous_line = line
620     previous_line_num = line_num
621
622   warnings = []
623   for (line_num, previous_line_num) in problem_linenums:
624     if line_num in changed_linenums or previous_line_num in changed_linenums:
625       warnings.append('    %s:%d' % (file_path, line_num))
626   return warnings
627
628
629 def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
630   """Checks the #include order for the given file f."""
631
632   system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
633   # Exclude the following includes from the check:
634   # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
635   # specific order.
636   # 2) <atlbase.h>, "build/build_config.h"
637   excluded_include_pattern = input_api.re.compile(
638       r'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
639   custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
640   # Match the final or penultimate token if it is xxxtest so we can ignore it
641   # when considering the special first include.
642   test_file_tag_pattern = input_api.re.compile(
643     r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
644   if_pattern = input_api.re.compile(
645       r'\s*#\s*(if|elif|else|endif|define|undef).*')
646   # Some files need specialized order of includes; exclude such files from this
647   # check.
648   uncheckable_includes_pattern = input_api.re.compile(
649       r'\s*#include '
650       '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
651
652   contents = f.NewContents()
653   warnings = []
654   line_num = 0
655
656   # Handle the special first include. If the first include file is
657   # some/path/file.h, the corresponding including file can be some/path/file.cc,
658   # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
659   # etc. It's also possible that no special first include exists.
660   # If the included file is some/path/file_platform.h the including file could
661   # also be some/path/file_xxxtest_platform.h.
662   including_file_base_name = test_file_tag_pattern.sub(
663     '', input_api.os_path.basename(f.LocalPath()))
664
665   for line in contents:
666     line_num += 1
667     if system_include_pattern.match(line):
668       # No special first include -> process the line again along with normal
669       # includes.
670       line_num -= 1
671       break
672     match = custom_include_pattern.match(line)
673     if match:
674       match_dict = match.groupdict()
675       header_basename = test_file_tag_pattern.sub(
676         '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '')
677
678       if header_basename not in including_file_base_name:
679         # No special first include -> process the line again along with normal
680         # includes.
681         line_num -= 1
682       break
683
684   # Split into scopes: Each region between #if and #endif is its own scope.
685   scopes = []
686   current_scope = []
687   for line in contents[line_num:]:
688     line_num += 1
689     if uncheckable_includes_pattern.match(line):
690       continue
691     if if_pattern.match(line):
692       scopes.append(current_scope)
693       current_scope = []
694     elif ((system_include_pattern.match(line) or
695            custom_include_pattern.match(line)) and
696           not excluded_include_pattern.match(line)):
697       current_scope.append((line_num, line))
698   scopes.append(current_scope)
699
700   for scope in scopes:
701     warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
702                                                changed_linenums))
703   return warnings
704
705
706 def _CheckIncludeOrder(input_api, output_api):
707   """Checks that the #include order is correct.
708
709   1. The corresponding header for source files.
710   2. C system files in alphabetical order
711   3. C++ system files in alphabetical order
712   4. Project's .h files in alphabetical order
713
714   Each region separated by #if, #elif, #else, #endif, #define and #undef follows
715   these rules separately.
716   """
717
718   warnings = []
719   for f in input_api.AffectedFiles():
720     if f.LocalPath().endswith(('.cc', '.h')):
721       changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
722       warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
723
724   results = []
725   if warnings:
726     results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
727                                                       warnings))
728   return results
729
730
731 def _CheckForVersionControlConflictsInFile(input_api, f):
732   pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
733   errors = []
734   for line_num, line in f.ChangedContents():
735     if pattern.match(line):
736       errors.append('    %s:%d %s' % (f.LocalPath(), line_num, line))
737   return errors
738
739
740 def _CheckForVersionControlConflicts(input_api, output_api):
741   """Usually this is not intentional and will cause a compile failure."""
742   errors = []
743   for f in input_api.AffectedFiles():
744     errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
745
746   results = []
747   if errors:
748     results.append(output_api.PresubmitError(
749       'Version control conflict markers found, please resolve.', errors))
750   return results
751
752
753 def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
754   def FilterFile(affected_file):
755     """Filter function for use with input_api.AffectedSourceFiles,
756     below.  This filters out everything except non-test files from
757     top-level directories that generally speaking should not hard-code
758     service URLs (e.g. src/android_webview/, src/content/ and others).
759     """
760     return input_api.FilterSourceFile(
761       affected_file,
762       white_list=(r'^(android_webview|base|content|net)[\\\/].*', ),
763       black_list=(_EXCLUDED_PATHS +
764                   _TEST_CODE_EXCLUDED_PATHS +
765                   input_api.DEFAULT_BLACK_LIST))
766
767   base_pattern = '"[^"]*google\.com[^"]*"'
768   comment_pattern = input_api.re.compile('//.*%s' % base_pattern)
769   pattern = input_api.re.compile(base_pattern)
770   problems = []  # items are (filename, line_number, line)
771   for f in input_api.AffectedSourceFiles(FilterFile):
772     for line_num, line in f.ChangedContents():
773       if not comment_pattern.search(line) and pattern.search(line):
774         problems.append((f.LocalPath(), line_num, line))
775
776   if problems:
777     return [output_api.PresubmitPromptOrNotify(
778         'Most layers below src/chrome/ should not hardcode service URLs.\n'
779         'Are you sure this is correct?',
780         ['  %s:%d:  %s' % (
781             problem[0], problem[1], problem[2]) for problem in problems])]
782   else:
783     return []
784
785
786 def _CheckNoAbbreviationInPngFileName(input_api, output_api):
787   """Makes sure there are no abbreviations in the name of PNG files.
788   """
789   pattern = input_api.re.compile(r'.*_[a-z]_.*\.png$|.*_[a-z]\.png$')
790   errors = []
791   for f in input_api.AffectedFiles(include_deletes=False):
792     if pattern.match(f.LocalPath()):
793       errors.append('    %s' % f.LocalPath())
794
795   results = []
796   if errors:
797     results.append(output_api.PresubmitError(
798         'The name of PNG files should not have abbreviations. \n'
799         'Use _hover.png, _center.png, instead of _h.png, _c.png.\n'
800         'Contact oshima@chromium.org if you have questions.', errors))
801   return results
802
803
804 def _FilesToCheckForIncomingDeps(re, changed_lines):
805   """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
806   a set of DEPS entries that we should look up.
807
808   For a directory (rather than a specific filename) we fake a path to
809   a specific filename by adding /DEPS. This is chosen as a file that
810   will seldom or never be subject to per-file include_rules.
811   """
812   # We ignore deps entries on auto-generated directories.
813   AUTO_GENERATED_DIRS = ['grit', 'jni']
814
815   # This pattern grabs the path without basename in the first
816   # parentheses, and the basename (if present) in the second. It
817   # relies on the simple heuristic that if there is a basename it will
818   # be a header file ending in ".h".
819   pattern = re.compile(
820       r"""['"]\+([^'"]+?)(/[a-zA-Z0-9_]+\.h)?['"].*""")
821   results = set()
822   for changed_line in changed_lines:
823     m = pattern.match(changed_line)
824     if m:
825       path = m.group(1)
826       if path.split('/')[0] not in AUTO_GENERATED_DIRS:
827         if m.group(2):
828           results.add('%s%s' % (path, m.group(2)))
829         else:
830           results.add('%s/DEPS' % path)
831   return results
832
833
834 def _CheckAddedDepsHaveTargetApprovals(input_api, output_api):
835   """When a dependency prefixed with + is added to a DEPS file, we
836   want to make sure that the change is reviewed by an OWNER of the
837   target file or directory, to avoid layering violations from being
838   introduced. This check verifies that this happens.
839   """
840   changed_lines = set()
841   for f in input_api.AffectedFiles():
842     filename = input_api.os_path.basename(f.LocalPath())
843     if filename == 'DEPS':
844       changed_lines |= set(line.strip()
845                            for line_num, line
846                            in f.ChangedContents())
847   if not changed_lines:
848     return []
849
850   virtual_depended_on_files = _FilesToCheckForIncomingDeps(input_api.re,
851                                                            changed_lines)
852   if not virtual_depended_on_files:
853     return []
854
855   if input_api.is_committing:
856     if input_api.tbr:
857       return [output_api.PresubmitNotifyResult(
858           '--tbr was specified, skipping OWNERS check for DEPS additions')]
859     if not input_api.change.issue:
860       return [output_api.PresubmitError(
861           "DEPS approval by OWNERS check failed: this change has "
862           "no Rietveld issue number, so we can't check it for approvals.")]
863     output = output_api.PresubmitError
864   else:
865     output = output_api.PresubmitNotifyResult
866
867   owners_db = input_api.owners_db
868   owner_email, reviewers = input_api.canned_checks._RietveldOwnerAndReviewers(
869       input_api,
870       owners_db.email_regexp,
871       approval_needed=input_api.is_committing)
872
873   owner_email = owner_email or input_api.change.author_email
874
875   reviewers_plus_owner = set(reviewers)
876   if owner_email:
877     reviewers_plus_owner.add(owner_email)
878   missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
879                                                  reviewers_plus_owner)
880
881   # We strip the /DEPS part that was added by
882   # _FilesToCheckForIncomingDeps to fake a path to a file in a
883   # directory.
884   def StripDeps(path):
885     start_deps = path.rfind('/DEPS')
886     if start_deps != -1:
887       return path[:start_deps]
888     else:
889       return path
890   unapproved_dependencies = ["'+%s'," % StripDeps(path)
891                              for path in missing_files]
892
893   if unapproved_dependencies:
894     output_list = [
895       output('Missing LGTM from OWNERS of dependencies added to DEPS:\n    %s' %
896              '\n    '.join(sorted(unapproved_dependencies)))]
897     if not input_api.is_committing:
898       suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
899       output_list.append(output(
900           'Suggested missing target path OWNERS:\n    %s' %
901           '\n    '.join(suggested_owners or [])))
902     return output_list
903
904   return []
905
906
907 def _CheckSpamLogging(input_api, output_api):
908   file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
909   black_list = (_EXCLUDED_PATHS +
910                 _TEST_CODE_EXCLUDED_PATHS +
911                 input_api.DEFAULT_BLACK_LIST +
912                 (r"^base[\\\/]logging\.h$",
913                  r"^base[\\\/]logging\.cc$",
914                  r"^cloud_print[\\\/]",
915                  r"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$",
916                  r"^chrome[\\\/]app[\\\/]chrome_main_delegate\.cc$",
917                  r"^chrome[\\\/]browser[\\\/]chrome_browser_main\.cc$",
918                  r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]"
919                      r"startup_browser_creator\.cc$",
920                  r"^chrome[\\\/]installer[\\\/]setup[\\\/].*",
921                  r"^extensions[\\\/]renderer[\\\/]logging_native_handler\.cc$",
922                  r"^content[\\\/]common[\\\/]gpu[\\\/]client[\\\/]"
923                      r"gl_helper_benchmark\.cc$",
924                  r"^native_client_sdk[\\\/]",
925                  r"^remoting[\\\/]base[\\\/]logging\.h$",
926                  r"^remoting[\\\/]host[\\\/].*",
927                  r"^sandbox[\\\/]linux[\\\/].*",
928                  r"^tools[\\\/]",
929                  r"^ui[\\\/]aura[\\\/]bench[\\\/]bench_main\.cc$",))
930   source_file_filter = lambda x: input_api.FilterSourceFile(
931       x, white_list=(file_inclusion_pattern,), black_list=black_list)
932
933   log_info = []
934   printf = []
935
936   for f in input_api.AffectedSourceFiles(source_file_filter):
937     contents = input_api.ReadFile(f, 'rb')
938     if re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", contents):
939       log_info.append(f.LocalPath())
940     elif re.search(r"\bD?LOG_IF\s*\(\s*INFO\s*,", contents):
941       log_info.append(f.LocalPath())
942
943     if re.search(r"\bprintf\(", contents):
944       printf.append(f.LocalPath())
945     elif re.search(r"\bfprintf\((stdout|stderr)", contents):
946       printf.append(f.LocalPath())
947
948   if log_info:
949     return [output_api.PresubmitError(
950       'These files spam the console log with LOG(INFO):',
951       items=log_info)]
952   if printf:
953     return [output_api.PresubmitError(
954       'These files spam the console log with printf/fprintf:',
955       items=printf)]
956   return []
957
958
959 def _CheckForAnonymousVariables(input_api, output_api):
960   """These types are all expected to hold locks while in scope and
961      so should never be anonymous (which causes them to be immediately
962      destroyed)."""
963   they_who_must_be_named = [
964     'base::AutoLock',
965     'base::AutoReset',
966     'base::AutoUnlock',
967     'SkAutoAlphaRestore',
968     'SkAutoBitmapShaderInstall',
969     'SkAutoBlitterChoose',
970     'SkAutoBounderCommit',
971     'SkAutoCallProc',
972     'SkAutoCanvasRestore',
973     'SkAutoCommentBlock',
974     'SkAutoDescriptor',
975     'SkAutoDisableDirectionCheck',
976     'SkAutoDisableOvalCheck',
977     'SkAutoFree',
978     'SkAutoGlyphCache',
979     'SkAutoHDC',
980     'SkAutoLockColors',
981     'SkAutoLockPixels',
982     'SkAutoMalloc',
983     'SkAutoMaskFreeImage',
984     'SkAutoMutexAcquire',
985     'SkAutoPathBoundsUpdate',
986     'SkAutoPDFRelease',
987     'SkAutoRasterClipValidate',
988     'SkAutoRef',
989     'SkAutoTime',
990     'SkAutoTrace',
991     'SkAutoUnref',
992   ]
993   anonymous = r'(%s)\s*[({]' % '|'.join(they_who_must_be_named)
994   # bad: base::AutoLock(lock.get());
995   # not bad: base::AutoLock lock(lock.get());
996   bad_pattern = input_api.re.compile(anonymous)
997   # good: new base::AutoLock(lock.get())
998   good_pattern = input_api.re.compile(r'\bnew\s*' + anonymous)
999   errors = []
1000
1001   for f in input_api.AffectedFiles():
1002     if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
1003       continue
1004     for linenum, line in f.ChangedContents():
1005       if bad_pattern.search(line) and not good_pattern.search(line):
1006         errors.append('%s:%d' % (f.LocalPath(), linenum))
1007
1008   if errors:
1009     return [output_api.PresubmitError(
1010       'These lines create anonymous variables that need to be named:',
1011       items=errors)]
1012   return []
1013
1014
1015 def _CheckCygwinShell(input_api, output_api):
1016   source_file_filter = lambda x: input_api.FilterSourceFile(
1017       x, white_list=(r'.+\.(gyp|gypi)$',))
1018   cygwin_shell = []
1019
1020   for f in input_api.AffectedSourceFiles(source_file_filter):
1021     for linenum, line in f.ChangedContents():
1022       if 'msvs_cygwin_shell' in line:
1023         cygwin_shell.append(f.LocalPath())
1024         break
1025
1026   if cygwin_shell:
1027     return [output_api.PresubmitError(
1028       'These files should not use msvs_cygwin_shell (the default is 0):',
1029       items=cygwin_shell)]
1030   return []
1031
1032
1033 def _CheckUserActionUpdate(input_api, output_api):
1034   """Checks if any new user action has been added."""
1035   if any('actions.xml' == input_api.os_path.basename(f) for f in
1036          input_api.LocalPaths()):
1037     # If actions.xml is already included in the changelist, the PRESUBMIT
1038     # for actions.xml will do a more complete presubmit check.
1039     return []
1040
1041   file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm'))
1042   action_re = r'[^a-zA-Z]UserMetricsAction\("([^"]*)'
1043   current_actions = None
1044   for f in input_api.AffectedFiles(file_filter=file_filter):
1045     for line_num, line in f.ChangedContents():
1046       match = input_api.re.search(action_re, line)
1047       if match:
1048         # Loads contents in tools/metrics/actions/actions.xml to memory. It's
1049         # loaded only once.
1050         if not current_actions:
1051           with open('tools/metrics/actions/actions.xml') as actions_f:
1052             current_actions = actions_f.read()
1053         # Search for the matched user action name in |current_actions|.
1054         for action_name in match.groups():
1055           action = 'name="{0}"'.format(action_name)
1056           if action not in current_actions:
1057             return [output_api.PresubmitPromptWarning(
1058               'File %s line %d: %s is missing in '
1059               'tools/metrics/actions/actions.xml. Please run '
1060               'tools/metrics/actions/extract_actions.py to update.'
1061               % (f.LocalPath(), line_num, action_name))]
1062   return []
1063
1064
1065 def _CheckJavaStyle(input_api, output_api):
1066   """Runs checkstyle on changed java files and returns errors if any exist."""
1067   original_sys_path = sys.path
1068   try:
1069     sys.path = sys.path + [input_api.os_path.join(
1070         input_api.PresubmitLocalPath(), 'tools', 'android', 'checkstyle')]
1071     import checkstyle
1072   finally:
1073     # Restore sys.path to what it was before.
1074     sys.path = original_sys_path
1075
1076   return checkstyle.RunCheckstyle(
1077       input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml')
1078
1079
1080 _DEPRECATED_CSS = [
1081   # Values
1082   ( "-webkit-box", "flex" ),
1083   ( "-webkit-inline-box", "inline-flex" ),
1084   ( "-webkit-flex", "flex" ),
1085   ( "-webkit-inline-flex", "inline-flex" ),
1086   ( "-webkit-min-content", "min-content" ),
1087   ( "-webkit-max-content", "max-content" ),
1088
1089   # Properties
1090   ( "-webkit-background-clip", "background-clip" ),
1091   ( "-webkit-background-origin", "background-origin" ),
1092   ( "-webkit-background-size", "background-size" ),
1093   ( "-webkit-box-shadow", "box-shadow" ),
1094
1095   # Functions
1096   ( "-webkit-gradient", "gradient" ),
1097   ( "-webkit-repeating-gradient", "repeating-gradient" ),
1098   ( "-webkit-linear-gradient", "linear-gradient" ),
1099   ( "-webkit-repeating-linear-gradient", "repeating-linear-gradient" ),
1100   ( "-webkit-radial-gradient", "radial-gradient" ),
1101   ( "-webkit-repeating-radial-gradient", "repeating-radial-gradient" ),
1102 ]
1103
1104 def _CheckNoDeprecatedCSS(input_api, output_api):
1105   """ Make sure that we don't use deprecated CSS
1106       properties, functions or values. """
1107   results = []
1108   file_filter = lambda f: f.LocalPath().endswith('.css')
1109   for fpath in input_api.AffectedFiles(file_filter=file_filter):
1110     for line_num, line in fpath.ChangedContents():
1111       for (deprecated_value, value) in _DEPRECATED_CSS:
1112         if input_api.re.search(deprecated_value, line):
1113           results.append(output_api.PresubmitError(
1114               "%s:%d: Use of deprecated CSS %s, use %s instead" %
1115               (fpath.LocalPath(), line_num, deprecated_value, value)))
1116   return results
1117
1118 def _CommonChecks(input_api, output_api):
1119   """Checks common to both upload and commit."""
1120   results = []
1121   results.extend(input_api.canned_checks.PanProjectChecks(
1122       input_api, output_api,
1123       excluded_paths=_EXCLUDED_PATHS + _TESTRUNNER_PATHS))
1124   results.extend(_CheckAuthorizedAuthor(input_api, output_api))
1125   results.extend(
1126       _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
1127   results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
1128   results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
1129   results.extend(_CheckNoNewWStrings(input_api, output_api))
1130   results.extend(_CheckNoDEPSGIT(input_api, output_api))
1131   results.extend(_CheckNoBannedFunctions(input_api, output_api))
1132   results.extend(_CheckNoPragmaOnce(input_api, output_api))
1133   results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
1134   results.extend(_CheckUnwantedDependencies(input_api, output_api))
1135   results.extend(_CheckFilePermissions(input_api, output_api))
1136   results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
1137   results.extend(_CheckIncludeOrder(input_api, output_api))
1138   results.extend(_CheckForVersionControlConflicts(input_api, output_api))
1139   results.extend(_CheckPatchFiles(input_api, output_api))
1140   results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
1141   results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
1142   results.extend(_CheckForInvalidOSMacros(input_api, output_api))
1143   results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1144   results.extend(
1145       input_api.canned_checks.CheckChangeHasNoTabs(
1146           input_api,
1147           output_api,
1148           source_file_filter=lambda x: x.LocalPath().endswith('.grd')))
1149   results.extend(_CheckSpamLogging(input_api, output_api))
1150   results.extend(_CheckForAnonymousVariables(input_api, output_api))
1151   results.extend(_CheckCygwinShell(input_api, output_api))
1152   results.extend(_CheckUserActionUpdate(input_api, output_api))
1153   results.extend(_CheckNoDeprecatedCSS(input_api, output_api))
1154
1155   if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
1156     results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
1157         input_api, output_api,
1158         input_api.PresubmitLocalPath(),
1159         whitelist=[r'^PRESUBMIT_test\.py$']))
1160   return results
1161
1162
1163 def _CheckSubversionConfig(input_api, output_api):
1164   """Verifies the subversion config file is correctly setup.
1165
1166   Checks that autoprops are enabled, returns an error otherwise.
1167   """
1168   join = input_api.os_path.join
1169   if input_api.platform == 'win32':
1170     appdata = input_api.environ.get('APPDATA', '')
1171     if not appdata:
1172       return [output_api.PresubmitError('%APPDATA% is not configured.')]
1173     path = join(appdata, 'Subversion', 'config')
1174   else:
1175     home = input_api.environ.get('HOME', '')
1176     if not home:
1177       return [output_api.PresubmitError('$HOME is not configured.')]
1178     path = join(home, '.subversion', 'config')
1179
1180   error_msg = (
1181       'Please look at http://dev.chromium.org/developers/coding-style to\n'
1182       'configure your subversion configuration file. This enables automatic\n'
1183       'properties to simplify the project maintenance.\n'
1184       'Pro-tip: just download and install\n'
1185       'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
1186
1187   try:
1188     lines = open(path, 'r').read().splitlines()
1189     # Make sure auto-props is enabled and check for 2 Chromium standard
1190     # auto-prop.
1191     if (not '*.cc = svn:eol-style=LF' in lines or
1192         not '*.pdf = svn:mime-type=application/pdf' in lines or
1193         not 'enable-auto-props = yes' in lines):
1194       return [
1195           output_api.PresubmitNotifyResult(
1196               'It looks like you have not configured your subversion config '
1197               'file or it is not up-to-date.\n' + error_msg)
1198       ]
1199   except (OSError, IOError):
1200     return [
1201         output_api.PresubmitNotifyResult(
1202             'Can\'t find your subversion config file.\n' + error_msg)
1203     ]
1204   return []
1205
1206
1207 def _CheckAuthorizedAuthor(input_api, output_api):
1208   """For non-googler/chromites committers, verify the author's email address is
1209   in AUTHORS.
1210   """
1211   # TODO(maruel): Add it to input_api?
1212   import fnmatch
1213
1214   author = input_api.change.author_email
1215   if not author:
1216     input_api.logging.info('No author, skipping AUTHOR check')
1217     return []
1218   authors_path = input_api.os_path.join(
1219       input_api.PresubmitLocalPath(), 'AUTHORS')
1220   valid_authors = (
1221       input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
1222       for line in open(authors_path))
1223   valid_authors = [item.group(1).lower() for item in valid_authors if item]
1224   if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
1225     input_api.logging.info('Valid authors are %s', ', '.join(valid_authors))
1226     return [output_api.PresubmitPromptWarning(
1227         ('%s is not in AUTHORS file. If you are a new contributor, please visit'
1228         '\n'
1229         'http://www.chromium.org/developers/contributing-code and read the '
1230         '"Legal" section\n'
1231         'If you are a chromite, verify the contributor signed the CLA.') %
1232         author)]
1233   return []
1234
1235
1236 def _CheckPatchFiles(input_api, output_api):
1237   problems = [f.LocalPath() for f in input_api.AffectedFiles()
1238       if f.LocalPath().endswith(('.orig', '.rej'))]
1239   if problems:
1240     return [output_api.PresubmitError(
1241         "Don't commit .rej and .orig files.", problems)]
1242   else:
1243     return []
1244
1245
1246 def _DidYouMeanOSMacro(bad_macro):
1247   try:
1248     return {'A': 'OS_ANDROID',
1249             'B': 'OS_BSD',
1250             'C': 'OS_CHROMEOS',
1251             'F': 'OS_FREEBSD',
1252             'L': 'OS_LINUX',
1253             'M': 'OS_MACOSX',
1254             'N': 'OS_NACL',
1255             'O': 'OS_OPENBSD',
1256             'P': 'OS_POSIX',
1257             'S': 'OS_SOLARIS',
1258             'W': 'OS_WIN'}[bad_macro[3].upper()]
1259   except KeyError:
1260     return ''
1261
1262
1263 def _CheckForInvalidOSMacrosInFile(input_api, f):
1264   """Check for sensible looking, totally invalid OS macros."""
1265   preprocessor_statement = input_api.re.compile(r'^\s*#')
1266   os_macro = input_api.re.compile(r'defined\((OS_[^)]+)\)')
1267   results = []
1268   for lnum, line in f.ChangedContents():
1269     if preprocessor_statement.search(line):
1270       for match in os_macro.finditer(line):
1271         if not match.group(1) in _VALID_OS_MACROS:
1272           good = _DidYouMeanOSMacro(match.group(1))
1273           did_you_mean = ' (did you mean %s?)' % good if good else ''
1274           results.append('    %s:%d %s%s' % (f.LocalPath(),
1275                                              lnum,
1276                                              match.group(1),
1277                                              did_you_mean))
1278   return results
1279
1280
1281 def _CheckForInvalidOSMacros(input_api, output_api):
1282   """Check all affected files for invalid OS macros."""
1283   bad_macros = []
1284   for f in input_api.AffectedFiles():
1285     if not f.LocalPath().endswith(('.py', '.js', '.html', '.css')):
1286       bad_macros.extend(_CheckForInvalidOSMacrosInFile(input_api, f))
1287
1288   if not bad_macros:
1289     return []
1290
1291   return [output_api.PresubmitError(
1292       'Possibly invalid OS macro[s] found. Please fix your code\n'
1293       'or add your macro to src/PRESUBMIT.py.', bad_macros)]
1294
1295
1296 def CheckChangeOnUpload(input_api, output_api):
1297   results = []
1298   results.extend(_CommonChecks(input_api, output_api))
1299   results.extend(_CheckJavaStyle(input_api, output_api))
1300   return results
1301
1302
1303 def GetTryServerMasterForBot(bot):
1304   """Returns the Try Server master for the given bot.
1305
1306   Assumes that most Try Servers are on the tryserver.chromium master."""
1307   non_default_master_map = {
1308       'linux_gpu': 'tryserver.chromium.gpu',
1309       'mac_gpu': 'tryserver.chromium.gpu',
1310       'win_gpu': 'tryserver.chromium.gpu',
1311   }
1312   return non_default_master_map.get(bot, 'tryserver.chromium')
1313
1314
1315 def GetDefaultTryConfigs(bots=None):
1316   """Returns a list of ('bot', set(['tests']), optionally filtered by [bots].
1317
1318   To add tests to this list, they MUST be in the the corresponding master's
1319   gatekeeper config. For example, anything on master.chromium would be closed by
1320   tools/build/masters/master.chromium/master_gatekeeper_cfg.py.
1321
1322   If 'bots' is specified, will only return configurations for bots in that list.
1323   """
1324
1325   standard_tests = [
1326       'base_unittests',
1327       'browser_tests',
1328       'cacheinvalidation_unittests',
1329       'check_deps',
1330       'check_deps2git',
1331       'content_browsertests',
1332       'content_unittests',
1333       'crypto_unittests',
1334       'gpu_unittests',
1335       'interactive_ui_tests',
1336       'ipc_tests',
1337       'jingle_unittests',
1338       'media_unittests',
1339       'net_unittests',
1340       'ppapi_unittests',
1341       'printing_unittests',
1342       'sql_unittests',
1343       'sync_unit_tests',
1344       'unit_tests',
1345       # Broken in release.
1346       #'url_unittests',
1347       #'webkit_unit_tests',
1348   ]
1349
1350   builders_and_tests = {
1351       # TODO(maruel): Figure out a way to run 'sizes' where people can
1352       # effectively update the perf expectation correctly.  This requires a
1353       # clobber=True build running 'sizes'. 'sizes' is not accurate with
1354       # incremental build. Reference:
1355       # http://chromium.org/developers/tree-sheriffs/perf-sheriffs.
1356       # TODO(maruel): An option would be to run 'sizes' but not count a failure
1357       # of this step as a try job failure.
1358       'android_aosp': ['compile'],
1359       'android_chromium_gn_compile_rel': ['compile'],
1360       'android_clang_dbg': ['slave_steps'],
1361       'android_dbg': ['slave_steps'],
1362       'cros_x86': ['defaulttests'],
1363       'ios_dbg_simulator': [
1364           'compile',
1365           'base_unittests',
1366           'content_unittests',
1367           'crypto_unittests',
1368           'url_unittests',
1369           'net_unittests',
1370           'sql_unittests',
1371           'ui_unittests',
1372       ],
1373       'ios_rel_device': ['compile'],
1374       'linux_asan': ['compile'],
1375       'mac_asan': ['compile'],
1376       #TODO(stip): Change the name of this builder to reflect that it's release.
1377       'linux_gtk': standard_tests,
1378       'linux_chromeos_asan': ['compile'],
1379       'linux_chromium_chromeos_clang_dbg': ['defaulttests'],
1380       'linux_chromium_chromeos_rel': ['defaulttests'],
1381       'linux_chromium_compile_dbg': ['defaulttests'],
1382       'linux_chromium_gn_rel': ['defaulttests'],
1383       'linux_chromium_rel': ['defaulttests'],
1384       'linux_chromium_clang_dbg': ['defaulttests'],
1385       'linux_gpu': ['defaulttests'],
1386       'linux_nacl_sdk_build': ['compile'],
1387       'linux_rel': [
1388           'telemetry_perf_unittests',
1389           'telemetry_unittests',
1390       ],
1391       'mac_chromium_compile_dbg': ['defaulttests'],
1392       'mac_chromium_rel': ['defaulttests'],
1393       'mac_gpu': ['defaulttests'],
1394       'mac_nacl_sdk_build': ['compile'],
1395       'mac_rel': [
1396           'telemetry_perf_unittests',
1397           'telemetry_unittests',
1398       ],
1399       'win': ['compile'],
1400       'win_chromium_compile_dbg': ['defaulttests'],
1401       'win_chromium_dbg': ['defaulttests'],
1402       'win_chromium_rel': ['defaulttests'],
1403       'win_chromium_x64_rel': ['defaulttests'],
1404       'win_gpu': ['defaulttests'],
1405       'win_nacl_sdk_build': ['compile'],
1406       'win_rel': standard_tests + [
1407           'app_list_unittests',
1408           'ash_unittests',
1409           'aura_unittests',
1410           'cc_unittests',
1411           'chrome_elf_unittests',
1412           'chromedriver_unittests',
1413           'components_unittests',
1414           'compositor_unittests',
1415           'events_unittests',
1416           'gfx_unittests',
1417           'google_apis_unittests',
1418           'installer_util_unittests',
1419           'mini_installer_test',
1420           'nacl_integration',
1421           'remoting_unittests',
1422           'sync_integration_tests',
1423           'telemetry_perf_unittests',
1424           'telemetry_unittests',
1425           'views_unittests',
1426       ],
1427       'win_x64_rel': [
1428           'base_unittests',
1429       ],
1430   }
1431
1432   swarm_enabled_builders = (
1433   # http://crbug.com/354263
1434   #    'linux_rel',
1435   #    'mac_rel',
1436   #    'win_rel',
1437   )
1438
1439   swarm_enabled_tests = (
1440       'base_unittests',
1441       'browser_tests',
1442       'interactive_ui_tests',
1443       'net_unittests',
1444       'unit_tests',
1445   )
1446
1447   for bot in builders_and_tests:
1448     if bot in swarm_enabled_builders:
1449       builders_and_tests[bot] = [x + '_swarm' if x in swarm_enabled_tests else x
1450                                  for x in builders_and_tests[bot]]
1451
1452   if bots:
1453     filtered_builders_and_tests = dict((bot, set(builders_and_tests[bot]))
1454                                        for bot in bots)
1455   else:
1456     filtered_builders_and_tests = dict(
1457         (bot, set(tests))
1458         for bot, tests in builders_and_tests.iteritems())
1459
1460   # Build up the mapping from tryserver master to bot/test.
1461   out = dict()
1462   for bot, tests in filtered_builders_and_tests.iteritems():
1463     out.setdefault(GetTryServerMasterForBot(bot), {})[bot] = tests
1464   return out
1465
1466
1467 def CheckChangeOnCommit(input_api, output_api):
1468   results = []
1469   results.extend(_CommonChecks(input_api, output_api))
1470   # TODO(thestig) temporarily disabled, doesn't work in third_party/
1471   #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
1472   #    input_api, output_api, sources))
1473   # Make sure the tree is 'open'.
1474   results.extend(input_api.canned_checks.CheckTreeIsOpen(
1475       input_api,
1476       output_api,
1477       json_url='http://chromium-status.appspot.com/current?format=json'))
1478
1479   results.extend(input_api.canned_checks.CheckChangeHasBugField(
1480       input_api, output_api))
1481   results.extend(input_api.canned_checks.CheckChangeHasDescription(
1482       input_api, output_api))
1483   results.extend(_CheckSubversionConfig(input_api, output_api))
1484   return results
1485
1486
1487 def GetPreferredTryMasters(project, change):
1488   files = change.LocalPaths()
1489
1490   if not files or all(re.search(r'[\\/]OWNERS$', f) for f in files):
1491     return {}
1492
1493   if all(re.search('\.(m|mm)$|(^|[/_])mac[/_.]', f) for f in files):
1494     return GetDefaultTryConfigs([
1495         'mac_chromium_compile_dbg',
1496         'mac_chromium_rel',
1497     ])
1498   if all(re.search('(^|[/_])win[/_.]', f) for f in files):
1499     return GetDefaultTryConfigs(['win_chromium_dbg', 'win_chromium_rel'])
1500   if all(re.search('(^|[/_])android[/_.]', f) for f in files):
1501     return GetDefaultTryConfigs([
1502         'android_aosp',
1503         'android_clang_dbg',
1504         'android_dbg',
1505     ])
1506   if all(re.search('[/_]ios[/_.]', f) for f in files):
1507     return GetDefaultTryConfigs(['ios_rel_device', 'ios_dbg_simulator'])
1508
1509   builders = [
1510       'android_chromium_gn_compile_rel',
1511       'android_clang_dbg',
1512       'android_dbg',
1513       'ios_dbg_simulator',
1514       'ios_rel_device',
1515       'linux_chromium_chromeos_rel',
1516       'linux_chromium_clang_dbg',
1517       'linux_chromium_gn_rel',
1518       'linux_chromium_rel',
1519       'linux_gpu',
1520       'mac_chromium_compile_dbg',
1521       'mac_chromium_rel',
1522       'mac_gpu',
1523       'win_chromium_compile_dbg',
1524       'win_chromium_rel',
1525       'win_chromium_x64_rel',
1526       'win_gpu',
1527   ]
1528
1529   # Match things like path/aura/file.cc and path/file_aura.cc.
1530   # Same for chromeos.
1531   if any(re.search('[/_](aura|chromeos)', f) for f in files):
1532     builders.extend([
1533         'linux_chromeos_asan',
1534         'linux_chromium_chromeos_clang_dbg'
1535     ])
1536
1537   # If there are gyp changes to base, build, or chromeos, run a full cros build
1538   # in addition to the shorter linux_chromeos build. Changes to high level gyp
1539   # files have a much higher chance of breaking the cros build, which is
1540   # differnt from the linux_chromeos build that most chrome developers test
1541   # with.
1542   if any(re.search('^(base|build|chromeos).*\.gypi?$', f) for f in files):
1543     builders.extend(['cros_x86'])
1544
1545   # The AOSP bot doesn't build the chrome/ layer, so ignore any changes to it
1546   # unless they're .gyp(i) files as changes to those files can break the gyp
1547   # step on that bot.
1548   if (not all(re.search('^chrome', f) for f in files) or
1549       any(re.search('\.gypi?$', f) for f in files)):
1550     builders.extend(['android_aosp'])
1551
1552   return GetDefaultTryConfigs(builders)