blob: 2fba7a512289eb58395f5c74c8af493c3b80dc0a [file] [log] [blame]
[email protected]a18130a2012-01-03 17:52:081# Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ca8d1982009-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]ca8d1982009-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]ca8d1982009-02-19 16:33:1230
[email protected]ca8d1982009-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,
132 ),
133 (
134 'ScopedAllowIO',
135 (
[email protected]e3c945502012-06-26 20:01:49136 'New code should not use ScopedAllowIO. Post a task to the blocking',
137 'pool or the FILE thread instead.',
[email protected]23e6cbc2012-06-16 18:51:20138 ),
[email protected]e3c945502012-06-26 20:01:49139 True,
[email protected]23e6cbc2012-06-16 18:51:20140 ),
141 (
142 'FilePathWatcher::Delegate',
143 (
[email protected]e3c945502012-06-26 20:01:49144 'New code should not use FilePathWatcher::Delegate. Use the callback',
[email protected]23e6cbc2012-06-16 18:51:20145 'interface instead.',
146 ),
147 False,
148 ),
[email protected]e3c945502012-06-26 20:01:49149 (
[email protected]aebced32012-11-12 01:32:20150 'chrome::FindLastActiveWithProfile',
[email protected]e3c945502012-06-26 20:01:49151 (
152 'This function is deprecated and we\'re working on removing it. Pass',
153 'more context to get a Browser*, like a WebContents, window, or session',
[email protected]1099dbd2012-11-01 19:56:02154 'id. Talk to robertshield@ for more information.',
[email protected]e3c945502012-06-26 20:01:49155 ),
156 True,
157 ),
158 (
[email protected]e3c945502012-06-26 20:01:49159 'browser::FindAnyBrowser',
160 (
161 'This function is deprecated and we\'re working on removing it. Pass',
162 'more context to get a Browser*, like a WebContents, window, or session',
[email protected]1099dbd2012-11-01 19:56:02163 'id. Talk to robertshield@ for more information.',
[email protected]e3c945502012-06-26 20:01:49164 ),
165 True,
166 ),
167 (
168 'browser::FindOrCreateTabbedBrowser',
169 (
170 'This function is deprecated and we\'re working on removing it. Pass',
171 'more context to get a Browser*, like a WebContents, window, or session',
[email protected]1099dbd2012-11-01 19:56:02172 'id. Talk to robertshield@ for more information.',
[email protected]e3c945502012-06-26 20:01:49173 ),
174 True,
175 ),
176 (
[email protected]1099dbd2012-11-01 19:56:02177 'browser::FindTabbedBrowserDeprecated',
[email protected]e3c945502012-06-26 20:01:49178 (
179 'This function is deprecated and we\'re working on removing it. Pass',
180 'more context to get a Browser*, like a WebContents, window, or session',
[email protected]1099dbd2012-11-01 19:56:02181 'id. Talk to robertshield@ for more information.',
[email protected]e3c945502012-06-26 20:01:49182 ),
183 True,
184 ),
[email protected]bb5eff1cc2012-11-01 20:46:29185 (
186 'RunAllPending()',
187 (
188 'This function is deprecated and we\'re working on removing it. Rename',
189 'to RunUntilIdle',
190 ),
191 True,
192 ),
[email protected]127f18ec2012-06-16 05:05:59193)
194
195
[email protected]eea609a2011-11-18 13:10:12196
[email protected]55459852011-08-10 15:17:19197def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
198 """Attempts to prevent use of functions intended only for testing in
199 non-testing code. For now this is just a best-effort implementation
200 that ignores header files and may have some false positives. A
201 better implementation would probably need a proper C++ parser.
202 """
203 # We only scan .cc files and the like, as the declaration of
204 # for-testing functions in header files are hard to distinguish from
205 # calls to such functions without a proper C++ parser.
[email protected]403bfbc92012-06-11 23:30:09206 platform_specifiers = r'(_(android|chromeos|gtk|mac|posix|win))?'
[email protected]55459852011-08-10 15:17:19207 source_extensions = r'\.(cc|cpp|cxx|mm)$'
208 file_inclusion_pattern = r'.+%s' % source_extensions
[email protected]19e77fd2011-10-20 05:24:05209 file_exclusion_patterns = (
[email protected]44e376e2012-10-26 19:40:21210 r'.*[/\\](fake_|test_|mock_).+%s' % source_extensions,
[email protected]c762d252012-02-28 02:07:24211 r'.+_test_(base|support|util)%s' % source_extensions,
[email protected]403bfbc92012-06-11 23:30:09212 r'.+_(api|browser|perf|unit|ui)?test%s%s' % (platform_specifiers,
213 source_extensions),
[email protected]19e77fd2011-10-20 05:24:05214 r'.+profile_sync_service_harness%s' % source_extensions,
215 )
216 path_exclusion_patterns = (
217 r'.*[/\\](test|tool(s)?)[/\\].*',
218 # At request of folks maintaining this folder.
219 r'chrome[/\\]browser[/\\]automation[/\\].*',
220 )
[email protected]55459852011-08-10 15:17:19221
222 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
223 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
224 exclusion_pattern = input_api.re.compile(
225 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
226 base_function_pattern, base_function_pattern))
227
228 def FilterFile(affected_file):
[email protected]19e77fd2011-10-20 05:24:05229 black_list = (file_exclusion_patterns + path_exclusion_patterns +
[email protected]3afb12a42011-08-15 13:48:33230 _EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19231 return input_api.FilterSourceFile(
232 affected_file,
233 white_list=(file_inclusion_pattern, ),
234 black_list=black_list)
235
236 problems = []
237 for f in input_api.AffectedSourceFiles(FilterFile):
238 local_path = f.LocalPath()
239 lines = input_api.ReadFile(f).splitlines()
240 line_number = 0
241 for line in lines:
242 if (inclusion_pattern.search(line) and
243 not exclusion_pattern.search(line)):
244 problems.append(
245 '%s:%d\n %s' % (local_path, line_number, line.strip()))
246 line_number += 1
247
248 if problems:
[email protected]eea609a2011-11-18 13:10:12249 if not input_api.is_committing:
250 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
251 else:
252 # We don't warn on commit, to avoid stopping commits going through CQ.
253 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19254 else:
255 return []
256
257
[email protected]10689ca2011-09-02 02:31:54258def _CheckNoIOStreamInHeaders(input_api, output_api):
259 """Checks to make sure no .h files include <iostream>."""
260 files = []
261 pattern = input_api.re.compile(r'^#include\s*<iostream>',
262 input_api.re.MULTILINE)
263 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
264 if not f.LocalPath().endswith('.h'):
265 continue
266 contents = input_api.ReadFile(f)
267 if pattern.search(contents):
268 files.append(f)
269
270 if len(files):
271 return [ output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:06272 'Do not #include <iostream> in header files, since it inserts static '
273 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:54274 '#include <ostream>. See http://crbug.com/94794',
275 files) ]
276 return []
277
278
[email protected]72df4e782012-06-21 16:28:18279def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
280 """Checks to make sure no source files use UNIT_TEST"""
281 problems = []
282 for f in input_api.AffectedFiles():
283 if (not f.LocalPath().endswith(('.cc', '.mm'))):
284 continue
285
286 for line_num, line in f.ChangedContents():
287 if 'UNIT_TEST' in line:
288 problems.append(' %s:%d' % (f.LocalPath(), line_num))
289
290 if not problems:
291 return []
292 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
293 '\n'.join(problems))]
294
295
[email protected]8ea5d4b2011-09-13 21:49:22296def _CheckNoNewWStrings(input_api, output_api):
297 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27298 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22299 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20300 if (not f.LocalPath().endswith(('.cc', '.h')) or
301 f.LocalPath().endswith('test.cc')):
302 continue
[email protected]8ea5d4b2011-09-13 21:49:22303
[email protected]a11dbe9b2012-08-07 01:32:58304 allowWString = False
[email protected]b5c24292011-11-28 14:38:20305 for line_num, line in f.ChangedContents():
[email protected]a11dbe9b2012-08-07 01:32:58306 if 'presubmit: allow wstring' in line:
307 allowWString = True
308 elif not allowWString and 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27309 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]a11dbe9b2012-08-07 01:32:58310 allowWString = False
311 else:
312 allowWString = False
[email protected]8ea5d4b2011-09-13 21:49:22313
[email protected]55463aa62011-10-12 00:48:27314 if not problems:
315 return []
316 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
[email protected]a11dbe9b2012-08-07 01:32:58317 ' If you are calling a cross-platform API that accepts a wstring, '
318 'fix the API.\n' +
[email protected]55463aa62011-10-12 00:48:27319 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22320
321
[email protected]2a8ac9c2011-10-19 17:20:44322def _CheckNoDEPSGIT(input_api, output_api):
323 """Make sure .DEPS.git is never modified manually."""
324 if any(f.LocalPath().endswith('.DEPS.git') for f in
325 input_api.AffectedFiles()):
326 return [output_api.PresubmitError(
327 'Never commit changes to .DEPS.git. This file is maintained by an\n'
328 'automated system based on what\'s in DEPS and your changes will be\n'
329 'overwritten.\n'
330 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
331 'for more information')]
332 return []
333
334
[email protected]127f18ec2012-06-16 05:05:59335def _CheckNoBannedFunctions(input_api, output_api):
336 """Make sure that banned functions are not used."""
337 warnings = []
338 errors = []
339
340 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
341 for f in input_api.AffectedFiles(file_filter=file_filter):
342 for line_num, line in f.ChangedContents():
343 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
344 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 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
353 for f in input_api.AffectedFiles(file_filter=file_filter):
354 for line_num, line in f.ChangedContents():
355 for func_name, message, error in _BANNED_CPP_FUNCTIONS:
356 if func_name in line:
357 problems = warnings;
358 if error:
359 problems = errors;
360 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
361 for message_line in message:
362 problems.append(' %s' % message_line)
363
364 result = []
365 if (warnings):
366 result.append(output_api.PresubmitPromptWarning(
367 'Banned functions were used.\n' + '\n'.join(warnings)))
368 if (errors):
369 result.append(output_api.PresubmitError(
370 'Banned functions were used.\n' + '\n'.join(errors)))
371 return result
372
373
[email protected]6c063c62012-07-11 19:11:06374def _CheckNoPragmaOnce(input_api, output_api):
375 """Make sure that banned functions are not used."""
376 files = []
377 pattern = input_api.re.compile(r'^#pragma\s+once',
378 input_api.re.MULTILINE)
379 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
380 if not f.LocalPath().endswith('.h'):
381 continue
382 contents = input_api.ReadFile(f)
383 if pattern.search(contents):
384 files.append(f)
385
386 if files:
387 return [output_api.PresubmitError(
388 'Do not use #pragma once in header files.\n'
389 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
390 files)]
391 return []
392
[email protected]127f18ec2012-06-16 05:05:59393
[email protected]e7479052012-09-19 00:26:12394def _CheckNoTrinaryTrueFalse(input_api, output_api):
395 """Checks to make sure we don't introduce use of foo ? true : false."""
396 problems = []
397 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
398 for f in input_api.AffectedFiles():
399 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
400 continue
401
402 for line_num, line in f.ChangedContents():
403 if pattern.match(line):
404 problems.append(' %s:%d' % (f.LocalPath(), line_num))
405
406 if not problems:
407 return []
408 return [output_api.PresubmitPromptWarning(
409 'Please consider avoiding the "? true : false" pattern if possible.\n' +
410 '\n'.join(problems))]
411
412
[email protected]55f9f382012-07-31 11:02:18413def _CheckUnwantedDependencies(input_api, output_api):
414 """Runs checkdeps on #include statements added in this
415 change. Breaking - rules is an error, breaking ! rules is a
416 warning.
417 """
418 # We need to wait until we have an input_api object and use this
419 # roundabout construct to import checkdeps because this file is
420 # eval-ed and thus doesn't have __file__.
421 original_sys_path = sys.path
422 try:
423 sys.path = sys.path + [input_api.os_path.join(
424 input_api.PresubmitLocalPath(), 'tools', 'checkdeps')]
425 import checkdeps
426 from cpp_checker import CppChecker
427 from rules import Rule
428 finally:
429 # Restore sys.path to what it was before.
430 sys.path = original_sys_path
431
432 added_includes = []
433 for f in input_api.AffectedFiles():
434 if not CppChecker.IsCppFile(f.LocalPath()):
435 continue
436
437 changed_lines = [line for line_num, line in f.ChangedContents()]
438 added_includes.append([f.LocalPath(), changed_lines])
439
440 deps_checker = checkdeps.DepsChecker()
441
442 error_descriptions = []
443 warning_descriptions = []
444 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
445 added_includes):
446 description_with_path = '%s\n %s' % (path, rule_description)
447 if rule_type == Rule.DISALLOW:
448 error_descriptions.append(description_with_path)
449 else:
450 warning_descriptions.append(description_with_path)
451
452 results = []
453 if error_descriptions:
454 results.append(output_api.PresubmitError(
455 'You added one or more #includes that violate checkdeps rules.',
456 error_descriptions))
457 if warning_descriptions:
[email protected]779caa52012-08-21 17:05:59458 if not input_api.is_committing:
459 warning_factory = output_api.PresubmitPromptWarning
460 else:
461 # We don't want to block use of the CQ when there is a warning
462 # of this kind, so we only show a message when committing.
463 warning_factory = output_api.PresubmitNotifyResult
464 results.append(warning_factory(
[email protected]55f9f382012-07-31 11:02:18465 'You added one or more #includes of files that are temporarily\n'
466 'allowed but being removed. Can you avoid introducing the\n'
467 '#include? See relevant DEPS file(s) for details and contacts.',
468 warning_descriptions))
469 return results
470
471
[email protected]fbcafe5a2012-08-08 15:31:22472def _CheckFilePermissions(input_api, output_api):
473 """Check that all files have their permissions properly set."""
474 args = [sys.executable, 'tools/checkperms/checkperms.py', '--root',
475 input_api.change.RepositoryRoot()]
476 for f in input_api.AffectedFiles():
477 args += ['--file', f.LocalPath()]
478 errors = []
479 (errors, stderrdata) = subprocess.Popen(args).communicate()
480
481 results = []
482 if errors:
[email protected]c8278b32012-10-30 20:35:49483 results.append(output_api.PresubmitError('checkperms.py failed.',
[email protected]fbcafe5a2012-08-08 15:31:22484 errors))
485 return results
486
487
[email protected]c8278b32012-10-30 20:35:49488def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
489 """Makes sure we don't include ui/aura/window_property.h
490 in header files.
491 """
492 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
493 errors = []
494 for f in input_api.AffectedFiles():
495 if not f.LocalPath().endswith('.h'):
496 continue
497 for line_num, line in f.ChangedContents():
498 if pattern.match(line):
499 errors.append(' %s:%d' % (f.LocalPath(), line_num))
500
501 results = []
502 if errors:
503 results.append(output_api.PresubmitError(
504 'Header files should not include ui/aura/window_property.h', errors))
505 return results
506
507
[email protected]cf9b78f2012-11-14 11:40:28508def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
509 """Checks that the lines in scope occur in the right order.
510
511 1. C system files in alphabetical order
512 2. C++ system files in alphabetical order
513 3. Project's .h files
514 """
515
516 c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
517 cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
518 custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
519
520 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
521
522 state = C_SYSTEM_INCLUDES
523
524 previous_line = ''
[email protected]728b9bb2012-11-14 20:38:57525 previous_line_num = 0
[email protected]cf9b78f2012-11-14 11:40:28526 problem_linenums = []
527 for line_num, line in scope:
528 if c_system_include_pattern.match(line):
529 if state != C_SYSTEM_INCLUDES:
[email protected]728b9bb2012-11-14 20:38:57530 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28531 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 elif cpp_system_include_pattern.match(line):
534 if state == C_SYSTEM_INCLUDES:
535 state = CPP_SYSTEM_INCLUDES
536 elif state == CUSTOM_INCLUDES:
[email protected]728b9bb2012-11-14 20:38:57537 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28538 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57539 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28540 elif custom_include_pattern.match(line):
541 if state != CUSTOM_INCLUDES:
542 state = CUSTOM_INCLUDES
543 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57544 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28545 else:
546 problem_linenums.append(line_num)
547 previous_line = line
[email protected]728b9bb2012-11-14 20:38:57548 previous_line_num = line_num
[email protected]cf9b78f2012-11-14 11:40:28549
550 warnings = []
[email protected]728b9bb2012-11-14 20:38:57551 for (line_num, previous_line_num) in problem_linenums:
552 if line_num in changed_linenums or previous_line_num in changed_linenums:
[email protected]cf9b78f2012-11-14 11:40:28553 warnings.append(' %s:%d' % (file_path, line_num))
554 return warnings
555
556
[email protected]2299dcf2012-11-15 19:56:24557def _CheckIncludeOrderInFile(input_api, f, is_source, changed_linenums):
[email protected]cf9b78f2012-11-14 11:40:28558 """Checks the #include order for the given file f."""
559
[email protected]2299dcf2012-11-15 19:56:24560 system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
[email protected]962f117e2012-11-22 18:11:56561 # Exclude #include <.../...> includes from the check; e.g., <sys/...> includes
562 # often need to appear in a specific order.
563 excluded_include_pattern = input_api.re.compile(r'\s*#include \<.*/.*')
[email protected]2299dcf2012-11-15 19:56:24564 custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
[email protected]2309b0fa02012-11-16 12:18:27565 if_pattern = input_api.re.compile(r'\s*#\s*(if|elif|else|endif).*')
[email protected]cf9b78f2012-11-14 11:40:28566
567 contents = f.NewContents()
568 warnings = []
569 line_num = 0
570
[email protected]2299dcf2012-11-15 19:56:24571 # Handle the special first include for source files. If the header file is
572 # some/path/file.h, the corresponding source file can be some/path/file.cc,
573 # some/other/path/file.cc, some/path/file_platform.cc etc. It's also possible
574 # that no special first include exists.
[email protected]cf9b78f2012-11-14 11:40:28575 if is_source:
576 for line in contents:
577 line_num += 1
[email protected]2299dcf2012-11-15 19:56:24578 if system_include_pattern.match(line):
579 # No special first include -> process the line again along with normal
580 # includes.
581 line_num -= 1
582 break
583 match = custom_include_pattern.match(line)
584 if match:
585 match_dict = match.groupdict()
586 header_basename = input_api.os_path.basename(
587 match_dict['FILE']).replace('.h', '')
588 if header_basename not in input_api.os_path.basename(f.LocalPath()):
589 # No special first include -> process the line again along with normal
590 # includes.
[email protected]cf9b78f2012-11-14 11:40:28591 line_num -= 1
592 break
593
594 # Split into scopes: Each region between #if and #endif is its own scope.
595 scopes = []
596 current_scope = []
597 for line in contents[line_num:]:
598 line_num += 1
[email protected]2309b0fa02012-11-16 12:18:27599 if if_pattern.match(line):
[email protected]cf9b78f2012-11-14 11:40:28600 scopes.append(current_scope)
601 current_scope = []
[email protected]962f117e2012-11-22 18:11:56602 elif ((system_include_pattern.match(line) or
603 custom_include_pattern.match(line)) and
604 not excluded_include_pattern.match(line)):
[email protected]cf9b78f2012-11-14 11:40:28605 current_scope.append((line_num, line))
606 scopes.append(current_scope)
607
608 for scope in scopes:
609 warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
610 changed_linenums))
611 return warnings
612
613
614def _CheckIncludeOrder(input_api, output_api):
615 """Checks that the #include order is correct.
616
617 1. The corresponding header for source files.
618 2. C system files in alphabetical order
619 3. C++ system files in alphabetical order
620 4. Project's .h files in alphabetical order
621
622 Each region between #if and #endif follows these rules separately.
623 """
624
625 warnings = []
626 for f in input_api.AffectedFiles():
627 changed_linenums = set([line_num for line_num, _ in f.ChangedContents()])
628 if f.LocalPath().endswith('.cc'):
[email protected]2299dcf2012-11-15 19:56:24629 warnings.extend(_CheckIncludeOrderInFile(input_api, f, True,
630 changed_linenums))
[email protected]cf9b78f2012-11-14 11:40:28631 elif f.LocalPath().endswith('.h'):
[email protected]2299dcf2012-11-15 19:56:24632 warnings.extend(_CheckIncludeOrderInFile(input_api, f, False,
633 changed_linenums))
[email protected]cf9b78f2012-11-14 11:40:28634
635 results = []
636 if warnings:
637 results.append(output_api.PresubmitPromptWarning(_INCLUDE_ORDER_WARNING,
638 warnings))
639 return results
640
641
[email protected]70ca77752012-11-20 03:45:03642def _CheckForVersionControlConflictsInFile(input_api, f):
643 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
644 errors = []
645 for line_num, line in f.ChangedContents():
646 if pattern.match(line):
647 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
648 return errors
649
650
651def _CheckForVersionControlConflicts(input_api, output_api):
652 """Usually this is not intentional and will cause a compile failure."""
653 errors = []
654 for f in input_api.AffectedFiles():
655 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
656
657 results = []
658 if errors:
659 results.append(output_api.PresubmitError(
660 'Version control conflict markers found, please resolve.', errors))
661 return results
662
663
[email protected]22c9bd72011-03-27 16:47:39664def _CommonChecks(input_api, output_api):
665 """Checks common to both upload and commit."""
666 results = []
667 results.extend(input_api.canned_checks.PanProjectChecks(
668 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
[email protected]66daa702011-05-28 14:41:46669 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19670 results.extend(
671 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54672 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:18673 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22674 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44675 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:59676 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]6c063c62012-07-11 19:11:06677 results.extend(_CheckNoPragmaOnce(input_api, output_api))
[email protected]e7479052012-09-19 00:26:12678 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
[email protected]55f9f382012-07-31 11:02:18679 results.extend(_CheckUnwantedDependencies(input_api, output_api))
[email protected]fbcafe5a2012-08-08 15:31:22680 results.extend(_CheckFilePermissions(input_api, output_api))
[email protected]c8278b32012-10-30 20:35:49681 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
[email protected]2309b0fa02012-11-16 12:18:27682 results.extend(_CheckIncludeOrder(input_api, output_api))
[email protected]70ca77752012-11-20 03:45:03683 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:24684
685 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
686 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
687 input_api, output_api,
688 input_api.PresubmitLocalPath(),
689 whitelist=[r'.+_test\.py$']))
[email protected]22c9bd72011-03-27 16:47:39690 return results
[email protected]1f7b4172010-01-28 01:17:34691
[email protected]b337cb5b2011-01-23 21:24:05692
693def _CheckSubversionConfig(input_api, output_api):
694 """Verifies the subversion config file is correctly setup.
695
696 Checks that autoprops are enabled, returns an error otherwise.
697 """
698 join = input_api.os_path.join
699 if input_api.platform == 'win32':
700 appdata = input_api.environ.get('APPDATA', '')
701 if not appdata:
702 return [output_api.PresubmitError('%APPDATA% is not configured.')]
703 path = join(appdata, 'Subversion', 'config')
704 else:
705 home = input_api.environ.get('HOME', '')
706 if not home:
707 return [output_api.PresubmitError('$HOME is not configured.')]
708 path = join(home, '.subversion', 'config')
709
710 error_msg = (
711 'Please look at http://dev.chromium.org/developers/coding-style to\n'
712 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20713 'properties to simplify the project maintenance.\n'
714 'Pro-tip: just download and install\n'
715 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05716
717 try:
718 lines = open(path, 'r').read().splitlines()
719 # Make sure auto-props is enabled and check for 2 Chromium standard
720 # auto-prop.
721 if (not '*.cc = svn:eol-style=LF' in lines or
722 not '*.pdf = svn:mime-type=application/pdf' in lines or
723 not 'enable-auto-props = yes' in lines):
724 return [
[email protected]79ed7e62011-02-21 21:08:53725 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05726 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56727 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05728 ]
729 except (OSError, IOError):
730 return [
[email protected]79ed7e62011-02-21 21:08:53731 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05732 'Can\'t find your subversion config file.\n' + error_msg)
733 ]
734 return []
735
736
[email protected]66daa702011-05-28 14:41:46737def _CheckAuthorizedAuthor(input_api, output_api):
738 """For non-googler/chromites committers, verify the author's email address is
739 in AUTHORS.
740 """
[email protected]9bb9cb82011-06-13 20:43:01741 # TODO(maruel): Add it to input_api?
742 import fnmatch
743
[email protected]66daa702011-05-28 14:41:46744 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01745 if not author:
746 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46747 return []
[email protected]c99663292011-05-31 19:46:08748 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46749 input_api.PresubmitLocalPath(), 'AUTHORS')
750 valid_authors = (
751 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
752 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18753 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]9bb9cb82011-06-13 20:43:01754 if input_api.verbose:
755 print 'Valid authors are %s' % ', '.join(valid_authors)
[email protected]d8b50be2011-06-15 14:19:44756 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]66daa702011-05-28 14:41:46757 return [output_api.PresubmitPromptWarning(
758 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
759 '\n'
760 'http://www.chromium.org/developers/contributing-code and read the '
761 '"Legal" section\n'
762 'If you are a chromite, verify the contributor signed the CLA.') %
763 author)]
764 return []
765
766
[email protected]1f7b4172010-01-28 01:17:34767def CheckChangeOnUpload(input_api, output_api):
768 results = []
769 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54770 return results
[email protected]ca8d1982009-02-19 16:33:12771
772
773def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54774 results = []
[email protected]1f7b4172010-01-28 01:17:34775 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51776 # TODO(thestig) temporarily disabled, doesn't work in third_party/
777 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
778 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54779 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27780 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34781 input_api,
782 output_api,
[email protected]4efa42142010-08-26 01:29:26783 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27784 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
[email protected]4ddc5df2011-12-12 03:05:04785 output_api, 'http://codereview.chromium.org',
[email protected]c1ba4c52012-03-09 14:23:28786 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
787 '[email protected]'))
[email protected]806e98e2010-03-19 17:49:27788
[email protected]3e4eb112011-01-18 03:29:54789 results.extend(input_api.canned_checks.CheckChangeHasBugField(
790 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:41791 results.extend(input_api.canned_checks.CheckChangeHasDescription(
792 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05793 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54794 return results
[email protected]ca8d1982009-02-19 16:33:12795
796
[email protected]5efb2a822011-09-27 23:06:13797def GetPreferredTrySlaves(project, change):
[email protected]4ce995ea2012-06-27 02:13:10798 files = change.LocalPaths()
799
[email protected]3019c902012-06-29 00:09:03800 if not files:
801 return []
802
[email protected]d668899a2012-09-06 18:16:59803 if all(re.search('\.(m|mm)$|(^|[/_])mac[/_.]', f) for f in files):
[email protected]641f2e3e2012-09-03 11:16:24804 return ['mac_rel', 'mac_asan']
[email protected]d668899a2012-09-06 18:16:59805 if all(re.search('(^|[/_])win[/_.]', f) for f in files):
[email protected]4ce995ea2012-06-27 02:13:10806 return ['win_rel']
[email protected]d668899a2012-09-06 18:16:59807 if all(re.search('(^|[/_])android[/_.]', f) for f in files):
[email protected]3e2f0402012-11-02 16:28:01808 return ['android_dbg', 'android_clang_dbg']
[email protected]356aa542012-09-19 23:31:29809 if all(re.search('^native_client_sdk', f) for f in files):
810 return ['linux_nacl_sdk', 'win_nacl_sdk', 'mac_nacl_sdk']
[email protected]de142152012-10-03 23:02:45811 if all(re.search('[/_]ios[/_.]', f) for f in files):
812 return ['ios_rel_device', 'ios_dbg_simulator']
[email protected]4ce995ea2012-06-27 02:13:10813
[email protected]3e2f0402012-11-02 16:28:01814 trybots = [
815 'android_clang_dbg',
816 'android_dbg',
817 'ios_dbg_simulator',
818 'ios_rel_device',
819 'linux_asan',
820 'linux_chromeos',
821 'linux_clang:compile',
822 'linux_rel',
823 'mac_asan',
824 'mac_rel',
825 'win_rel',
826 ]
[email protected]911753b2012-08-02 12:11:54827
828 # Match things like path/aura/file.cc and path/file_aura.cc.
[email protected]0be9553a2012-08-10 00:14:45829 # Same for ash and chromeos.
830 if any(re.search('[/_](ash|aura)', f) for f in files):
[email protected]3e2f0402012-11-02 16:28:01831 trybots += ['linux_chromeos_clang:compile', 'win_aura',
[email protected]641f2e3e2012-09-03 11:16:24832 'linux_chromeos_asan']
[email protected]3e2f0402012-11-02 16:28:01833 elif any(re.search('[/_]chromeos', f) for f in files):
834 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