OLD | NEW |
1 # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 1 # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
2 # | 2 # |
3 # Use of this source code is governed by a BSD-style license | 3 # Use of this source code is governed by a BSD-style license |
4 # that can be found in the LICENSE file in the root of the source | 4 # that can be found in the LICENSE file in the root of the source |
5 # tree. An additional intellectual property rights grant can be found | 5 # tree. An additional intellectual property rights grant can be found |
6 # in the file PATENTS. All contributing project authors may | 6 # in the file PATENTS. All contributing project authors may |
7 # be found in the AUTHORS file in the root of the source tree. | 7 # be found in the AUTHORS file in the root of the source tree. |
8 | 8 |
9 import json | 9 import json |
10 import os | 10 import os |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 99 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
100 cwd=cwd) | 100 cwd=cwd) |
101 stdout = p.stdout.read() | 101 stdout = p.stdout.read() |
102 stderr = p.stderr.read() | 102 stderr = p.stderr.read() |
103 p.wait() | 103 p.wait() |
104 p.stdout.close() | 104 p.stdout.close() |
105 p.stderr.close() | 105 p.stderr.close() |
106 return p.returncode, stdout, stderr | 106 return p.returncode, stdout, stderr |
107 | 107 |
108 | 108 |
109 def _VerifyNativeApiHeadersListIsValid(input_api, output_api): | 109 def VerifyNativeApiHeadersListIsValid(input_api, output_api): |
110 """Ensures the list of native API header directories is up to date.""" | 110 """Ensures the list of native API header directories is up to date.""" |
111 non_existing_paths = [] | 111 non_existing_paths = [] |
112 native_api_full_paths = [ | 112 native_api_full_paths = [ |
113 input_api.os_path.join(input_api.PresubmitLocalPath(), | 113 input_api.os_path.join(input_api.PresubmitLocalPath(), |
114 *path.split('/')) for path in API_DIRS] | 114 *path.split('/')) for path in API_DIRS] |
115 for path in native_api_full_paths: | 115 for path in native_api_full_paths: |
116 if not os.path.isdir(path): | 116 if not os.path.isdir(path): |
117 non_existing_paths.append(path) | 117 non_existing_paths.append(path) |
118 if non_existing_paths: | 118 if non_existing_paths: |
119 return [output_api.PresubmitError( | 119 return [output_api.PresubmitError( |
(...skipping 15 matching lines...) Expand all Loading... |
135 simple, 1-2 weeks might be good; if they need to do serious work, | 135 simple, 1-2 weeks might be good; if they need to do serious work, |
136 up to 3 months may be called for.) | 136 up to 3 months may be called for.) |
137 4. Update/inform existing downstream code owners to stop using the | 137 4. Update/inform existing downstream code owners to stop using the |
138 deprecated stuff. (Send announcements to | 138 deprecated stuff. (Send announcements to |
139 discuss-webrtc@googlegroups.com and webrtc-users@google.com.) | 139 discuss-webrtc@googlegroups.com and webrtc-users@google.com.) |
140 5. Remove the deprecated stuff, once the agreed-upon amount of time | 140 5. Remove the deprecated stuff, once the agreed-upon amount of time |
141 has passed. | 141 has passed. |
142 Related files: | 142 Related files: |
143 """ | 143 """ |
144 | 144 |
145 def _CheckNativeApiHeaderChanges(input_api, output_api): | 145 def CheckNativeApiHeaderChanges(input_api, output_api): |
146 """Checks to remind proper changing of native APIs.""" | 146 """Checks to remind proper changing of native APIs.""" |
147 files = [] | 147 files = [] |
148 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): | 148 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): |
149 if f.LocalPath().endswith('.h'): | 149 if f.LocalPath().endswith('.h'): |
150 for path in API_DIRS: | 150 for path in API_DIRS: |
151 if os.path.dirname(f.LocalPath()) == path: | 151 if os.path.dirname(f.LocalPath()) == path: |
152 files.append(f) | 152 files.append(f) |
153 | 153 |
154 if files: | 154 if files: |
155 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)] | 155 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)] |
156 return [] | 156 return [] |
157 | 157 |
158 | 158 |
159 def _CheckNoIOStreamInHeaders(input_api, output_api): | 159 def CheckNoIOStreamInHeaders(input_api, output_api): |
160 """Checks to make sure no .h files include <iostream>.""" | 160 """Checks to make sure no .h files include <iostream>.""" |
161 files = [] | 161 files = [] |
162 pattern = input_api.re.compile(r'^#include\s*<iostream>', | 162 pattern = input_api.re.compile(r'^#include\s*<iostream>', |
163 input_api.re.MULTILINE) | 163 input_api.re.MULTILINE) |
164 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): | 164 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): |
165 if not f.LocalPath().endswith('.h'): | 165 if not f.LocalPath().endswith('.h'): |
166 continue | 166 continue |
167 contents = input_api.ReadFile(f) | 167 contents = input_api.ReadFile(f) |
168 if pattern.search(contents): | 168 if pattern.search(contents): |
169 files.append(f) | 169 files.append(f) |
170 | 170 |
171 if len(files): | 171 if len(files): |
172 return [output_api.PresubmitError( | 172 return [output_api.PresubmitError( |
173 'Do not #include <iostream> in header files, since it inserts static ' + | 173 'Do not #include <iostream> in header files, since it inserts static ' + |
174 'initialization into every file including the header. Instead, ' + | 174 'initialization into every file including the header. Instead, ' + |
175 '#include <ostream>. See http://crbug.com/94794', | 175 '#include <ostream>. See http://crbug.com/94794', |
176 files)] | 176 files)] |
177 return [] | 177 return [] |
178 | 178 |
179 | 179 |
180 def _CheckNoPragmaOnce(input_api, output_api): | 180 def CheckNoPragmaOnce(input_api, output_api): |
181 """Make sure that banned functions are not used.""" | 181 """Make sure that banned functions are not used.""" |
182 files = [] | 182 files = [] |
183 pattern = input_api.re.compile(r'^#pragma\s+once', | 183 pattern = input_api.re.compile(r'^#pragma\s+once', |
184 input_api.re.MULTILINE) | 184 input_api.re.MULTILINE) |
185 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): | 185 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): |
186 if not f.LocalPath().endswith('.h'): | 186 if not f.LocalPath().endswith('.h'): |
187 continue | 187 continue |
188 contents = input_api.ReadFile(f) | 188 contents = input_api.ReadFile(f) |
189 if pattern.search(contents): | 189 if pattern.search(contents): |
190 files.append(f) | 190 files.append(f) |
191 | 191 |
192 if files: | 192 if files: |
193 return [output_api.PresubmitError( | 193 return [output_api.PresubmitError( |
194 'Do not use #pragma once in header files.\n' | 194 'Do not use #pragma once in header files.\n' |
195 'See http://www.chromium.org/developers/coding-style#TOC-File-headers', | 195 'See http://www.chromium.org/developers/coding-style#TOC-File-headers', |
196 files)] | 196 files)] |
197 return [] | 197 return [] |
198 | 198 |
199 | 199 |
200 def _CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name | 200 def CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name |
201 """Make sure that gtest's FRIEND_TEST() macro is not used, the | 201 """Make sure that gtest's FRIEND_TEST() macro is not used, the |
202 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be | 202 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be |
203 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes.""" | 203 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes.""" |
204 problems = [] | 204 problems = [] |
205 | 205 |
206 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h')) | 206 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h')) |
207 for f in input_api.AffectedFiles(file_filter=file_filter): | 207 for f in input_api.AffectedFiles(file_filter=file_filter): |
208 for line_num, line in f.ChangedContents(): | 208 for line_num, line in f.ChangedContents(): |
209 if 'FRIEND_TEST(' in line: | 209 if 'FRIEND_TEST(' in line: |
210 problems.append(' %s:%d' % (f.LocalPath(), line_num)) | 210 problems.append(' %s:%d' % (f.LocalPath(), line_num)) |
211 | 211 |
212 if not problems: | 212 if not problems: |
213 return [] | 213 return [] |
214 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use ' | 214 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use ' |
215 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and ' | 215 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and ' |
216 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))] | 216 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))] |
217 | 217 |
218 | 218 |
219 def _IsLintBlacklisted(blacklist_paths, file_path): | 219 def IsLintBlacklisted(blacklist_paths, file_path): |
220 """ Checks if a file is blacklisted for lint check.""" | 220 """ Checks if a file is blacklisted for lint check.""" |
221 for path in blacklist_paths: | 221 for path in blacklist_paths: |
222 if file_path == path or os.path.dirname(file_path).startswith(path): | 222 if file_path == path or os.path.dirname(file_path).startswith(path): |
223 return True | 223 return True |
224 return False | 224 return False |
225 | 225 |
226 | 226 |
227 def _CheckApprovedFilesLintClean(input_api, output_api, | 227 def CheckApprovedFilesLintClean(input_api, output_api, |
228 source_file_filter=None): | 228 source_file_filter=None): |
229 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py. | 229 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py. |
230 This check is based on _CheckChangeLintsClean in | 230 This check is based on CheckChangeLintsClean in |
231 depot_tools/presubmit_canned_checks.py but has less filters and only checks | 231 depot_tools/presubmit_canned_checks.py but has less filters and only checks |
232 added files.""" | 232 added files.""" |
233 result = [] | 233 result = [] |
234 | 234 |
235 # Initialize cpplint. | 235 # Initialize cpplint. |
236 import cpplint | 236 import cpplint |
237 # Access to a protected member _XX of a client class | 237 # Access to a protected member _XX of a client class |
238 # pylint: disable=W0212 | 238 # pylint: disable=W0212 |
239 cpplint._cpplint_state.ResetErrorCounts() | 239 cpplint._cpplint_state.ResetErrorCounts() |
240 | 240 |
241 lint_filters = cpplint._Filters() | 241 lint_filters = cpplint._Filters() |
242 lint_filters.extend(BLACKLIST_LINT_FILTERS) | 242 lint_filters.extend(BLACKLIST_LINT_FILTERS) |
243 cpplint._SetFilters(','.join(lint_filters)) | 243 cpplint._SetFilters(','.join(lint_filters)) |
244 | 244 |
245 # Create a platform independent blacklist for cpplint. | 245 # Create a platform independent blacklist for cpplint. |
246 blacklist_paths = [input_api.os_path.join(*path.split('/')) | 246 blacklist_paths = [input_api.os_path.join(*path.split('/')) |
247 for path in CPPLINT_BLACKLIST] | 247 for path in CPPLINT_BLACKLIST] |
248 | 248 |
249 # Use the strictest verbosity level for cpplint.py (level 1) which is the | 249 # Use the strictest verbosity level for cpplint.py (level 1) which is the |
250 # default when running cpplint.py from command line. To make it possible to | 250 # default when running cpplint.py from command line. To make it possible to |
251 # work with not-yet-converted code, we're only applying it to new (or | 251 # work with not-yet-converted code, we're only applying it to new (or |
252 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST. | 252 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST. |
253 verbosity_level = 1 | 253 verbosity_level = 1 |
254 files = [] | 254 files = [] |
255 for f in input_api.AffectedSourceFiles(source_file_filter): | 255 for f in input_api.AffectedSourceFiles(source_file_filter): |
256 # Note that moved/renamed files also count as added. | 256 # Note that moved/renamed files also count as added. |
257 if f.Action() == 'A' or not _IsLintBlacklisted(blacklist_paths, | 257 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths, |
258 f.LocalPath()): | 258 f.LocalPath()): |
259 files.append(f.AbsoluteLocalPath()) | 259 files.append(f.AbsoluteLocalPath()) |
260 | 260 |
261 for file_name in files: | 261 for file_name in files: |
262 cpplint.ProcessFile(file_name, verbosity_level) | 262 cpplint.ProcessFile(file_name, verbosity_level) |
263 | 263 |
264 if cpplint._cpplint_state.error_count > 0: | 264 if cpplint._cpplint_state.error_count > 0: |
265 if input_api.is_committing: | 265 if input_api.is_committing: |
266 res_type = output_api.PresubmitError | 266 res_type = output_api.PresubmitError |
267 else: | 267 else: |
268 res_type = output_api.PresubmitPromptWarning | 268 res_type = output_api.PresubmitPromptWarning |
269 result = [res_type('Changelist failed cpplint.py check.')] | 269 result = [res_type('Changelist failed cpplint.py check.')] |
270 | 270 |
271 return result | 271 return result |
272 | 272 |
273 def _CheckNoSourcesAbove(input_api, gn_files, output_api): | 273 def CheckNoSourcesAbove(input_api, gn_files, output_api): |
274 # Disallow referencing source files with paths above the GN file location. | 274 # Disallow referencing source files with paths above the GN file location. |
275 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]', | 275 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]', |
276 re.MULTILINE | re.DOTALL) | 276 re.MULTILINE | re.DOTALL) |
277 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"') | 277 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"') |
278 violating_gn_files = set() | 278 violating_gn_files = set() |
279 violating_source_entries = [] | 279 violating_source_entries = [] |
280 for gn_file in gn_files: | 280 for gn_file in gn_files: |
281 contents = input_api.ReadFile(gn_file) | 281 contents = input_api.ReadFile(gn_file) |
282 for source_block_match in source_pattern.finditer(contents): | 282 for source_block_match in source_pattern.finditer(contents): |
283 # Find all source list entries starting with ../ in the source block | 283 # Find all source list entries starting with ../ in the source block |
284 # (exclude overrides entries). | 284 # (exclude overrides entries). |
285 for file_list_match in file_pattern.finditer(source_block_match.group(1)): | 285 for file_list_match in file_pattern.finditer(source_block_match.group(1)): |
286 source_file = file_list_match.group(1) | 286 source_file = file_list_match.group(1) |
287 if 'overrides/' not in source_file: | 287 if 'overrides/' not in source_file: |
288 violating_source_entries.append(source_file) | 288 violating_source_entries.append(source_file) |
289 violating_gn_files.add(gn_file) | 289 violating_gn_files.add(gn_file) |
290 if violating_gn_files: | 290 if violating_gn_files: |
291 return [output_api.PresubmitError( | 291 return [output_api.PresubmitError( |
292 'Referencing source files above the directory of the GN file is not ' | 292 'Referencing source files above the directory of the GN file is not ' |
293 'allowed. Please introduce new GN targets in the proper location ' | 293 'allowed. Please introduce new GN targets in the proper location ' |
294 'instead.\n' | 294 'instead.\n' |
295 'Invalid source entries:\n' | 295 'Invalid source entries:\n' |
296 '%s\n' | 296 '%s\n' |
297 'Violating GN files:' % '\n'.join(violating_source_entries), | 297 'Violating GN files:' % '\n'.join(violating_source_entries), |
298 items=violating_gn_files)] | 298 items=violating_gn_files)] |
299 return [] | 299 return [] |
300 | 300 |
301 def _CheckNoMixingCAndCCSources(input_api, gn_files, output_api): | 301 def CheckNoMixingCAndCCSources(input_api, gn_files, output_api): |
302 # Disallow mixing .c and .cc source files in the same target. | 302 # Disallow mixing .c and .cc source files in the same target. |
303 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]', | 303 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]', |
304 re.MULTILINE | re.DOTALL) | 304 re.MULTILINE | re.DOTALL) |
305 file_pattern = input_api.re.compile(r'"(.*)"') | 305 file_pattern = input_api.re.compile(r'"(.*)"') |
306 violating_gn_files = dict() | 306 violating_gn_files = dict() |
307 for gn_file in gn_files: | 307 for gn_file in gn_files: |
308 contents = input_api.ReadFile(gn_file) | 308 contents = input_api.ReadFile(gn_file) |
309 for source_block_match in source_pattern.finditer(contents): | 309 for source_block_match in source_pattern.finditer(contents): |
310 c_files = [] | 310 c_files = [] |
311 cc_files = [] | 311 cc_files = [] |
312 for file_list_match in file_pattern.finditer(source_block_match.group(1)): | 312 for file_list_match in file_pattern.finditer(source_block_match.group(1)): |
313 source_file = file_list_match.group(1) | 313 source_file = file_list_match.group(1) |
314 if source_file.endswith('.c'): | 314 if source_file.endswith('.c'): |
315 c_files.append(source_file) | 315 c_files.append(source_file) |
316 if source_file.endswith('.cc'): | 316 if source_file.endswith('.cc'): |
317 cc_files.append(source_file) | 317 cc_files.append(source_file) |
318 if c_files and cc_files: | 318 if c_files and cc_files: |
319 violating_gn_files[gn_file.LocalPath()] = sorted(c_files + cc_files) | 319 violating_gn_files[gn_file.LocalPath()] = sorted(c_files + cc_files) |
320 if violating_gn_files: | 320 if violating_gn_files: |
321 return [output_api.PresubmitError( | 321 return [output_api.PresubmitError( |
322 'GN targets cannot mix .cc and .c source files. Please create a ' | 322 'GN targets cannot mix .cc and .c source files. Please create a ' |
323 'separate target for each collection of sources.\n' | 323 'separate target for each collection of sources.\n' |
324 'Mixed sources: \n' | 324 'Mixed sources: \n' |
325 '%s\n' | 325 '%s\n' |
326 'Violating GN files:' % json.dumps(violating_gn_files, indent=2), | 326 'Violating GN files:' % json.dumps(violating_gn_files, indent=2), |
327 items=violating_gn_files.keys())] | 327 items=violating_gn_files.keys())] |
328 return [] | 328 return [] |
329 | 329 |
330 def _CheckNoPackageBoundaryViolations(input_api, gn_files, output_api): | 330 def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api): |
331 cwd = input_api.PresubmitLocalPath() | 331 cwd = input_api.PresubmitLocalPath() |
332 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib', | 332 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib', |
333 'check_package_boundaries.py') | 333 'check_package_boundaries.py') |
334 webrtc_path = os.path.join('webrtc') | 334 webrtc_path = os.path.join('webrtc') |
335 command = [sys.executable, script_path, webrtc_path] | 335 command = [sys.executable, script_path, webrtc_path] |
336 command += [gn_file.LocalPath() for gn_file in gn_files] | 336 command += [gn_file.LocalPath() for gn_file in gn_files] |
337 returncode, _, stderr = _RunCommand(command, cwd) | 337 returncode, _, stderr = _RunCommand(command, cwd) |
338 if returncode: | 338 if returncode: |
339 return [output_api.PresubmitError( | 339 return [output_api.PresubmitError( |
340 'There are package boundary violations in the following GN files:\n\n' | 340 'There are package boundary violations in the following GN files:\n\n' |
341 '%s' % stderr)] | 341 '%s' % stderr)] |
342 return [] | 342 return [] |
343 | 343 |
344 def _CheckGnChanges(input_api, output_api): | 344 def CheckGnChanges(input_api, output_api): |
345 source_file_filter = lambda x: input_api.FilterSourceFile( | 345 source_file_filter = lambda x: input_api.FilterSourceFile( |
346 x, white_list=(r'.+\.(gn|gni)$',)) | 346 x, white_list=(r'.+\.(gn|gni)$',)) |
347 | 347 |
348 gn_files = [] | 348 gn_files = [] |
349 for f in input_api.AffectedSourceFiles(source_file_filter): | 349 for f in input_api.AffectedSourceFiles(source_file_filter): |
350 if f.LocalPath().startswith('webrtc'): | 350 if f.LocalPath().startswith('webrtc'): |
351 gn_files.append(f) | 351 gn_files.append(f) |
352 | 352 |
353 result = [] | 353 result = [] |
354 if gn_files: | 354 if gn_files: |
355 result.extend(_CheckNoSourcesAbove(input_api, gn_files, output_api)) | 355 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api)) |
356 result.extend(_CheckNoMixingCAndCCSources(input_api, gn_files, output_api)) | 356 result.extend(CheckNoMixingCAndCCSources(input_api, gn_files, output_api)) |
357 result.extend(_CheckNoPackageBoundaryViolations( | 357 result.extend(CheckNoPackageBoundaryViolations( |
358 input_api, gn_files, output_api)) | 358 input_api, gn_files, output_api)) |
359 return result | 359 return result |
360 | 360 |
361 def _CheckUnwantedDependencies(input_api, output_api): | 361 def CheckUnwantedDependencies(input_api, output_api): |
362 """Runs checkdeps on #include statements added in this | 362 """Runs checkdeps on #include statements added in this |
363 change. Breaking - rules is an error, breaking ! rules is a | 363 change. Breaking - rules is an error, breaking ! rules is a |
364 warning. | 364 warning. |
365 """ | 365 """ |
366 # Copied from Chromium's src/PRESUBMIT.py. | 366 # Copied from Chromium's src/PRESUBMIT.py. |
367 | 367 |
368 # We need to wait until we have an input_api object and use this | 368 # We need to wait until we have an input_api object and use this |
369 # roundabout construct to import checkdeps because this file is | 369 # roundabout construct to import checkdeps because this file is |
370 # eval-ed and thus doesn't have __file__. | 370 # eval-ed and thus doesn't have __file__. |
371 original_sys_path = sys.path | 371 original_sys_path = sys.path |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
415 if warning_descriptions: | 415 if warning_descriptions: |
416 results.append(output_api.PresubmitPromptOrNotify( | 416 results.append(output_api.PresubmitPromptOrNotify( |
417 'You added one or more #includes of files that are temporarily\n' | 417 'You added one or more #includes of files that are temporarily\n' |
418 'allowed but being removed. Can you avoid introducing the\n' | 418 'allowed but being removed. Can you avoid introducing the\n' |
419 '#include? See relevant DEPS file(s) for details and contacts.\n' | 419 '#include? See relevant DEPS file(s) for details and contacts.\n' |
420 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for ' | 420 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for ' |
421 'more details about checkdeps.', | 421 'more details about checkdeps.', |
422 warning_descriptions)) | 422 warning_descriptions)) |
423 return results | 423 return results |
424 | 424 |
425 def _CheckChangeHasBugField(input_api, output_api): | 425 def CheckCommitMessageBugEntry(input_api, output_api): |
| 426 """Check that bug entries are well-formed in commit message.""" |
| 427 bogus_bug_msg = ( |
| 428 'Bogus BUG entry: %s. Please specify the issue tracker prefix and the ' |
| 429 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.') |
| 430 results = [] |
| 431 for bug in (input_api.change.BUG or '').split(','): |
| 432 bug = bug.strip() |
| 433 if bug.lower() == 'none': |
| 434 continue |
| 435 if ':' not in bug: |
| 436 try: |
| 437 if int(bug) > 100000: |
| 438 # Rough indicator for current chromium bugs. |
| 439 prefix_guess = 'chromium' |
| 440 else: |
| 441 prefix_guess = 'webrtc' |
| 442 results.append('BUG entry requires issue tracker prefix, e.g. %s:%s' % |
| 443 (prefix_guess, bug)) |
| 444 except ValueError: |
| 445 results.append(bogus_bug_msg % bug) |
| 446 elif not re.match(r'\w+:\d+', bug): |
| 447 results.append(bogus_bug_msg % bug) |
| 448 return [output_api.PresubmitError(r) for r in results] |
| 449 |
| 450 def CheckChangeHasBugField(input_api, output_api): |
426 """Requires that the changelist have a BUG= field. | 451 """Requires that the changelist have a BUG= field. |
427 | 452 |
428 This check is stricter than the one in depot_tools/presubmit_canned_checks.py | 453 This check is stricter than the one in depot_tools/presubmit_canned_checks.py |
429 since it fails the presubmit if the BUG= field is missing or doesn't contain | 454 since it fails the presubmit if the BUG= field is missing or doesn't contain |
430 a bug reference. | 455 a bug reference. |
431 """ | 456 """ |
432 if input_api.change.BUG: | 457 if input_api.change.BUG: |
433 return [] | 458 return [] |
434 else: | 459 else: |
435 return [output_api.PresubmitError( | 460 return [output_api.PresubmitError( |
436 'The BUG=[bug number] field is mandatory. Please create a bug and ' | 461 'The BUG=[bug number] field is mandatory. Please create a bug and ' |
437 'reference it using either of:\n' | 462 'reference it using either of:\n' |
438 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n' | 463 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n' |
439 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')] | 464 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')] |
440 | 465 |
441 def _CheckJSONParseErrors(input_api, output_api): | 466 def CheckJSONParseErrors(input_api, output_api): |
442 """Check that JSON files do not contain syntax errors.""" | 467 """Check that JSON files do not contain syntax errors.""" |
443 | 468 |
444 def FilterFile(affected_file): | 469 def FilterFile(affected_file): |
445 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json' | 470 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json' |
446 | 471 |
447 def GetJSONParseError(input_api, filename): | 472 def GetJSONParseError(input_api, filename): |
448 try: | 473 try: |
449 contents = input_api.ReadFile(filename) | 474 contents = input_api.ReadFile(filename) |
450 input_api.json.loads(contents) | 475 input_api.json.loads(contents) |
451 except ValueError as e: | 476 except ValueError as e: |
452 return e | 477 return e |
453 return None | 478 return None |
454 | 479 |
455 results = [] | 480 results = [] |
456 for affected_file in input_api.AffectedFiles( | 481 for affected_file in input_api.AffectedFiles( |
457 file_filter=FilterFile, include_deletes=False): | 482 file_filter=FilterFile, include_deletes=False): |
458 parse_error = GetJSONParseError(input_api, | 483 parse_error = GetJSONParseError(input_api, |
459 affected_file.AbsoluteLocalPath()) | 484 affected_file.AbsoluteLocalPath()) |
460 if parse_error: | 485 if parse_error: |
461 results.append(output_api.PresubmitError('%s could not be parsed: %s' % | 486 results.append(output_api.PresubmitError('%s could not be parsed: %s' % |
462 (affected_file.LocalPath(), parse_error))) | 487 (affected_file.LocalPath(), parse_error))) |
463 return results | 488 return results |
464 | 489 |
465 | 490 |
466 def _RunPythonTests(input_api, output_api): | 491 def RunPythonTests(input_api, output_api): |
467 def Join(*args): | 492 def Join(*args): |
468 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args) | 493 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args) |
469 | 494 |
470 test_directories = [ | 495 test_directories = [ |
| 496 '/', |
471 Join('webrtc', 'rtc_tools', 'py_event_log_analyzer'), | 497 Join('webrtc', 'rtc_tools', 'py_event_log_analyzer'), |
472 Join('webrtc', 'rtc_tools'), | 498 Join('webrtc', 'rtc_tools'), |
473 Join('webrtc', 'audio', 'test', 'unittests'), | 499 Join('webrtc', 'audio', 'test', 'unittests'), |
474 ] + [ | 500 ] + [ |
475 root for root, _, files in os.walk(Join('tools_webrtc')) | 501 root for root, _, files in os.walk(Join('tools_webrtc')) |
476 if any(f.endswith('_test.py') for f in files) | 502 if any(f.endswith('_test.py') for f in files) |
477 ] | 503 ] |
478 | 504 |
479 tests = [] | 505 tests = [] |
480 for directory in test_directories: | 506 for directory in test_directories: |
481 tests.extend( | 507 tests.extend( |
482 input_api.canned_checks.GetUnitTestsInDirectory( | 508 input_api.canned_checks.GetUnitTestsInDirectory( |
483 input_api, | 509 input_api, |
484 output_api, | 510 output_api, |
485 directory, | 511 directory, |
486 whitelist=[r'.+_test\.py$'])) | 512 whitelist=[r'.+_test\.py$'])) |
487 return input_api.RunTests(tests, parallel=True) | 513 return input_api.RunTests(tests, parallel=True) |
488 | 514 |
489 | 515 |
490 def _CheckUsageOfGoogleProtobufNamespace(input_api, output_api): | 516 def CheckUsageOfGoogleProtobufNamespace(input_api, output_api): |
491 """Checks that the namespace google::protobuf has not been used.""" | 517 """Checks that the namespace google::protobuf has not been used.""" |
492 files = [] | 518 files = [] |
493 pattern = input_api.re.compile(r'google::protobuf') | 519 pattern = input_api.re.compile(r'google::protobuf') |
494 proto_utils_path = os.path.join('webrtc', 'rtc_base', 'protobuf_utils.h') | 520 proto_utils_path = os.path.join('webrtc', 'rtc_base', 'protobuf_utils.h') |
495 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): | 521 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): |
496 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']: | 522 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']: |
497 continue | 523 continue |
498 contents = input_api.ReadFile(f) | 524 contents = input_api.ReadFile(f) |
499 if pattern.search(contents): | 525 if pattern.search(contents): |
500 files.append(f) | 526 files.append(f) |
501 | 527 |
502 if files: | 528 if files: |
503 return [output_api.PresubmitError( | 529 return [output_api.PresubmitError( |
504 'Please avoid to use namespace `google::protobuf` directly.\n' | 530 'Please avoid to use namespace `google::protobuf` directly.\n' |
505 'Add a using directive in `%s` and include that header instead.' | 531 'Add a using directive in `%s` and include that header instead.' |
506 % proto_utils_path, files)] | 532 % proto_utils_path, files)] |
507 return [] | 533 return [] |
508 | 534 |
509 | 535 |
510 def _CommonChecks(input_api, output_api): | 536 def CommonChecks(input_api, output_api): |
511 """Checks common to both upload and commit.""" | 537 """Checks common to both upload and commit.""" |
512 results = [] | 538 results = [] |
513 # Filter out files that are in objc or ios dirs from being cpplint-ed since | 539 # Filter out files that are in objc or ios dirs from being cpplint-ed since |
514 # they do not follow C++ lint rules. | 540 # they do not follow C++ lint rules. |
515 black_list = input_api.DEFAULT_BLACK_LIST + ( | 541 black_list = input_api.DEFAULT_BLACK_LIST + ( |
516 r".*\bobjc[\\\/].*", | 542 r".*\bobjc[\\\/].*", |
517 r".*objc\.[hcm]+$", | 543 r".*objc\.[hcm]+$", |
518 r"webrtc\/build\/ios\/SDK\/.*", | 544 r"webrtc\/build\/ios\/SDK\/.*", |
519 ) | 545 ) |
520 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list) | 546 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list) |
521 results.extend(_CheckApprovedFilesLintClean( | 547 results.extend(CheckApprovedFilesLintClean( |
522 input_api, output_api, source_file_filter)) | 548 input_api, output_api, source_file_filter)) |
523 results.extend(input_api.canned_checks.RunPylint(input_api, output_api, | 549 results.extend(input_api.canned_checks.RunPylint(input_api, output_api, |
524 black_list=(r'^base[\\\/].*\.py$', | 550 black_list=(r'^base[\\\/].*\.py$', |
525 r'^build[\\\/].*\.py$', | 551 r'^build[\\\/].*\.py$', |
526 r'^buildtools[\\\/].*\.py$', | 552 r'^buildtools[\\\/].*\.py$', |
527 r'^infra[\\\/].*\.py$', | 553 r'^infra[\\\/].*\.py$', |
528 r'^ios[\\\/].*\.py$', | 554 r'^ios[\\\/].*\.py$', |
529 r'^out.*[\\\/].*\.py$', | 555 r'^out.*[\\\/].*\.py$', |
530 r'^testing[\\\/].*\.py$', | 556 r'^testing[\\\/].*\.py$', |
531 r'^third_party[\\\/].*\.py$', | 557 r'^third_party[\\\/].*\.py$', |
(...skipping 25 matching lines...) Expand all Loading... |
557 source_file_filter=hundred_char_sources)) | 583 source_file_filter=hundred_char_sources)) |
558 | 584 |
559 results.extend(input_api.canned_checks.CheckChangeHasNoTabs( | 585 results.extend(input_api.canned_checks.CheckChangeHasNoTabs( |
560 input_api, output_api)) | 586 input_api, output_api)) |
561 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace( | 587 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace( |
562 input_api, output_api)) | 588 input_api, output_api)) |
563 results.extend(input_api.canned_checks.CheckAuthorizedAuthor( | 589 results.extend(input_api.canned_checks.CheckAuthorizedAuthor( |
564 input_api, output_api)) | 590 input_api, output_api)) |
565 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner( | 591 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner( |
566 input_api, output_api)) | 592 input_api, output_api)) |
567 results.extend(_CheckNativeApiHeaderChanges(input_api, output_api)) | 593 results.extend(CheckNativeApiHeaderChanges(input_api, output_api)) |
568 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api)) | 594 results.extend(CheckNoIOStreamInHeaders(input_api, output_api)) |
569 results.extend(_CheckNoPragmaOnce(input_api, output_api)) | 595 results.extend(CheckNoPragmaOnce(input_api, output_api)) |
570 results.extend(_CheckNoFRIEND_TEST(input_api, output_api)) | 596 results.extend(CheckNoFRIEND_TEST(input_api, output_api)) |
571 results.extend(_CheckGnChanges(input_api, output_api)) | 597 results.extend(CheckGnChanges(input_api, output_api)) |
572 results.extend(_CheckUnwantedDependencies(input_api, output_api)) | 598 results.extend(CheckUnwantedDependencies(input_api, output_api)) |
573 results.extend(_CheckJSONParseErrors(input_api, output_api)) | 599 results.extend(CheckJSONParseErrors(input_api, output_api)) |
574 results.extend(_RunPythonTests(input_api, output_api)) | 600 results.extend(RunPythonTests(input_api, output_api)) |
575 results.extend(_CheckUsageOfGoogleProtobufNamespace(input_api, output_api)) | 601 results.extend(CheckUsageOfGoogleProtobufNamespace(input_api, output_api)) |
576 results.extend(_CheckOrphanHeaders(input_api, output_api)) | 602 results.extend(CheckOrphanHeaders(input_api, output_api)) |
577 results.extend(_CheckNewLineAtTheEndOfProtoFiles(input_api, output_api)) | 603 results.extend(CheckNewLineAtTheEndOfProtoFiles(input_api, output_api)) |
578 return results | 604 return results |
579 | 605 |
580 | 606 |
581 def CheckChangeOnUpload(input_api, output_api): | 607 def CheckChangeOnUpload(input_api, output_api): |
582 results = [] | 608 results = [] |
583 results.extend(_CommonChecks(input_api, output_api)) | 609 results.extend(CommonChecks(input_api, output_api)) |
584 results.extend( | 610 results.extend( |
585 input_api.canned_checks.CheckGNFormatted(input_api, output_api)) | 611 input_api.canned_checks.CheckGNFormatted(input_api, output_api)) |
586 return results | 612 return results |
587 | 613 |
588 | 614 |
589 def CheckChangeOnCommit(input_api, output_api): | 615 def CheckChangeOnCommit(input_api, output_api): |
590 results = [] | 616 results = [] |
591 results.extend(_CommonChecks(input_api, output_api)) | 617 results.extend(CommonChecks(input_api, output_api)) |
592 results.extend(_VerifyNativeApiHeadersListIsValid(input_api, output_api)) | 618 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api)) |
593 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api)) | 619 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api)) |
594 results.extend(input_api.canned_checks.CheckChangeWasUploaded( | 620 results.extend(input_api.canned_checks.CheckChangeWasUploaded( |
595 input_api, output_api)) | 621 input_api, output_api)) |
596 results.extend(input_api.canned_checks.CheckChangeHasDescription( | 622 results.extend(input_api.canned_checks.CheckChangeHasDescription( |
597 input_api, output_api)) | 623 input_api, output_api)) |
598 results.extend(_CheckChangeHasBugField(input_api, output_api)) | 624 results.extend(CheckChangeHasBugField(input_api, output_api)) |
| 625 results.extend(CheckCommitMessageBugEntry(input_api, output_api)) |
599 results.extend(input_api.canned_checks.CheckTreeIsOpen( | 626 results.extend(input_api.canned_checks.CheckTreeIsOpen( |
600 input_api, output_api, | 627 input_api, output_api, |
601 json_url='http://webrtc-status.appspot.com/current?format=json')) | 628 json_url='http://webrtc-status.appspot.com/current?format=json')) |
602 return results | 629 return results |
603 | 630 |
604 | 631 |
605 def _CheckOrphanHeaders(input_api, output_api): | 632 def CheckOrphanHeaders(input_api, output_api): |
606 # We need to wait until we have an input_api object and use this | 633 # We need to wait until we have an input_api object and use this |
607 # roundabout construct to import prebubmit_checks_lib because this file is | 634 # roundabout construct to import prebubmit_checks_lib because this file is |
608 # eval-ed and thus doesn't have __file__. | 635 # eval-ed and thus doesn't have __file__. |
609 error_msg = """Header file {} is not listed in any GN target. | 636 error_msg = """Header file {} is not listed in any GN target. |
610 Please create a target or add it to an existing one in {}""" | 637 Please create a target or add it to an existing one in {}""" |
611 results = [] | 638 results = [] |
612 original_sys_path = sys.path | 639 original_sys_path = sys.path |
613 try: | 640 try: |
614 sys.path = sys.path + [input_api.os_path.join( | 641 sys.path = sys.path + [input_api.os_path.join( |
615 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')] | 642 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')] |
616 from check_orphan_headers import GetBuildGnPathFromFilePath | 643 from check_orphan_headers import GetBuildGnPathFromFilePath |
617 from check_orphan_headers import IsHeaderInBuildGn | 644 from check_orphan_headers import IsHeaderInBuildGn |
618 finally: | 645 finally: |
619 # Restore sys.path to what it was before. | 646 # Restore sys.path to what it was before. |
620 sys.path = original_sys_path | 647 sys.path = original_sys_path |
621 | 648 |
622 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): | 649 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): |
623 if f.LocalPath().endswith('.h') and f.Action() == 'A': | 650 if f.LocalPath().endswith('.h') and f.Action() == 'A': |
624 file_path = os.path.abspath(f.LocalPath()) | 651 file_path = os.path.abspath(f.LocalPath()) |
625 root_dir = os.getcwd() | 652 root_dir = os.getcwd() |
626 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists, | 653 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists, |
627 root_dir) | 654 root_dir) |
628 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path) | 655 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path) |
629 if not in_build_gn: | 656 if not in_build_gn: |
630 results.append(output_api.PresubmitError(error_msg.format( | 657 results.append(output_api.PresubmitError(error_msg.format( |
631 file_path, gn_file_path))) | 658 file_path, gn_file_path))) |
632 return results | 659 return results |
633 | 660 |
634 | 661 |
635 def _CheckNewLineAtTheEndOfProtoFiles(input_api, output_api): | 662 def CheckNewLineAtTheEndOfProtoFiles(input_api, output_api): |
636 """Checks that all .proto files are terminated with a newline.""" | 663 """Checks that all .proto files are terminated with a newline.""" |
637 error_msg = 'File {} must end with exactly one newline.' | 664 error_msg = 'File {} must end with exactly one newline.' |
638 results = [] | 665 results = [] |
639 source_file_filter = lambda x: input_api.FilterSourceFile( | 666 source_file_filter = lambda x: input_api.FilterSourceFile( |
640 x, white_list=(r'.+\.proto$',)) | 667 x, white_list=(r'.+\.proto$',)) |
641 for f in input_api.AffectedSourceFiles(source_file_filter): | 668 for f in input_api.AffectedSourceFiles(source_file_filter): |
642 file_path = f.LocalPath() | 669 file_path = f.LocalPath() |
643 with open(file_path) as f: | 670 with open(file_path) as f: |
644 lines = f.readlines() | 671 lines = f.readlines() |
645 if lines[-1] != '\n' or lines[-2] == '\n': | 672 if lines[-1] != '\n' or lines[-2] == '\n': |
646 results.append(output_api.PresubmitError(error_msg.format(file_path))) | 673 results.append(output_api.PresubmitError(error_msg.format(file_path))) |
647 return results | 674 return results |
OLD | NEW |