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