blob: 9acfcb281054a834ff6b9229622417bbf865a886 [file] [log] [blame]
[email protected]a18130a2012-01-03 17:52:081# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ca8d19842009-02-19 16:33:122# 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
[email protected]f1293792009-07-31 18:09:567See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
[email protected]50d7d721e2009-11-15 17:56:188for more details about the presubmit API built into gcl.
[email protected]ca8d19842009-02-19 16:33:129"""
10
[email protected]eea609a2011-11-18 13:10:1211
[email protected]9d16ad12011-12-14 20:49:4712import re
[email protected]fbcafe5a2012-08-08 15:31:2213import subprocess
[email protected]55f9f382012-07-31 11:02:1814import sys
[email protected]9d16ad12011-12-14 20:49:4715
16
[email protected]379e7dd2010-01-28 17:39:2117_EXCLUDED_PATHS = (
[email protected]3e4eb112011-01-18 03:29:5418 r"^breakpad[\\\/].*",
[email protected]40d1dbb2012-10-26 07:18:0019 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_rules.py",
20 r"^native_client_sdk[\\\/]src[\\\/]build_tools[\\\/]make_simple.py",
[email protected]a18130a2012-01-03 17:52:0821 r"^net[\\\/]tools[\\\/]spdyshark[\\\/].*",
[email protected]3e4eb112011-01-18 03:29:5422 r"^skia[\\\/].*",
23 r"^v8[\\\/].*",
24 r".*MakeFile$",
[email protected]1084ccc2012-03-14 03:22:5325 r".+_autogen\.h$",
[email protected]94f206c12012-08-25 00:09:1426 r"^cc[\\\/].*",
[email protected]39849c6c2012-09-14 22:15:5927 r"^webkit[\\\/]compositor_bindings[\\\/].*",
[email protected]ce145c02012-09-06 09:49:3428 r".+[\\\/]pnacl_shim\.c$",
[email protected]4306417642009-06-11 00:33:4029)
[email protected]ca8d19842009-02-19 16:33:1230
[email protected]ca8d19842009-02-19 16:33:1231
[email protected]eea609a2011-11-18 13:10:1232_TEST_ONLY_WARNING = (
33 'You might be calling functions intended only for testing from\n'
34 'production code. It is OK to ignore this warning if you know what\n'
35 'you are doing, as the heuristics used to detect the situation are\n'
36 'not perfect. The commit queue will not block on this warning.\n'
37 'Email [email protected] if you have questions.')
38
39
[email protected]cf9b78f2012-11-14 11:40:2840_INCLUDE_ORDER_WARNING = (
41 'Your #include order seems to be broken. Send mail to\n'
42 '[email protected] if this is not the case.')
43
44
[email protected]127f18ec2012-06-16 05:05:5945_BANNED_OBJC_FUNCTIONS = (
46 (
47 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:2048 (
49 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:5950 'prohibited. Please use CrTrackingArea instead.',
51 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
52 ),
53 False,
54 ),
55 (
56 'NSTrackingArea',
[email protected]23e6cbc2012-06-16 18:51:2057 (
58 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:5959 'instead.',
60 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
61 ),
62 False,
63 ),
64 (
65 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:2066 (
67 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5968 'Please use |convertPoint:(point) fromView:nil| instead.',
69 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
70 ),
71 True,
72 ),
73 (
74 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:2075 (
76 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5977 'Please use |convertPoint:(point) toView:nil| instead.',
78 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
79 ),
80 True,
81 ),
82 (
83 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:2084 (
85 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5986 'Please use |convertRect:(point) fromView:nil| instead.',
87 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
88 ),
89 True,
90 ),
91 (
92 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:2093 (
94 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5995 'Please use |convertRect:(point) toView:nil| instead.',
96 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
97 ),
98 True,
99 ),
100 (
101 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20102 (
103 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59104 'Please use |convertSize:(point) fromView:nil| instead.',
105 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
106 ),
107 True,
108 ),
109 (
110 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20111 (
112 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59113 'Please use |convertSize:(point) toView:nil| instead.',
114 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
115 ),
116 True,
117 ),
118)
119
120
121_BANNED_CPP_FUNCTIONS = (
[email protected]23e6cbc2012-06-16 18:51:20122 # Make sure that gtest's FRIEND_TEST() macro is not used; the
123 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
[email protected]e00ccc92012-11-01 17:32:30124 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
[email protected]23e6cbc2012-06-16 18:51:20125 (
126 'FRIEND_TEST(',
127 (
[email protected]e3c945502012-06-26 20:01:49128 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
[email protected]23e6cbc2012-06-16 18:51:20129 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
130 ),
131 False,
[email protected]7345da02012-11-27 14:31:49132 (),
[email protected]23e6cbc2012-06-16 18:51:20133 ),
134 (
135 'ScopedAllowIO',
136 (
[email protected]e3c945502012-06-26 20:01:49137 'New code should not use ScopedAllowIO. Post a task to the blocking',
138 'pool or the FILE thread instead.',
[email protected]23e6cbc2012-06-16 18:51:20139 ),
[email protected]e3c945502012-06-26 20:01:49140 True,
[email protected]7345da02012-11-27 14:31:49141 (
142 r"^content[\\\/]shell[\\\/]shell_browser_main\.cc$",
143 ),
[email protected]23e6cbc2012-06-16 18:51:20144 ),
145 (
146 'FilePathWatcher::Delegate',
147 (
[email protected]e3c945502012-06-26 20:01:49148 'New code should not use FilePathWatcher::Delegate. Use the callback',
[email protected]23e6cbc2012-06-16 18:51:20149 'interface instead.',
150 ),
151 False,
[email protected]7345da02012-11-27 14:31:49152 (),
[email protected]23e6cbc2012-06-16 18:51:20153 ),
[email protected]e3c945502012-06-26 20:01:49154 (
[email protected]73981ace2012-12-06 20:11:00155 'browser::FindOrCreateTabbedBrowserDeprecated',
[email protected]e3c945502012-06-26 20:01:49156 (
157 'This function is deprecated and we\'re working on removing it. Pass',
158 'more context to get a Browser*, like a WebContents, window, or session',
[email protected]1099dbd2012-11-01 19:56:02159 'id. Talk to robertshield@ for more information.',
[email protected]e3c945502012-06-26 20:01:49160 ),
161 True,
[email protected]7345da02012-11-27 14:31:49162 (),
[email protected]e3c945502012-06-26 20:01:49163 ),
164 (
[email protected]bb5eff1cc2012-11-01 20:46:29165 'RunAllPending()',
166 (
167 'This function is deprecated and we\'re working on removing it. Rename',
168 'to RunUntilIdle',
169 ),
170 True,
[email protected]7345da02012-11-27 14:31:49171 (),
[email protected]bb5eff1cc2012-11-01 20:46:29172 ),
[email protected]127f18ec2012-06-16 05:05:59173)
174
175
[email protected]eea609a2011-11-18 13:10:12176
[email protected]55459852011-08-10 15:17:19177def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
178 """Attempts to prevent use of functions intended only for testing in
179 non-testing code. For now this is just a best-effort implementation
180 that ignores header files and may have some false positives. A
181 better implementation would probably need a proper C++ parser.
182 """
183 # We only scan .cc files and the like, as the declaration of
184 # for-testing functions in header files are hard to distinguish from
185 # calls to such functions without a proper C++ parser.
[email protected]403bfbc92012-06-11 23:30:09186 platform_specifiers = r'(_(android|chromeos|gtk|mac|posix|win))?'
[email protected]55459852011-08-10 15:17:19187 source_extensions = r'\.(cc|cpp|cxx|mm)$'
188 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]19e77fd2011-10-20 05:24:05189 file_exclusion_patterns = (
[email protected]44e376e2012-10-26 19:40:21190 r'.*[/\\](fake_|test_|mock_).+%s' % source_extensions,
[email protected]c762d252012-02-28 02:07:24191 r'.+_test_(base|support|util)%s' % source_extensions,
[email protected]403bfbc92012-06-11 23:30:09192 r'.+_(api|browser|perf|unit|ui)?test%s%s' % (platform_specifiers,
193 source_extensions),
[email protected]19e77fd2011-10-20 05:24:05194 r'.+profile_sync_service_harness%s' % source_extensions,
195 )
196 path_exclusion_patterns = (
197 r'.*[/\\](test|tool(s)?)[/\\].*',
198 # At request of folks maintaining this folder.
199 r'chrome[/\\]browser[/\\]automation[/\\].*',
200 )
[email protected]55459852011-08-10 15:17:19201
202 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
203 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
204 exclusion_pattern = input_api.re.compile(
205 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
206 base_function_pattern, base_function_pattern))
207
208 def FilterFile(affected_file):
[email protected]19e77fd2011-10-20 05:24:05209 black_list = (file_exclusion_patterns + path_exclusion_patterns +
[email protected]3afb12a42011-08-15 13:48:33210 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19211 return input_api.FilterSourceFile(
212 affected_file,
213 white_list=(file_inclusion_pattern, ),
214 black_list=black_list)
215
216 problems = []
217 for f in input_api.AffectedSourceFiles(FilterFile):
218 local_path = f.LocalPath()
219 lines = input_api.ReadFile(f).splitlines()
220 line_number = 0
221 for line in lines:
222 if (inclusion_pattern.search(line) and
223 not exclusion_pattern.search(line)):
224 problems.append(
225 '%s:%d\n %s' % (local_path, line_number, line.strip()))
226 line_number += 1
227
228 if problems:
[email protected]eea609a2011-11-18 13:10:12229 if not input_api.is_committing:
230 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
231 else:
232 # We don't warn on commit, to avoid stopping commits going through CQ.
233 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19234 else:
235 return []
236
237
[email protected]10689ca2011-09-02 02:31:54238def _CheckNoIOStreamInHeaders(input_api, output_api):
239 """Checks to make sure no .h files include <iostream>."""
240 files = []
241 pattern = input_api.re.compile(r'^#include\s*<iostream>',
242 input_api.re.MULTILINE)
243 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
244 if not f.LocalPath().endswith('.h'):
245 continue
246 contents = input_api.ReadFile(f)
247 if pattern.search(contents):
248 files.append(f)
249
250 if len(files):
251 return [ output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:06252 'Do not #include <iostream> in header files, since it inserts static '
253 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:54254 '#include <ostream>. See http://crbug.com/94794',
255 files) ]
256 return []
257
258
[email protected]72df4e782012-06-21 16:28:18259def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
260 """Checks to make sure no source files use UNIT_TEST"""
261 problems = []
262 for f in input_api.AffectedFiles():
263 if (not f.LocalPath().endswith(('.cc', '.mm'))):
264 continue
265
266 for line_num, line in f.ChangedContents():
267 if 'UNIT_TEST' in line:
268 problems.append(' %s:%d' % (f.LocalPath(), line_num))
269
270 if not problems:
271 return []
272 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
273 '\n'.join(problems))]
274
275
[email protected]8ea5d4b2011-09-13 21:49:22276def _CheckNoNewWStrings(input_api, output_api):
277 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27278 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22279 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20280 if (not f.LocalPath().endswith(('.cc', '.h')) or
281 f.LocalPath().endswith('test.cc')):
282 continue
[email protected]8ea5d4b2011-09-13 21:49:22283
[email protected]a11dbe9b2012-08-07 01:32:58284 allowWString = False
[email protected]b5c24292011-11-28 14:38:20285 for line_num, line in f.ChangedContents():
[email protected]a11dbe9b2012-08-07 01:32:58286 if 'presubmit: allow wstring' in line:
287 allowWString = True
288 elif not allowWString and 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27289 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]a11dbe9b2012-08-07 01:32:58290 allowWString = False
291 else:
292 allowWString = False
[email protected]8ea5d4b2011-09-13 21:49:22293
[email protected]55463aa62011-10-12 00:48:27294 if not problems:
295 return []
296 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
[email protected]a11dbe9b2012-08-07 01:32:58297 ' If you are calling a cross-platform API that accepts a wstring, '
298 'fix the API.\n' +
[email protected]55463aa62011-10-12 00:48:27299 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22300
301
[email protected]2a8ac9c2011-10-19 17:20:44302def _CheckNoDEPSGIT(input_api, output_api):
303 """Make sure .DEPS.git is never modified manually."""
304 if any(f.LocalPath().endswith('.DEPS.git') for f in
305 input_api.AffectedFiles()):
306 return [output_api.PresubmitError(
307 'Never commit changes to .DEPS.git. This file is maintained by an\n'
308 'automated system based on what\'s in DEPS and your changes will be\n'
309 'overwritten.\n'
310 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
311 'for more information')]
312 return []
313
314
[email protected]127f18ec2012-06-16 05:05:59315def _CheckNoBannedFunctions(input_api, output_api):
316 """Make sure that banned functions are not used."""
317 warnings = []
318 errors = []
319
320 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
321 for f in input_api.AffectedFiles(file_filter=file_filter):
322 for line_num, line in f.ChangedContents():
323 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
324 if func_name in line:
325 problems = warnings;
326 if error:
327 problems = errors;
328 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
329 for message_line in message:
330 problems.append(' %s' % message_line)
331
332 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
333 for f in input_api.AffectedFiles(file_filter=file_filter):
334 for line_num, line in f.ChangedContents():
[email protected]7345da02012-11-27 14:31:49335 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
336 def IsBlacklisted(affected_file, blacklist):
337 local_path = affected_file.LocalPath()
338 for item in blacklist:
339 if input_api.re.match(item, local_path):
340 return True
341 return False
342 if IsBlacklisted(f, excluded_paths):
343 continue
[email protected]127f18ec2012-06-16 05:05:59344 if func_name in line:
345 problems = warnings;
346 if error:
347 problems = errors;
348 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
349 for message_line in message:
350 problems.append(' %s' % message_line)
351
352 result = []
353 if (warnings):
354 result.append(output_api.PresubmitPromptWarning(
355 'Banned functions were used.\n' + '\n'.join(warnings)))
356 if (errors):
357 result.append(output_api.PresubmitError(
358 'Banned functions were used.\n' + '\n'.join(errors)))
359 return result
360
361
[email protected]6c063c62012-07-11 19:11:06362def _CheckNoPragmaOnce(input_api, output_api):
363 """Make sure that banned functions are not used."""
364 files = []
365 pattern = input_api.re.compile(r'^#pragma\s+once',
366 input_api.re.MULTILINE)
367 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
368 if not f.LocalPath().endswith('.h'):
369 continue
370 contents = input_api.ReadFile(f)
371 if pattern.search(contents):
372 files.append(f)
373
374 if files:
375 return [output_api.PresubmitError(
376 'Do not use #pragma once in header files.\n'
377 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
378 files)]
379 return []
380
[email protected]127f18ec2012-06-16 05:05:59381
[email protected]e7479052012-09-19 00:26:12382def _CheckNoTrinaryTrueFalse(input_api, output_api):
383 """Checks to make sure we don't introduce use of foo ? true : false."""
384 problems = []
385 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
386 for f in input_api.AffectedFiles():
387 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
388 continue
389
390 for line_num, line in f.ChangedContents():
391 if pattern.match(line):
392 problems.append(' %s:%d' % (f.LocalPath(), line_num))
393
394 if not problems:
395 return []
396 return [output_api.PresubmitPromptWarning(
397 'Please consider avoiding the "? true : false" pattern if possible.\n' +
398 '\n'.join(problems))]
399
400
[email protected]55f9f382012-07-31 11:02:18401def _CheckUnwantedDependencies(input_api, output_api):
402 """Runs checkdeps on #include statements added in this
403 change. Breaking - rules is an error, breaking ! rules is a
404 warning.
405 """
406 # We need to wait until we have an input_api object and use this
407 # roundabout construct to import checkdeps because this file is
408 # eval-ed and thus doesn't have __file__.
409 original_sys_path = sys.path
410 try:
411 sys.path = sys.path + [input_api.os_path.join(
412 input_api.PresubmitLocalPath(), 'tools', 'checkdeps')]
413 import checkdeps
414 from cpp_checker import CppChecker
415 from rules import Rule
416 finally:
417 # Restore sys.path to what it was before.
418 sys.path = original_sys_path
419
420 added_includes = []
421 for f in input_api.AffectedFiles():
422 if not CppChecker.IsCppFile(f.LocalPath()):
423 continue
424
425 changed_lines = [line for line_num, line in f.ChangedContents()]
426 added_includes.append([f.LocalPath(), changed_lines])
427
428 deps_checker = checkdeps.DepsChecker()
429
430 error_descriptions = []
431 warning_descriptions = []
432 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
433 added_includes):
434 description_with_path = '%s\n %s' % (path, rule_description)
435 if rule_type == Rule.DISALLOW:
436 error_descriptions.append(description_with_path)
437 else:
438 warning_descriptions.append(description_with_path)
439
440 results = []
441 if error_descriptions:
442 results.append(output_api.PresubmitError(
443 'You added one or more #includes that violate checkdeps rules.',
444 error_descriptions))
445 if warning_descriptions:
[email protected]779caa52012-08-21 17:05:59446 if not input_api.is_committing:
447 warning_factory = output_api.PresubmitPromptWarning
448 else:
449 # We don't want to block use of the CQ when there is a warning
450 # of this kind, so we only show a message when committing.
451 warning_factory = output_api.PresubmitNotifyResult
452 results.append(warning_factory(
[email protected]55f9f382012-07-31 11:02:18453 'You added one or more #includes of files that are temporarily\n'
454 'allowed but being removed. Can you avoid introducing the\n'
455 '#include? See relevant DEPS file(s) for details and contacts.',
456 warning_descriptions))
457 return results
458
459
[email protected]fbcafe5a2012-08-08 15:31:22460def _CheckFilePermissions(input_api, output_api):
461 """Check that all files have their permissions properly set."""
462 args = [sys.executable, 'tools/checkperms/checkperms.py', '--root',
463 input_api.change.RepositoryRoot()]
464 for f in input_api.AffectedFiles():
465 args += ['--file', f.LocalPath()]
466 errors = []
467 (errors, stderrdata) = subprocess.Popen(args).communicate()
468
469 results = []
470 if errors:
[email protected]c8278b32012-10-30 20:35:49471 results.append(output_api.PresubmitError('checkperms.py failed.',
[email protected]fbcafe5a2012-08-08 15:31:22472 errors))
473 return results
474
475
[email protected]c8278b32012-10-30 20:35:49476def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
477 """Makes sure we don't include ui/aura/window_property.h
478 in header files.
479 """
480 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
481 errors = []
482 for f in input_api.AffectedFiles():
483 if not f.LocalPath().endswith('.h'):
484 continue
485 for line_num, line in f.ChangedContents():
486 if pattern.match(line):
487 errors.append(' %s:%d' % (f.LocalPath(), line_num))
488
489 results = []
490 if errors:
491 results.append(output_api.PresubmitError(
492 'Header files should not include ui/aura/window_property.h', errors))
493 return results
494
495
[email protected]cf9b78f2012-11-14 11:40:28496def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
497 """Checks that the lines in scope occur in the right order.
498
499 1. C system files in alphabetical order
500 2. C++ system files in alphabetical order
501 3. Project's .h files
502 """
503
504 c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
505 cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
506 custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
507
508 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
509
510 state = C_SYSTEM_INCLUDES
511
512 previous_line = ''
[email protected]728b9bb2012-11-14 20:38:57513 previous_line_num = 0
[email protected]cf9b78f2012-11-14 11:40:28514 problem_linenums = []
515 for line_num, line in scope:
516 if c_system_include_pattern.match(line):
517 if state != C_SYSTEM_INCLUDES:
[email protected]728b9bb2012-11-14 20:38:57518 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28519 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57520 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28521 elif cpp_system_include_pattern.match(line):
522 if state == C_SYSTEM_INCLUDES:
523 state = CPP_SYSTEM_INCLUDES
524 elif state == CUSTOM_INCLUDES:
[email protected]728b9bb2012-11-14 20:38:57525 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28526 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57527 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28528 elif custom_include_pattern.match(line):
529 if state != CUSTOM_INCLUDES:
530 state = CUSTOM_INCLUDES
531 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57532 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28533 else:
534 problem_linenums.append(line_num)
535 previous_line = line
[email protected]728b9bb2012-11-14 20:38:57536 previous_line_num = line_num
[email protected]cf9b78f2012-11-14 11:40:28537
538 warnings = []
[email protected]728b9bb2012-11-14 20:38:57539 for (line_num, previous_line_num) in problem_linenums:
540 if line_num in changed_linenums or previous_line_num in changed_linenums:
[email protected]cf9b78f2012-11-14 11:40:28541 warnings.append(' %s:%d' % (file_path, line_num))
542 return warnings
543
544
[email protected]ac294a12012-12-06 16:38:43545def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
[email protected]cf9b78f2012-11-14 11:40:28546 """Checks the #include order for the given file f."""
547
[email protected]2299dcf2012-11-15 19:56:24548 system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
[email protected]962f117e2012-11-22 18:11:56549 # Exclude #include <.../...> includes from the check; e.g., <sys/...> includes
550 # often need to appear in a specific order.
551 excluded_include_pattern = input_api.re.compile(r'\s*#include \<.*/.*')
[email protected]2299dcf2012-11-15 19:56:24552 custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
[email protected]ac294a12012-12-06 16:38:43553 if_pattern = (
554 input_api.re.compile(r'\s*#\s*(if|elif|else|endif|define|undef).*'))
[email protected]cf9b78f2012-11-14 11:40:28555
556 contents = f.NewContents()
557 warnings = []
558 line_num = 0
559
[email protected]ac294a12012-12-06 16:38:43560 # Handle the special first include. If the first include file is
561 # some/path/file.h, the corresponding including file can be some/path/file.cc,
562 # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
563 # etc. It's also possible that no special first include exists.
564 for line in contents:
565 line_num += 1
566 if system_include_pattern.match(line):
567 # No special first include -> process the line again along with normal
568 # includes.
569 line_num -= 1
570 break
571 match = custom_include_pattern.match(line)
572 if match:
573 match_dict = match.groupdict()
574 header_basename = input_api.os_path.basename(
575 match_dict['FILE']).replace('.h', '')
576 if header_basename not in input_api.os_path.basename(f.LocalPath()):
[email protected]2299dcf2012-11-15 19:56:24577 # No special first include -> process the line again along with normal
578 # includes.
579 line_num -= 1
[email protected]ac294a12012-12-06 16:38:43580 break
[email protected]cf9b78f2012-11-14 11:40:28581
582 # Split into scopes: Each region between #if and #endif is its own scope.
583 scopes = []
584 current_scope = []
585 for line in contents[line_num:]:
586 line_num += 1
[email protected]2309b0fa02012-11-16 12:18:27587 if if_pattern.match(line):
[email protected]cf9b78f2012-11-14 11:40:28588 scopes.append(current_scope)
589 current_scope = []
[email protected]962f117e2012-11-22 18:11:56590 elif ((system_include_pattern.match(line) or
591 custom_include_pattern.match(line)) and
592 not excluded_include_pattern.match(line)):
[email protected]cf9b78f2012-11-14 11:40:28593 current_scope.append((line_num, line))
594 scopes.append(current_scope)
595
596 for scope in scopes:
597 warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
598 changed_linenums))
599 return warnings
600
601
602def _CheckIncludeOrder(input_api, output_api):
603 """Checks that the #include order is correct.
604
605 1. The corresponding header for source files.
606 2. C system files in alphabetical order
607 3. C++ system files in alphabetical order
608 4. Project's .h files in alphabetical order
609
[email protected]ac294a12012-12-06 16:38:43610 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
611 these rules separately.
[email protected]cf9b78f2012-11-14 11:40:28612 """
613
614 warnings = []
615 for f in input_api.AffectedFiles():
[email protected]ac294a12012-12-06 16:38:43616 if f.LocalPath().endswith(('.cc', '.h')):
617 changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
618 warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
[email protected]cf9b78f2012-11-14 11:40:28619
620 results = []
621 if warnings:
[email protected]120cf540d2012-12-10 17:55:53622 if not input_api.is_committing:
623 results.append(output_api.PresubmitPromptWarning(_INCLUDE_ORDER_WARNING,
624 warnings))
625 else:
626 # We don't warn on commit, to avoid stopping commits going through CQ.
627 results.append(output_api.PresubmitNotifyResult(_INCLUDE_ORDER_WARNING,
628 warnings))
[email protected]cf9b78f2012-11-14 11:40:28629 return results
630
631
[email protected]70ca77752012-11-20 03:45:03632def _CheckForVersionControlConflictsInFile(input_api, f):
633 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
634 errors = []
635 for line_num, line in f.ChangedContents():
636 if pattern.match(line):
637 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
638 return errors
639
640
641def _CheckForVersionControlConflicts(input_api, output_api):
642 """Usually this is not intentional and will cause a compile failure."""
643 errors = []
644 for f in input_api.AffectedFiles():
645 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
646
647 results = []
648 if errors:
649 results.append(output_api.PresubmitError(
650 'Version control conflict markers found, please resolve.', errors))
651 return results
652
653
[email protected]22c9bd72011-03-27 16:47:39654def _CommonChecks(input_api, output_api):
655 """Checks common to both upload and commit."""
656 results = []
657 results.extend(input_api.canned_checks.PanProjectChecks(
658 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
[email protected]66daa702011-05-28 14:41:46659 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19660 results.extend(
661 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54662 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:18663 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22664 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44665 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:59666 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]6c063c62012-07-11 19:11:06667 results.extend(_CheckNoPragmaOnce(input_api, output_api))
[email protected]e7479052012-09-19 00:26:12668 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
[email protected]55f9f382012-07-31 11:02:18669 results.extend(_CheckUnwantedDependencies(input_api, output_api))
[email protected]fbcafe5a2012-08-08 15:31:22670 results.extend(_CheckFilePermissions(input_api, output_api))
[email protected]c8278b32012-10-30 20:35:49671 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
[email protected]2309b0fa02012-11-16 12:18:27672 results.extend(_CheckIncludeOrder(input_api, output_api))
[email protected]70ca77752012-11-20 03:45:03673 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
[email protected]b8079ae4a2012-12-05 19:56:49674 results.extend(_CheckPatchFiles(input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:24675
676 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
677 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
678 input_api, output_api,
679 input_api.PresubmitLocalPath(),
680 whitelist=[r'.+_test\.py$']))
[email protected]22c9bd72011-03-27 16:47:39681 return results
[email protected]1f7b4172010-01-28 01:17:34682
[email protected]b337cb5b2011-01-23 21:24:05683
684def _CheckSubversionConfig(input_api, output_api):
685 """Verifies the subversion config file is correctly setup.
686
687 Checks that autoprops are enabled, returns an error otherwise.
688 """
689 join = input_api.os_path.join
690 if input_api.platform == 'win32':
691 appdata = input_api.environ.get('APPDATA', '')
692 if not appdata:
693 return [output_api.PresubmitError('%APPDATA% is not configured.')]
694 path = join(appdata, 'Subversion', 'config')
695 else:
696 home = input_api.environ.get('HOME', '')
697 if not home:
698 return [output_api.PresubmitError('$HOME is not configured.')]
699 path = join(home, '.subversion', 'config')
700
701 error_msg = (
702 'Please look at http://dev.chromium.org/developers/coding-style to\n'
703 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20704 'properties to simplify the project maintenance.\n'
705 'Pro-tip: just download and install\n'
706 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05707
708 try:
709 lines = open(path, 'r').read().splitlines()
710 # Make sure auto-props is enabled and check for 2 Chromium standard
711 # auto-prop.
712 if (not '*.cc = svn:eol-style=LF' in lines or
713 not '*.pdf = svn:mime-type=application/pdf' in lines or
714 not 'enable-auto-props = yes' in lines):
715 return [
[email protected]79ed7e62011-02-21 21:08:53716 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05717 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56718 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05719 ]
720 except (OSError, IOError):
721 return [
[email protected]79ed7e62011-02-21 21:08:53722 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05723 'Can\'t find your subversion config file.\n' + error_msg)
724 ]
725 return []
726
727
[email protected]66daa702011-05-28 14:41:46728def _CheckAuthorizedAuthor(input_api, output_api):
729 """For non-googler/chromites committers, verify the author's email address is
730 in AUTHORS.
731 """
[email protected]9bb9cb82011-06-13 20:43:01732 # TODO(maruel): Add it to input_api?
733 import fnmatch
734
[email protected]66daa702011-05-28 14:41:46735 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01736 if not author:
737 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46738 return []
[email protected]c99663292011-05-31 19:46:08739 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46740 input_api.PresubmitLocalPath(), 'AUTHORS')
741 valid_authors = (
742 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
743 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18744 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01745 if input_api.verbose:
746 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44747 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46748 return [output_api.PresubmitPromptWarning(
749 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
750 '\n'
751 'http://www.chromium.org/developers/contributing-code and read the '
752 '"Legal" section\n'
753 'If you are a chromite, verify the contributor signed the CLA.') %
754 author)]
755 return []
756
757
[email protected]b8079ae4a2012-12-05 19:56:49758def _CheckPatchFiles(input_api, output_api):
759 problems = [f.LocalPath() for f in input_api.AffectedFiles()
760 if f.LocalPath().endswith(('.orig', '.rej'))]
761 if problems:
762 return [output_api.PresubmitError(
763 "Don't commit .rej and .orig files.", problems)]
764 else:
765 return []
766
767
[email protected]1f7b4172010-01-28 01:17:34768def CheckChangeOnUpload(input_api, output_api):
769 results = []
770 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54771 return results
[email protected]ca8d19842009-02-19 16:33:12772
773
774def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54775 results = []
[email protected]1f7b4172010-01-28 01:17:34776 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51777 # TODO(thestig) temporarily disabled, doesn't work in third_party/
778 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
779 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54780 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27781 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34782 input_api,
783 output_api,
[email protected]4efa42142010-08-26 01:29:26784 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27785 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
[email protected]4ddc5df2011-12-12 03:05:04786 output_api, 'http://codereview.chromium.org',
[email protected]c1ba4c52012-03-09 14:23:28787 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
788 '[email protected]'))
[email protected]806e98e2010-03-19 17:49:27789
[email protected]3e4eb112011-01-18 03:29:54790 results.extend(input_api.canned_checks.CheckChangeHasBugField(
791 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:41792 results.extend(input_api.canned_checks.CheckChangeHasDescription(
793 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05794 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54795 return results
[email protected]ca8d19842009-02-19 16:33:12796
797
[email protected]5efb2a822011-09-27 23:06:13798def GetPreferredTrySlaves(project, change):
[email protected]4ce995ea2012-06-27 02:13:10799 files = change.LocalPaths()
800
[email protected]3019c902012-06-29 00:09:03801 if not files:
802 return []
803
[email protected]d668899a2012-09-06 18:16:59804 if all(re.search('\.(m|mm)$|(^|[/_])mac[/_.]', f) for f in files):
[email protected]641f2e3e2012-09-03 11:16:24805 return ['mac_rel', 'mac_asan']
[email protected]d668899a2012-09-06 18:16:59806 if all(re.search('(^|[/_])win[/_.]', f) for f in files):
[email protected]4ce995ea2012-06-27 02:13:10807 return ['win_rel']
[email protected]d668899a2012-09-06 18:16:59808 if all(re.search('(^|[/_])android[/_.]', f) for f in files):
[email protected]3e2f0402012-11-02 16:28:01809 return ['android_dbg', 'android_clang_dbg']
[email protected]356aa542012-09-19 23:31:29810 if all(re.search('^native_client_sdk', f) for f in files):
811 return ['linux_nacl_sdk', 'win_nacl_sdk', 'mac_nacl_sdk']
[email protected]de142152012-10-03 23:02:45812 if all(re.search('[/_]ios[/_.]', f) for f in files):
813 return ['ios_rel_device', 'ios_dbg_simulator']
[email protected]4ce995ea2012-06-27 02:13:10814
[email protected]3e2f0402012-11-02 16:28:01815 trybots = [
816 'android_clang_dbg',
817 'android_dbg',
818 'ios_dbg_simulator',
819 'ios_rel_device',
820 'linux_asan',
[email protected]95c989162012-11-29 05:58:25821 'linux_aura',
[email protected]3e2f0402012-11-02 16:28:01822 'linux_chromeos',
823 'linux_clang:compile',
824 'linux_rel',
825 'mac_asan',
826 'mac_rel',
[email protected]95c989162012-11-29 05:58:25827 'win_aura',
[email protected]3e2f0402012-11-02 16:28:01828 'win_rel',
829 ]
[email protected]911753b2012-08-02 12:11:54830
831 # Match things like path/aura/file.cc and path/file_aura.cc.
[email protected]95c989162012-11-29 05:58:25832 # Same for chromeos.
833 if any(re.search('[/_](aura|chromeos)', f) for f in files):
[email protected]3e2f0402012-11-02 16:28:01834 trybots += ['linux_chromeos_clang:compile', 'linux_chromeos_asan']
[email protected]4ce995ea2012-06-27 02:13:10835
[email protected]4ce995ea2012-06-27 02:13:10836 return trybots