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 cc.
6
7See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
8details on the presubmit API built into gcl.
9"""
10
11import re
12import string
13
14CC_SOURCE_FILES=(r'^cc/.*\.(cc|h)$',)
15CC_PERF_TEST =(r'^.*_perftest.*\.(cc|h)$',)
16
17def CheckChangeLintsClean(input_api, output_api):
18  input_api.cpplint._cpplint_state.ResetErrorCounts()  # reset global state
19  source_filter = lambda x: input_api.FilterSourceFile(
20    x, white_list=CC_SOURCE_FILES, black_list=None)
21  files = [f.AbsoluteLocalPath() for f in
22           input_api.AffectedSourceFiles(source_filter)]
23  level = 1  # strict, but just warn
24
25  for file_name in files:
26    input_api.cpplint.ProcessFile(file_name, level)
27
28  if not input_api.cpplint._cpplint_state.error_count:
29    return []
30
31  return [output_api.PresubmitPromptWarning(
32    'Changelist failed cpplint.py check.')]
33
34def CheckAsserts(input_api, output_api, white_list=CC_SOURCE_FILES, black_list=None):
35  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
36  source_file_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
37
38  assert_files = []
39  notreached_files = []
40
41  for f in input_api.AffectedSourceFiles(source_file_filter):
42    contents = input_api.ReadFile(f, 'rb')
43    # WebKit ASSERT() is not allowed.
44    if re.search(r"\bASSERT\(", contents):
45      assert_files.append(f.LocalPath())
46    # WebKit ASSERT_NOT_REACHED() is not allowed.
47    if re.search(r"ASSERT_NOT_REACHED\(", contents):
48      notreached_files.append(f.LocalPath())
49
50  if assert_files:
51    return [output_api.PresubmitError(
52      'These files use ASSERT instead of using DCHECK:',
53      items=assert_files)]
54  if notreached_files:
55    return [output_api.PresubmitError(
56      'These files use ASSERT_NOT_REACHED instead of using NOTREACHED:',
57      items=notreached_files)]
58  return []
59
60def CheckStdAbs(input_api, output_api,
61                white_list=CC_SOURCE_FILES, black_list=None):
62  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
63  source_file_filter = lambda x: input_api.FilterSourceFile(x,
64                                                            white_list,
65                                                            black_list)
66
67  using_std_abs_files = []
68  found_fabs_files = []
69  missing_std_prefix_files = []
70
71  for f in input_api.AffectedSourceFiles(source_file_filter):
72    contents = input_api.ReadFile(f, 'rb')
73    if re.search(r"using std::f?abs;", contents):
74      using_std_abs_files.append(f.LocalPath())
75    if re.search(r"\bfabsf?\(", contents):
76      found_fabs_files.append(f.LocalPath());
77    # The following regular expression in words says:
78    # "if there is no 'std::' behind an 'abs(' or 'absf(',
79    # or if there is no 'std::' behind a 'fabs(' or 'fabsf(',
80    # then it's a match."
81    if re.search(r"((?<!std::)(\babsf?\()|(?<!std::)(\bfabsf?\())", contents):
82      missing_std_prefix_files.append(f.LocalPath())
83
84  result = []
85  if using_std_abs_files:
86    result.append(output_api.PresubmitError(
87        'These files have "using std::abs" which is not permitted.',
88        items=using_std_abs_files))
89  if found_fabs_files:
90    result.append(output_api.PresubmitError(
91        'std::abs() should be used instead of std::fabs() for consistency.',
92        items=found_fabs_files))
93  if missing_std_prefix_files:
94    result.append(output_api.PresubmitError(
95        'These files use abs(), absf(), fabs(), or fabsf() without qualifying '
96        'the std namespace. Please use std::abs() in all places.',
97        items=missing_std_prefix_files))
98  return result
99
100def CheckSpamLogging(input_api,
101                     output_api,
102                     white_list=CC_SOURCE_FILES,
103                     black_list=None):
104  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
105  source_file_filter = lambda x: input_api.FilterSourceFile(x,
106                                                            white_list,
107                                                            black_list)
108
109  log_info = []
110  printf = []
111
112  for f in input_api.AffectedSourceFiles(source_file_filter):
113    contents = input_api.ReadFile(f, 'rb')
114    if re.search(r"\bD?LOG\s*\(\s*INFO\s*\)", contents):
115      log_info.append(f.LocalPath())
116    if re.search(r"\bf?printf\(", contents):
117      printf.append(f.LocalPath())
118
119  if log_info:
120    return [output_api.PresubmitError(
121      'These files spam the console log with LOG(INFO):',
122      items=log_info)]
123  if printf:
124    return [output_api.PresubmitError(
125      'These files spam the console log with printf/fprintf:',
126      items=printf)]
127  return []
128
129def CheckPassByValue(input_api,
130                     output_api,
131                     white_list=CC_SOURCE_FILES,
132                     black_list=None):
133  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
134  source_file_filter = lambda x: input_api.FilterSourceFile(x,
135                                                            white_list,
136                                                            black_list)
137
138  local_errors = []
139
140  # Well-defined simple classes containing only <= 4 ints, or <= 2 floats.
141  pass_by_value_types = ['base::Time',
142                         'base::TimeTicks',
143                         'gfx::Point',
144                         'gfx::PointF',
145                         'gfx::Rect',
146                         'gfx::Size',
147                         'gfx::SizeF',
148                         'gfx::Vector2d',
149                         'gfx::Vector2dF',
150                         ]
151
152  for f in input_api.AffectedSourceFiles(source_file_filter):
153    contents = input_api.ReadFile(f, 'rb')
154    match = re.search(
155      r'\bconst +' + '(?P<type>(%s))&' %
156        string.join(pass_by_value_types, '|'),
157      contents)
158    if match:
159      local_errors.append(output_api.PresubmitError(
160        '%s passes %s by const ref instead of by value.' %
161        (f.LocalPath(), match.group('type'))))
162  return local_errors
163
164def CheckTodos(input_api, output_api):
165  errors = []
166
167  source_file_filter = lambda x: x
168  for f in input_api.AffectedSourceFiles(source_file_filter):
169    contents = input_api.ReadFile(f, 'rb')
170    if ('FIX'+'ME') in contents:
171      errors.append(f.LocalPath())
172
173  if errors:
174    return [output_api.PresubmitError(
175      'All TODO comments should be of the form TODO(name).',
176      items=errors)]
177  return []
178
179
180def CheckChangeOnUpload(input_api, output_api):
181  results = []
182  results += CheckAsserts(input_api, output_api)
183  results += CheckStdAbs(input_api, output_api)
184  results += CheckSpamLogging(input_api, output_api, black_list=CC_PERF_TEST)
185  results += CheckPassByValue(input_api, output_api)
186  results += CheckChangeLintsClean(input_api, output_api)
187  results += CheckTodos(input_api, output_api)
188  return results
189
190def GetPreferredTrySlaves(project, change):
191  return [
192    'linux_layout_rel',
193    ]
194