[email protected] | 69085bc5 | 2012-10-15 16:41:58 | [diff] [blame] | 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 | |
[email protected] | 94be7f70 | 2014-02-03 19:06:45 | [diff] [blame] | 7 | See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts |
| 8 | for more details about the presubmit API built into depot_tools. |
[email protected] | 69085bc5 | 2012-10-15 16:41:58 | [diff] [blame] | 9 | """ |
| 10 | |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 11 | import re |
[email protected] | 7add2e0b | 2013-04-23 05:16:42 | [diff] [blame] | 12 | import string |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 13 | |
| 14 | CC_SOURCE_FILES=(r'^cc/.*\.(cc|h)$',) |
| 15 | |
[email protected] | cf82459 | 2013-04-09 05:46:00 | [diff] [blame] | 16 | def CheckChangeLintsClean(input_api, output_api): |
[email protected] | 16f5cf0 | 2013-04-17 15:47:29 | [diff] [blame] | 17 | input_api.cpplint._cpplint_state.ResetErrorCounts() # reset global state |
[email protected] | cf82459 | 2013-04-09 05:46:00 | [diff] [blame] | 18 | source_filter = lambda x: input_api.FilterSourceFile( |
| 19 | x, white_list=CC_SOURCE_FILES, black_list=None) |
| 20 | files = [f.AbsoluteLocalPath() for f in |
| 21 | input_api.AffectedSourceFiles(source_filter)] |
| 22 | level = 1 # strict, but just warn |
| 23 | |
| 24 | for file_name in files: |
[email protected] | 16f5cf0 | 2013-04-17 15:47:29 | [diff] [blame] | 25 | input_api.cpplint.ProcessFile(file_name, level) |
[email protected] | cf82459 | 2013-04-09 05:46:00 | [diff] [blame] | 26 | |
[email protected] | 16f5cf0 | 2013-04-17 15:47:29 | [diff] [blame] | 27 | if not input_api.cpplint._cpplint_state.error_count: |
[email protected] | cf82459 | 2013-04-09 05:46:00 | [diff] [blame] | 28 | return [] |
| 29 | |
| 30 | return [output_api.PresubmitPromptWarning( |
| 31 | 'Changelist failed cpplint.py check.')] |
| 32 | |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 33 | def CheckAsserts(input_api, output_api, white_list=CC_SOURCE_FILES, black_list=None): |
| 34 | black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST) |
| 35 | source_file_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list) |
| 36 | |
| 37 | assert_files = [] |
| 38 | notreached_files = [] |
| 39 | |
| 40 | for f in input_api.AffectedSourceFiles(source_file_filter): |
| 41 | contents = input_api.ReadFile(f, 'rb') |
| 42 | # WebKit ASSERT() is not allowed. |
[email protected] | f4a4b0e | 2012-10-22 22:01:37 | [diff] [blame] | 43 | if re.search(r"\bASSERT\(", contents): |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 44 | assert_files.append(f.LocalPath()) |
| 45 | # WebKit ASSERT_NOT_REACHED() is not allowed. |
| 46 | if re.search(r"ASSERT_NOT_REACHED\(", contents): |
| 47 | notreached_files.append(f.LocalPath()) |
| 48 | |
| 49 | if assert_files: |
| 50 | return [output_api.PresubmitError( |
| 51 | 'These files use ASSERT instead of using DCHECK:', |
| 52 | items=assert_files)] |
| 53 | if notreached_files: |
| 54 | return [output_api.PresubmitError( |
| 55 | 'These files use ASSERT_NOT_REACHED instead of using NOTREACHED:', |
| 56 | items=notreached_files)] |
| 57 | return [] |
| 58 | |
[email protected] | 81d20544 | 2013-07-29 21:42:03 | [diff] [blame] | 59 | def CheckStdAbs(input_api, output_api, |
| 60 | white_list=CC_SOURCE_FILES, black_list=None): |
| 61 | black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST) |
| 62 | source_file_filter = lambda x: input_api.FilterSourceFile(x, |
| 63 | white_list, |
| 64 | black_list) |
| 65 | |
| 66 | using_std_abs_files = [] |
| 67 | found_fabs_files = [] |
| 68 | missing_std_prefix_files = [] |
| 69 | |
| 70 | for f in input_api.AffectedSourceFiles(source_file_filter): |
| 71 | contents = input_api.ReadFile(f, 'rb') |
| 72 | if re.search(r"using std::f?abs;", contents): |
| 73 | using_std_abs_files.append(f.LocalPath()) |
| 74 | if re.search(r"\bfabsf?\(", contents): |
| 75 | found_fabs_files.append(f.LocalPath()); |
[email protected] | dbddc746 | 2013-09-06 06:33:31 | [diff] [blame] | 76 | |
| 77 | no_std_prefix = r"(?<!std::)" |
| 78 | # Matches occurrences of abs/absf/fabs/fabsf without a "std::" prefix. |
| 79 | abs_without_prefix = r"%s(\babsf?\()" % no_std_prefix |
| 80 | fabs_without_prefix = r"%s(\bfabsf?\()" % no_std_prefix |
| 81 | # Skips matching any lines that have "// NOLINT". |
| 82 | no_nolint = r"(?![^\n]*//\s+NOLINT)" |
| 83 | |
| 84 | expression = re.compile("(%s|%s)%s" % |
| 85 | (abs_without_prefix, fabs_without_prefix, no_nolint)) |
| 86 | if expression.search(contents): |
[email protected] | 81d20544 | 2013-07-29 21:42:03 | [diff] [blame] | 87 | missing_std_prefix_files.append(f.LocalPath()) |
| 88 | |
| 89 | result = [] |
| 90 | if using_std_abs_files: |
| 91 | result.append(output_api.PresubmitError( |
| 92 | 'These files have "using std::abs" which is not permitted.', |
| 93 | items=using_std_abs_files)) |
| 94 | if found_fabs_files: |
| 95 | result.append(output_api.PresubmitError( |
| 96 | 'std::abs() should be used instead of std::fabs() for consistency.', |
| 97 | items=found_fabs_files)) |
| 98 | if missing_std_prefix_files: |
| 99 | result.append(output_api.PresubmitError( |
| 100 | 'These files use abs(), absf(), fabs(), or fabsf() without qualifying ' |
| 101 | 'the std namespace. Please use std::abs() in all places.', |
| 102 | items=missing_std_prefix_files)) |
| 103 | return result |
| 104 | |
[email protected] | 7add2e0b | 2013-04-23 05:16:42 | [diff] [blame] | 105 | def CheckPassByValue(input_api, |
| 106 | output_api, |
| 107 | white_list=CC_SOURCE_FILES, |
| 108 | black_list=None): |
| 109 | black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST) |
| 110 | source_file_filter = lambda x: input_api.FilterSourceFile(x, |
| 111 | white_list, |
| 112 | black_list) |
| 113 | |
| 114 | local_errors = [] |
| 115 | |
| 116 | # Well-defined simple classes containing only <= 4 ints, or <= 2 floats. |
| 117 | pass_by_value_types = ['base::Time', |
| 118 | 'base::TimeTicks', |
[email protected] | 7add2e0b | 2013-04-23 05:16:42 | [diff] [blame] | 119 | ] |
| 120 | |
| 121 | for f in input_api.AffectedSourceFiles(source_file_filter): |
| 122 | contents = input_api.ReadFile(f, 'rb') |
| 123 | match = re.search( |
| 124 | r'\bconst +' + '(?P<type>(%s))&' % |
| 125 | string.join(pass_by_value_types, '|'), |
| 126 | contents) |
| 127 | if match: |
| 128 | local_errors.append(output_api.PresubmitError( |
| 129 | '%s passes %s by const ref instead of by value.' % |
| 130 | (f.LocalPath(), match.group('type')))) |
| 131 | return local_errors |
[email protected] | d936f90 | 2013-01-06 05:08:07 | [diff] [blame] | 132 | |
[email protected] | 6dc0b6f | 2013-06-26 20:21:59 | [diff] [blame] | 133 | def CheckTodos(input_api, output_api): |
| 134 | errors = [] |
| 135 | |
| 136 | source_file_filter = lambda x: x |
| 137 | for f in input_api.AffectedSourceFiles(source_file_filter): |
| 138 | contents = input_api.ReadFile(f, 'rb') |
[email protected] | 932aff4 | 2013-06-27 12:59:27 | [diff] [blame] | 139 | if ('FIX'+'ME') in contents: |
[email protected] | 6dc0b6f | 2013-06-26 20:21:59 | [diff] [blame] | 140 | errors.append(f.LocalPath()) |
| 141 | |
| 142 | if errors: |
| 143 | return [output_api.PresubmitError( |
[email protected] | 932aff4 | 2013-06-27 12:59:27 | [diff] [blame] | 144 | 'All TODO comments should be of the form TODO(name).', |
[email protected] | 6dc0b6f | 2013-06-26 20:21:59 | [diff] [blame] | 145 | items=errors)] |
| 146 | return [] |
| 147 | |
[email protected] | e51444a | 2013-12-10 23:05:01 | [diff] [blame] | 148 | def FindUnquotedQuote(contents, pos): |
| 149 | match = re.search(r"(?<!\\)(?P<quote>\")", contents[pos:]) |
| 150 | return -1 if not match else match.start("quote") + pos |
| 151 | |
| 152 | def FindNamespaceInBlock(pos, namespace, contents, whitelist=[]): |
| 153 | open_brace = -1 |
| 154 | close_brace = -1 |
| 155 | quote = -1 |
| 156 | name = -1 |
| 157 | brace_count = 1 |
| 158 | quote_count = 0 |
| 159 | while pos < len(contents) and brace_count > 0: |
| 160 | if open_brace < pos: open_brace = contents.find("{", pos) |
| 161 | if close_brace < pos: close_brace = contents.find("}", pos) |
| 162 | if quote < pos: quote = FindUnquotedQuote(contents, pos) |
| 163 | if name < pos: name = contents.find(("%s::" % namespace), pos) |
| 164 | |
| 165 | if name < 0: |
| 166 | return False # The namespace is not used at all. |
| 167 | if open_brace < 0: |
| 168 | open_brace = len(contents) |
| 169 | if close_brace < 0: |
| 170 | close_brace = len(contents) |
| 171 | if quote < 0: |
| 172 | quote = len(contents) |
| 173 | |
| 174 | next = min(open_brace, min(close_brace, min(quote, name))) |
| 175 | |
| 176 | if next == open_brace: |
| 177 | brace_count += 1 |
| 178 | elif next == close_brace: |
| 179 | brace_count -= 1 |
| 180 | elif next == quote: |
| 181 | quote_count = 0 if quote_count else 1 |
| 182 | elif next == name and not quote_count: |
| 183 | in_whitelist = False |
| 184 | for w in whitelist: |
| 185 | if re.match(w, contents[next:]): |
| 186 | in_whitelist = True |
| 187 | break |
| 188 | if not in_whitelist: |
| 189 | return True |
| 190 | pos = next + 1 |
| 191 | return False |
| 192 | |
| 193 | # Checks for the use of cc:: within the cc namespace, which is usually |
| 194 | # redundant. |
| 195 | def CheckNamespace(input_api, output_api): |
| 196 | errors = [] |
| 197 | |
| 198 | source_file_filter = lambda x: x |
| 199 | for f in input_api.AffectedSourceFiles(source_file_filter): |
| 200 | contents = input_api.ReadFile(f, 'rb') |
| 201 | match = re.search(r'namespace\s*cc\s*{', contents) |
| 202 | if match: |
| 203 | whitelist = [ |
| 204 | r"cc::remove_if\b", |
| 205 | ] |
| 206 | if FindNamespaceInBlock(match.end(), 'cc', contents, whitelist=whitelist): |
| 207 | errors.append(f.LocalPath()) |
| 208 | |
| 209 | if errors: |
| 210 | return [output_api.PresubmitError( |
| 211 | 'Do not use cc:: inside of the cc namespace.', |
| 212 | items=errors)] |
| 213 | return [] |
| 214 | |
[email protected] | 6dc0b6f | 2013-06-26 20:21:59 | [diff] [blame] | 215 | |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 216 | def CheckChangeOnUpload(input_api, output_api): |
| 217 | results = [] |
| 218 | results += CheckAsserts(input_api, output_api) |
[email protected] | 81d20544 | 2013-07-29 21:42:03 | [diff] [blame] | 219 | results += CheckStdAbs(input_api, output_api) |
[email protected] | 7add2e0b | 2013-04-23 05:16:42 | [diff] [blame] | 220 | results += CheckPassByValue(input_api, output_api) |
[email protected] | cf82459 | 2013-04-09 05:46:00 | [diff] [blame] | 221 | results += CheckChangeLintsClean(input_api, output_api) |
[email protected] | 6dc0b6f | 2013-06-26 20:21:59 | [diff] [blame] | 222 | results += CheckTodos(input_api, output_api) |
[email protected] | e51444a | 2013-12-10 23:05:01 | [diff] [blame] | 223 | results += CheckNamespace(input_api, output_api) |
[email protected] | 4f0fd02 | 2014-01-30 08:05:22 | [diff] [blame] | 224 | results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) |
[email protected] | 1d99317 | 2012-10-18 18:15:04 | [diff] [blame] | 225 | return results |
| 226 | |
[email protected] | 69085bc5 | 2012-10-15 16:41:58 | [diff] [blame] | 227 | def GetPreferredTrySlaves(project, change): |
| 228 | return [ |
| 229 | 'linux_layout_rel', |
[email protected] | 43452a3 | 2013-10-23 01:36:58 | [diff] [blame] | 230 | 'linux_gpu', |
| 231 | 'mac_gpu', |
| 232 | 'mac_gpu_retina', |
[email protected] | 94be7f70 | 2014-02-03 19:06:45 | [diff] [blame] | 233 | 'win_gpu', |
[email protected] | e51444a | 2013-12-10 23:05:01 | [diff] [blame] | 234 | ] |