blob: a7384778306a74f5f4a0eea102bff9ba60509e61 [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]06e6d0ff2012-12-11 01:36:4431# Fragment of a regular expression that matches file name suffixes
32# used to indicate different platforms.
33_PLATFORM_SPECIFIERS = r'(_(android|chromeos|gtk|mac|posix|win))?'
34
35# Fragment of a regular expression that matches C++ and Objective-C++
36# implementation files.
37_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
38
39# Regular expression that matches code only used for test binaries
40# (best effort).
41_TEST_CODE_EXCLUDED_PATHS = (
42 r'.*[/\\](fake_|test_|mock_).+%s' % _IMPLEMENTATION_EXTENSIONS,
43 r'.+_test_(base|support|util)%s' % _IMPLEMENTATION_EXTENSIONS,
44 r'.+_(api|browser|perf|unit|ui)?test%s%s' % (_PLATFORM_SPECIFIERS,
45 _IMPLEMENTATION_EXTENSIONS),
46 r'.+profile_sync_service_harness%s' % _IMPLEMENTATION_EXTENSIONS,
47 r'.*[/\\](test|tool(s)?)[/\\].*',
48 # At request of folks maintaining this folder.
49 r'chrome[/\\]browser[/\\]automation[/\\].*',
50)
[email protected]ca8d1982009-02-19 16:33:1251
[email protected]eea609a2011-11-18 13:10:1252_TEST_ONLY_WARNING = (
53 'You might be calling functions intended only for testing from\n'
54 'production code. It is OK to ignore this warning if you know what\n'
55 'you are doing, as the heuristics used to detect the situation are\n'
56 'not perfect. The commit queue will not block on this warning.\n'
57 'Email [email protected] if you have questions.')
58
59
[email protected]cf9b78f2012-11-14 11:40:2860_INCLUDE_ORDER_WARNING = (
61 'Your #include order seems to be broken. Send mail to\n'
62 '[email protected] if this is not the case.')
63
64
[email protected]127f18ec2012-06-16 05:05:5965_BANNED_OBJC_FUNCTIONS = (
66 (
67 'addTrackingRect:',
[email protected]23e6cbc2012-06-16 18:51:2068 (
69 'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
[email protected]127f18ec2012-06-16 05:05:5970 'prohibited. Please use CrTrackingArea instead.',
71 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
72 ),
73 False,
74 ),
75 (
76 'NSTrackingArea',
[email protected]23e6cbc2012-06-16 18:51:2077 (
78 'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
[email protected]127f18ec2012-06-16 05:05:5979 'instead.',
80 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
81 ),
82 False,
83 ),
84 (
85 'convertPointFromBase:',
[email protected]23e6cbc2012-06-16 18:51:2086 (
87 'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5988 'Please use |convertPoint:(point) fromView:nil| instead.',
89 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
90 ),
91 True,
92 ),
93 (
94 'convertPointToBase:',
[email protected]23e6cbc2012-06-16 18:51:2095 (
96 'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:5997 'Please use |convertPoint:(point) toView:nil| instead.',
98 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
99 ),
100 True,
101 ),
102 (
103 'convertRectFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20104 (
105 'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59106 'Please use |convertRect:(point) fromView:nil| instead.',
107 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
108 ),
109 True,
110 ),
111 (
112 'convertRectToBase:',
[email protected]23e6cbc2012-06-16 18:51:20113 (
114 'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59115 'Please use |convertRect:(point) toView:nil| instead.',
116 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
117 ),
118 True,
119 ),
120 (
121 'convertSizeFromBase:',
[email protected]23e6cbc2012-06-16 18:51:20122 (
123 'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59124 'Please use |convertSize:(point) fromView:nil| instead.',
125 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
126 ),
127 True,
128 ),
129 (
130 'convertSizeToBase:',
[email protected]23e6cbc2012-06-16 18:51:20131 (
132 'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
[email protected]127f18ec2012-06-16 05:05:59133 'Please use |convertSize:(point) toView:nil| instead.',
134 'http://dev.chromium.org/developers/coding-style/cocoa-dos-and-donts',
135 ),
136 True,
137 ),
138)
139
140
141_BANNED_CPP_FUNCTIONS = (
[email protected]23e6cbc2012-06-16 18:51:20142 # Make sure that gtest's FRIEND_TEST() macro is not used; the
143 # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
[email protected]e00ccc92012-11-01 17:32:30144 # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
[email protected]23e6cbc2012-06-16 18:51:20145 (
146 'FRIEND_TEST(',
147 (
[email protected]e3c945502012-06-26 20:01:49148 'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
[email protected]23e6cbc2012-06-16 18:51:20149 'base/gtest_prod_util.h and use FRIEND_TEST_ALL_PREFIXES() instead.',
150 ),
151 False,
[email protected]7345da02012-11-27 14:31:49152 (),
[email protected]23e6cbc2012-06-16 18:51:20153 ),
154 (
155 'ScopedAllowIO',
156 (
[email protected]e3c945502012-06-26 20:01:49157 'New code should not use ScopedAllowIO. Post a task to the blocking',
158 'pool or the FILE thread instead.',
[email protected]23e6cbc2012-06-16 18:51:20159 ),
[email protected]e3c945502012-06-26 20:01:49160 True,
[email protected]7345da02012-11-27 14:31:49161 (
162 r"^content[\\\/]shell[\\\/]shell_browser_main\.cc$",
163 ),
[email protected]23e6cbc2012-06-16 18:51:20164 ),
165 (
166 'FilePathWatcher::Delegate',
167 (
[email protected]e3c945502012-06-26 20:01:49168 'New code should not use FilePathWatcher::Delegate. Use the callback',
[email protected]23e6cbc2012-06-16 18:51:20169 'interface instead.',
170 ),
171 False,
[email protected]7345da02012-11-27 14:31:49172 (),
[email protected]23e6cbc2012-06-16 18:51:20173 ),
[email protected]127f18ec2012-06-16 05:05:59174)
175
176
[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]06e6d0ff2012-12-11 01:36:44186 file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS
[email protected]55459852011-08-10 15:17:19187
188 base_function_pattern = r'ForTest(ing)?|for_test(ing)?'
189 inclusion_pattern = input_api.re.compile(r'(%s)\s*\(' % base_function_pattern)
190 exclusion_pattern = input_api.re.compile(
191 r'::[A-Za-z0-9_]+(%s)|(%s)[^;]+\{' % (
192 base_function_pattern, base_function_pattern))
193
194 def FilterFile(affected_file):
[email protected]06e6d0ff2012-12-11 01:36:44195 black_list = (_EXCLUDED_PATHS +
196 _TEST_CODE_EXCLUDED_PATHS +
197 input_api.DEFAULT_BLACK_LIST)
[email protected]55459852011-08-10 15:17:19198 return input_api.FilterSourceFile(
199 affected_file,
200 white_list=(file_inclusion_pattern, ),
201 black_list=black_list)
202
203 problems = []
204 for f in input_api.AffectedSourceFiles(FilterFile):
205 local_path = f.LocalPath()
206 lines = input_api.ReadFile(f).splitlines()
207 line_number = 0
208 for line in lines:
209 if (inclusion_pattern.search(line) and
210 not exclusion_pattern.search(line)):
211 problems.append(
212 '%s:%d\n %s' % (local_path, line_number, line.strip()))
213 line_number += 1
214
215 if problems:
[email protected]eea609a2011-11-18 13:10:12216 if not input_api.is_committing:
217 return [output_api.PresubmitPromptWarning(_TEST_ONLY_WARNING, problems)]
218 else:
219 # We don't warn on commit, to avoid stopping commits going through CQ.
220 return [output_api.PresubmitNotifyResult(_TEST_ONLY_WARNING, problems)]
[email protected]55459852011-08-10 15:17:19221 else:
222 return []
223
224
[email protected]10689ca2011-09-02 02:31:54225def _CheckNoIOStreamInHeaders(input_api, output_api):
226 """Checks to make sure no .h files include <iostream>."""
227 files = []
228 pattern = input_api.re.compile(r'^#include\s*<iostream>',
229 input_api.re.MULTILINE)
230 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
231 if not f.LocalPath().endswith('.h'):
232 continue
233 contents = input_api.ReadFile(f)
234 if pattern.search(contents):
235 files.append(f)
236
237 if len(files):
238 return [ output_api.PresubmitError(
[email protected]6c063c62012-07-11 19:11:06239 'Do not #include <iostream> in header files, since it inserts static '
240 'initialization into every file including the header. Instead, '
[email protected]10689ca2011-09-02 02:31:54241 '#include <ostream>. See http://crbug.com/94794',
242 files) ]
243 return []
244
245
[email protected]72df4e782012-06-21 16:28:18246def _CheckNoUNIT_TESTInSourceFiles(input_api, output_api):
247 """Checks to make sure no source files use UNIT_TEST"""
248 problems = []
249 for f in input_api.AffectedFiles():
250 if (not f.LocalPath().endswith(('.cc', '.mm'))):
251 continue
252
253 for line_num, line in f.ChangedContents():
254 if 'UNIT_TEST' in line:
255 problems.append(' %s:%d' % (f.LocalPath(), line_num))
256
257 if not problems:
258 return []
259 return [output_api.PresubmitPromptWarning('UNIT_TEST is only for headers.\n' +
260 '\n'.join(problems))]
261
262
[email protected]8ea5d4b2011-09-13 21:49:22263def _CheckNoNewWStrings(input_api, output_api):
264 """Checks to make sure we don't introduce use of wstrings."""
[email protected]55463aa62011-10-12 00:48:27265 problems = []
[email protected]8ea5d4b2011-09-13 21:49:22266 for f in input_api.AffectedFiles():
[email protected]b5c24292011-11-28 14:38:20267 if (not f.LocalPath().endswith(('.cc', '.h')) or
268 f.LocalPath().endswith('test.cc')):
269 continue
[email protected]8ea5d4b2011-09-13 21:49:22270
[email protected]a11dbe9b2012-08-07 01:32:58271 allowWString = False
[email protected]b5c24292011-11-28 14:38:20272 for line_num, line in f.ChangedContents():
[email protected]a11dbe9b2012-08-07 01:32:58273 if 'presubmit: allow wstring' in line:
274 allowWString = True
275 elif not allowWString and 'wstring' in line:
[email protected]55463aa62011-10-12 00:48:27276 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]a11dbe9b2012-08-07 01:32:58277 allowWString = False
278 else:
279 allowWString = False
[email protected]8ea5d4b2011-09-13 21:49:22280
[email protected]55463aa62011-10-12 00:48:27281 if not problems:
282 return []
283 return [output_api.PresubmitPromptWarning('New code should not use wstrings.'
[email protected]a11dbe9b2012-08-07 01:32:58284 ' If you are calling a cross-platform API that accepts a wstring, '
285 'fix the API.\n' +
[email protected]55463aa62011-10-12 00:48:27286 '\n'.join(problems))]
[email protected]8ea5d4b2011-09-13 21:49:22287
288
[email protected]2a8ac9c2011-10-19 17:20:44289def _CheckNoDEPSGIT(input_api, output_api):
290 """Make sure .DEPS.git is never modified manually."""
291 if any(f.LocalPath().endswith('.DEPS.git') for f in
292 input_api.AffectedFiles()):
293 return [output_api.PresubmitError(
294 'Never commit changes to .DEPS.git. This file is maintained by an\n'
295 'automated system based on what\'s in DEPS and your changes will be\n'
296 'overwritten.\n'
297 'See http://code.google.com/p/chromium/wiki/UsingNewGit#Rolling_DEPS\n'
298 'for more information')]
299 return []
300
301
[email protected]127f18ec2012-06-16 05:05:59302def _CheckNoBannedFunctions(input_api, output_api):
303 """Make sure that banned functions are not used."""
304 warnings = []
305 errors = []
306
307 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
308 for f in input_api.AffectedFiles(file_filter=file_filter):
309 for line_num, line in f.ChangedContents():
310 for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
311 if func_name in line:
312 problems = warnings;
313 if error:
314 problems = errors;
315 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
316 for message_line in message:
317 problems.append(' %s' % message_line)
318
319 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
320 for f in input_api.AffectedFiles(file_filter=file_filter):
321 for line_num, line in f.ChangedContents():
[email protected]7345da02012-11-27 14:31:49322 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
323 def IsBlacklisted(affected_file, blacklist):
324 local_path = affected_file.LocalPath()
325 for item in blacklist:
326 if input_api.re.match(item, local_path):
327 return True
328 return False
329 if IsBlacklisted(f, excluded_paths):
330 continue
[email protected]127f18ec2012-06-16 05:05:59331 if func_name in line:
332 problems = warnings;
333 if error:
334 problems = errors;
335 problems.append(' %s:%d:' % (f.LocalPath(), line_num))
336 for message_line in message:
337 problems.append(' %s' % message_line)
338
339 result = []
340 if (warnings):
341 result.append(output_api.PresubmitPromptWarning(
342 'Banned functions were used.\n' + '\n'.join(warnings)))
343 if (errors):
344 result.append(output_api.PresubmitError(
345 'Banned functions were used.\n' + '\n'.join(errors)))
346 return result
347
348
[email protected]6c063c62012-07-11 19:11:06349def _CheckNoPragmaOnce(input_api, output_api):
350 """Make sure that banned functions are not used."""
351 files = []
352 pattern = input_api.re.compile(r'^#pragma\s+once',
353 input_api.re.MULTILINE)
354 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
355 if not f.LocalPath().endswith('.h'):
356 continue
357 contents = input_api.ReadFile(f)
358 if pattern.search(contents):
359 files.append(f)
360
361 if files:
362 return [output_api.PresubmitError(
363 'Do not use #pragma once in header files.\n'
364 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
365 files)]
366 return []
367
[email protected]127f18ec2012-06-16 05:05:59368
[email protected]e7479052012-09-19 00:26:12369def _CheckNoTrinaryTrueFalse(input_api, output_api):
370 """Checks to make sure we don't introduce use of foo ? true : false."""
371 problems = []
372 pattern = input_api.re.compile(r'\?\s*(true|false)\s*:\s*(true|false)')
373 for f in input_api.AffectedFiles():
374 if not f.LocalPath().endswith(('.cc', '.h', '.inl', '.m', '.mm')):
375 continue
376
377 for line_num, line in f.ChangedContents():
378 if pattern.match(line):
379 problems.append(' %s:%d' % (f.LocalPath(), line_num))
380
381 if not problems:
382 return []
383 return [output_api.PresubmitPromptWarning(
384 'Please consider avoiding the "? true : false" pattern if possible.\n' +
385 '\n'.join(problems))]
386
387
[email protected]55f9f382012-07-31 11:02:18388def _CheckUnwantedDependencies(input_api, output_api):
389 """Runs checkdeps on #include statements added in this
390 change. Breaking - rules is an error, breaking ! rules is a
391 warning.
392 """
393 # We need to wait until we have an input_api object and use this
394 # roundabout construct to import checkdeps because this file is
395 # eval-ed and thus doesn't have __file__.
396 original_sys_path = sys.path
397 try:
398 sys.path = sys.path + [input_api.os_path.join(
399 input_api.PresubmitLocalPath(), 'tools', 'checkdeps')]
400 import checkdeps
401 from cpp_checker import CppChecker
402 from rules import Rule
403 finally:
404 # Restore sys.path to what it was before.
405 sys.path = original_sys_path
406
407 added_includes = []
408 for f in input_api.AffectedFiles():
409 if not CppChecker.IsCppFile(f.LocalPath()):
410 continue
411
412 changed_lines = [line for line_num, line in f.ChangedContents()]
413 added_includes.append([f.LocalPath(), changed_lines])
414
415 deps_checker = checkdeps.DepsChecker()
416
417 error_descriptions = []
418 warning_descriptions = []
419 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
420 added_includes):
421 description_with_path = '%s\n %s' % (path, rule_description)
422 if rule_type == Rule.DISALLOW:
423 error_descriptions.append(description_with_path)
424 else:
425 warning_descriptions.append(description_with_path)
426
427 results = []
428 if error_descriptions:
429 results.append(output_api.PresubmitError(
430 'You added one or more #includes that violate checkdeps rules.',
431 error_descriptions))
432 if warning_descriptions:
[email protected]779caa52012-08-21 17:05:59433 if not input_api.is_committing:
434 warning_factory = output_api.PresubmitPromptWarning
435 else:
436 # We don't want to block use of the CQ when there is a warning
437 # of this kind, so we only show a message when committing.
438 warning_factory = output_api.PresubmitNotifyResult
439 results.append(warning_factory(
[email protected]55f9f382012-07-31 11:02:18440 'You added one or more #includes of files that are temporarily\n'
441 'allowed but being removed. Can you avoid introducing the\n'
442 '#include? See relevant DEPS file(s) for details and contacts.',
443 warning_descriptions))
444 return results
445
446
[email protected]fbcafe5a2012-08-08 15:31:22447def _CheckFilePermissions(input_api, output_api):
448 """Check that all files have their permissions properly set."""
449 args = [sys.executable, 'tools/checkperms/checkperms.py', '--root',
450 input_api.change.RepositoryRoot()]
451 for f in input_api.AffectedFiles():
452 args += ['--file', f.LocalPath()]
453 errors = []
454 (errors, stderrdata) = subprocess.Popen(args).communicate()
455
456 results = []
457 if errors:
[email protected]c8278b32012-10-30 20:35:49458 results.append(output_api.PresubmitError('checkperms.py failed.',
[email protected]fbcafe5a2012-08-08 15:31:22459 errors))
460 return results
461
462
[email protected]c8278b32012-10-30 20:35:49463def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
464 """Makes sure we don't include ui/aura/window_property.h
465 in header files.
466 """
467 pattern = input_api.re.compile(r'^#include\s*"ui/aura/window_property.h"')
468 errors = []
469 for f in input_api.AffectedFiles():
470 if not f.LocalPath().endswith('.h'):
471 continue
472 for line_num, line in f.ChangedContents():
473 if pattern.match(line):
474 errors.append(' %s:%d' % (f.LocalPath(), line_num))
475
476 results = []
477 if errors:
478 results.append(output_api.PresubmitError(
479 'Header files should not include ui/aura/window_property.h', errors))
480 return results
481
482
[email protected]cf9b78f2012-11-14 11:40:28483def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
484 """Checks that the lines in scope occur in the right order.
485
486 1. C system files in alphabetical order
487 2. C++ system files in alphabetical order
488 3. Project's .h files
489 """
490
491 c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
492 cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
493 custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
494
495 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
496
497 state = C_SYSTEM_INCLUDES
498
499 previous_line = ''
[email protected]728b9bb2012-11-14 20:38:57500 previous_line_num = 0
[email protected]cf9b78f2012-11-14 11:40:28501 problem_linenums = []
502 for line_num, line in scope:
503 if c_system_include_pattern.match(line):
504 if state != C_SYSTEM_INCLUDES:
[email protected]728b9bb2012-11-14 20:38:57505 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28506 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57507 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28508 elif cpp_system_include_pattern.match(line):
509 if state == C_SYSTEM_INCLUDES:
510 state = CPP_SYSTEM_INCLUDES
511 elif state == CUSTOM_INCLUDES:
[email protected]728b9bb2012-11-14 20:38:57512 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28513 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57514 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28515 elif custom_include_pattern.match(line):
516 if state != CUSTOM_INCLUDES:
517 state = CUSTOM_INCLUDES
518 elif previous_line and previous_line > line:
[email protected]728b9bb2012-11-14 20:38:57519 problem_linenums.append((line_num, previous_line_num))
[email protected]cf9b78f2012-11-14 11:40:28520 else:
521 problem_linenums.append(line_num)
522 previous_line = line
[email protected]728b9bb2012-11-14 20:38:57523 previous_line_num = line_num
[email protected]cf9b78f2012-11-14 11:40:28524
525 warnings = []
[email protected]728b9bb2012-11-14 20:38:57526 for (line_num, previous_line_num) in problem_linenums:
527 if line_num in changed_linenums or previous_line_num in changed_linenums:
[email protected]cf9b78f2012-11-14 11:40:28528 warnings.append(' %s:%d' % (file_path, line_num))
529 return warnings
530
531
[email protected]ac294a12012-12-06 16:38:43532def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
[email protected]cf9b78f2012-11-14 11:40:28533 """Checks the #include order for the given file f."""
534
[email protected]2299dcf2012-11-15 19:56:24535 system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
[email protected]962f117e2012-11-22 18:11:56536 # Exclude #include <.../...> includes from the check; e.g., <sys/...> includes
537 # often need to appear in a specific order.
538 excluded_include_pattern = input_api.re.compile(r'\s*#include \<.*/.*')
[email protected]2299dcf2012-11-15 19:56:24539 custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
[email protected]0e5c1852012-12-18 20:17:11540 if_pattern = input_api.re.compile(
541 r'\s*#\s*(if|elif|else|endif|define|undef).*')
542 # Some files need specialized order of includes; exclude such files from this
543 # check.
544 uncheckable_includes_pattern = input_api.re.compile(
545 r'\s*#include '
546 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
[email protected]cf9b78f2012-11-14 11:40:28547
548 contents = f.NewContents()
549 warnings = []
550 line_num = 0
551
[email protected]ac294a12012-12-06 16:38:43552 # Handle the special first include. If the first include file is
553 # some/path/file.h, the corresponding including file can be some/path/file.cc,
554 # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
555 # etc. It's also possible that no special first include exists.
556 for line in contents:
557 line_num += 1
558 if system_include_pattern.match(line):
559 # No special first include -> process the line again along with normal
560 # includes.
561 line_num -= 1
562 break
563 match = custom_include_pattern.match(line)
564 if match:
565 match_dict = match.groupdict()
566 header_basename = input_api.os_path.basename(
567 match_dict['FILE']).replace('.h', '')
568 if header_basename not in input_api.os_path.basename(f.LocalPath()):
[email protected]2299dcf2012-11-15 19:56:24569 # No special first include -> process the line again along with normal
570 # includes.
571 line_num -= 1
[email protected]ac294a12012-12-06 16:38:43572 break
[email protected]cf9b78f2012-11-14 11:40:28573
574 # Split into scopes: Each region between #if and #endif is its own scope.
575 scopes = []
576 current_scope = []
577 for line in contents[line_num:]:
578 line_num += 1
[email protected]0e5c1852012-12-18 20:17:11579 if uncheckable_includes_pattern.match(line):
580 return []
[email protected]2309b0fa02012-11-16 12:18:27581 if if_pattern.match(line):
[email protected]cf9b78f2012-11-14 11:40:28582 scopes.append(current_scope)
583 current_scope = []
[email protected]962f117e2012-11-22 18:11:56584 elif ((system_include_pattern.match(line) or
585 custom_include_pattern.match(line)) and
586 not excluded_include_pattern.match(line)):
[email protected]cf9b78f2012-11-14 11:40:28587 current_scope.append((line_num, line))
588 scopes.append(current_scope)
589
590 for scope in scopes:
591 warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
592 changed_linenums))
593 return warnings
594
595
596def _CheckIncludeOrder(input_api, output_api):
597 """Checks that the #include order is correct.
598
599 1. The corresponding header for source files.
600 2. C system files in alphabetical order
601 3. C++ system files in alphabetical order
602 4. Project's .h files in alphabetical order
603
[email protected]ac294a12012-12-06 16:38:43604 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
605 these rules separately.
[email protected]cf9b78f2012-11-14 11:40:28606 """
607
608 warnings = []
609 for f in input_api.AffectedFiles():
[email protected]ac294a12012-12-06 16:38:43610 if f.LocalPath().endswith(('.cc', '.h')):
611 changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
612 warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
[email protected]cf9b78f2012-11-14 11:40:28613
614 results = []
615 if warnings:
[email protected]120cf540d2012-12-10 17:55:53616 if not input_api.is_committing:
617 results.append(output_api.PresubmitPromptWarning(_INCLUDE_ORDER_WARNING,
618 warnings))
619 else:
620 # We don't warn on commit, to avoid stopping commits going through CQ.
621 results.append(output_api.PresubmitNotifyResult(_INCLUDE_ORDER_WARNING,
622 warnings))
[email protected]cf9b78f2012-11-14 11:40:28623 return results
624
625
[email protected]70ca77752012-11-20 03:45:03626def _CheckForVersionControlConflictsInFile(input_api, f):
627 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
628 errors = []
629 for line_num, line in f.ChangedContents():
630 if pattern.match(line):
631 errors.append(' %s:%d %s' % (f.LocalPath(), line_num, line))
632 return errors
633
634
635def _CheckForVersionControlConflicts(input_api, output_api):
636 """Usually this is not intentional and will cause a compile failure."""
637 errors = []
638 for f in input_api.AffectedFiles():
639 errors.extend(_CheckForVersionControlConflictsInFile(input_api, f))
640
641 results = []
642 if errors:
643 results.append(output_api.PresubmitError(
644 'Version control conflict markers found, please resolve.', errors))
645 return results
646
647
[email protected]06e6d0ff2012-12-11 01:36:44648def _CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api):
649 def FilterFile(affected_file):
650 """Filter function for use with input_api.AffectedSourceFiles,
651 below. This filters out everything except non-test files from
652 top-level directories that generally speaking should not hard-code
653 service URLs (e.g. src/android_webview/, src/content/ and others).
654 """
655 return input_api.FilterSourceFile(
656 affected_file,
[email protected]78bb39d62012-12-11 15:11:56657 white_list=(r'^(android_webview|base|content|net)[\\\/].*', ),
[email protected]06e6d0ff2012-12-11 01:36:44658 black_list=(_EXCLUDED_PATHS +
659 _TEST_CODE_EXCLUDED_PATHS +
660 input_api.DEFAULT_BLACK_LIST))
661
662 pattern = input_api.re.compile('"[^"]*google\.com[^"]*"')
663 problems = [] # items are (filename, line_number, line)
664 for f in input_api.AffectedSourceFiles(FilterFile):
665 for line_num, line in f.ChangedContents():
666 if pattern.search(line):
667 problems.append((f.LocalPath(), line_num, line))
668
669 if problems:
670 if not input_api.is_committing:
671 warning_factory = output_api.PresubmitPromptWarning
672 else:
673 # We don't want to block use of the CQ when there is a warning
674 # of this kind, so we only show a message when committing.
675 warning_factory = output_api.PresubmitNotifyResult
676 return [warning_factory(
677 'Most layers below src/chrome/ should not hardcode service URLs.\n'
678 'Are you sure this is correct? (Contact: [email protected])',
679 [' %s:%d: %s' % (
680 problem[0], problem[1], problem[2]) for problem in problems])]
681 else:
682 return []
683
684
[email protected]22c9bd72011-03-27 16:47:39685def _CommonChecks(input_api, output_api):
686 """Checks common to both upload and commit."""
687 results = []
688 results.extend(input_api.canned_checks.PanProjectChecks(
689 input_api, output_api, excluded_paths=_EXCLUDED_PATHS))
[email protected]66daa702011-05-28 14:41:46690 results.extend(_CheckAuthorizedAuthor(input_api, output_api))
[email protected]55459852011-08-10 15:17:19691 results.extend(
692 _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api))
[email protected]10689ca2011-09-02 02:31:54693 results.extend(_CheckNoIOStreamInHeaders(input_api, output_api))
[email protected]72df4e782012-06-21 16:28:18694 results.extend(_CheckNoUNIT_TESTInSourceFiles(input_api, output_api))
[email protected]8ea5d4b2011-09-13 21:49:22695 results.extend(_CheckNoNewWStrings(input_api, output_api))
[email protected]2a8ac9c2011-10-19 17:20:44696 results.extend(_CheckNoDEPSGIT(input_api, output_api))
[email protected]127f18ec2012-06-16 05:05:59697 results.extend(_CheckNoBannedFunctions(input_api, output_api))
[email protected]6c063c62012-07-11 19:11:06698 results.extend(_CheckNoPragmaOnce(input_api, output_api))
[email protected]e7479052012-09-19 00:26:12699 results.extend(_CheckNoTrinaryTrueFalse(input_api, output_api))
[email protected]55f9f382012-07-31 11:02:18700 results.extend(_CheckUnwantedDependencies(input_api, output_api))
[email protected]fbcafe5a2012-08-08 15:31:22701 results.extend(_CheckFilePermissions(input_api, output_api))
[email protected]c8278b32012-10-30 20:35:49702 results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
[email protected]2309b0fa02012-11-16 12:18:27703 results.extend(_CheckIncludeOrder(input_api, output_api))
[email protected]70ca77752012-11-20 03:45:03704 results.extend(_CheckForVersionControlConflicts(input_api, output_api))
[email protected]b8079ae4a2012-12-05 19:56:49705 results.extend(_CheckPatchFiles(input_api, output_api))
[email protected]06e6d0ff2012-12-11 01:36:44706 results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
[email protected]2299dcf2012-11-15 19:56:24707
708 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
709 results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
710 input_api, output_api,
711 input_api.PresubmitLocalPath(),
712 whitelist=[r'.+_test\.py$']))
[email protected]22c9bd72011-03-27 16:47:39713 return results
[email protected]1f7b4172010-01-28 01:17:34714
[email protected]b337cb5b2011-01-23 21:24:05715
716def _CheckSubversionConfig(input_api, output_api):
717 """Verifies the subversion config file is correctly setup.
718
719 Checks that autoprops are enabled, returns an error otherwise.
720 """
721 join = input_api.os_path.join
722 if input_api.platform == 'win32':
723 appdata = input_api.environ.get('APPDATA', '')
724 if not appdata:
725 return [output_api.PresubmitError('%APPDATA% is not configured.')]
726 path = join(appdata, 'Subversion', 'config')
727 else:
728 home = input_api.environ.get('HOME', '')
729 if not home:
730 return [output_api.PresubmitError('$HOME is not configured.')]
731 path = join(home, '.subversion', 'config')
732
733 error_msg = (
734 'Please look at http://dev.chromium.org/developers/coding-style to\n'
735 'configure your subversion configuration file. This enables automatic\n'
[email protected]c6a3c10b2011-01-24 16:14:20736 'properties to simplify the project maintenance.\n'
737 'Pro-tip: just download and install\n'
738 'http://src.chromium.org/viewvc/chrome/trunk/tools/build/slave/config\n')
[email protected]b337cb5b2011-01-23 21:24:05739
740 try:
741 lines = open(path, 'r').read().splitlines()
742 # Make sure auto-props is enabled and check for 2 Chromium standard
743 # auto-prop.
744 if (not '*.cc = svn:eol-style=LF' in lines or
745 not '*.pdf = svn:mime-type=application/pdf' in lines or
746 not 'enable-auto-props = yes' in lines):
747 return [
[email protected]79ed7e62011-02-21 21:08:53748 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05749 'It looks like you have not configured your subversion config '
[email protected]b5359c02011-02-01 20:29:56750 'file or it is not up-to-date.\n' + error_msg)
[email protected]b337cb5b2011-01-23 21:24:05751 ]
752 except (OSError, IOError):
753 return [
[email protected]79ed7e62011-02-21 21:08:53754 output_api.PresubmitNotifyResult(
[email protected]b337cb5b2011-01-23 21:24:05755 'Can\'t find your subversion config file.\n' + error_msg)
756 ]
757 return []
758
759
[email protected]66daa702011-05-28 14:41:46760def _CheckAuthorizedAuthor(input_api, output_api):
761 """For non-googler/chromites committers, verify the author's email address is
762 in AUTHORS.
763 """
[email protected]9bb9cb82011-06-13 20:43:01764 # TODO(maruel): Add it to input_api?
765 import fnmatch
766
[email protected]66daa702011-05-28 14:41:46767 author = input_api.change.author_email
[email protected]9bb9cb82011-06-13 20:43:01768 if not author:
769 input_api.logging.info('No author, skipping AUTHOR check')
[email protected]66daa702011-05-28 14:41:46770 return []
[email protected]c99663292011-05-31 19:46:08771 authors_path = input_api.os_path.join(
[email protected]66daa702011-05-28 14:41:46772 input_api.PresubmitLocalPath(), 'AUTHORS')
773 valid_authors = (
774 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line)
775 for line in open(authors_path))
[email protected]ac54b132011-06-06 18:11:18776 valid_authors = [item.group(1).lower() for item in valid_authors if item]
[email protected]d8b50be2011-06-15 14:19:44777 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors):
[email protected]5861efb2013-01-07 18:33:23778 input_api.logging.info('Valid authors are %s', ', '.join(valid_authors))
[email protected]66daa702011-05-28 14:41:46779 return [output_api.PresubmitPromptWarning(
780 ('%s is not in AUTHORS file. If you are a new contributor, please visit'
781 '\n'
782 'http://www.chromium.org/developers/contributing-code and read the '
783 '"Legal" section\n'
784 'If you are a chromite, verify the contributor signed the CLA.') %
785 author)]
786 return []
787
788
[email protected]b8079ae4a2012-12-05 19:56:49789def _CheckPatchFiles(input_api, output_api):
790 problems = [f.LocalPath() for f in input_api.AffectedFiles()
791 if f.LocalPath().endswith(('.orig', '.rej'))]
792 if problems:
793 return [output_api.PresubmitError(
794 "Don't commit .rej and .orig files.", problems)]
795 else:
796 return []
797
798
[email protected]1f7b4172010-01-28 01:17:34799def CheckChangeOnUpload(input_api, output_api):
800 results = []
801 results.extend(_CommonChecks(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54802 return results
[email protected]ca8d1982009-02-19 16:33:12803
804
805def CheckChangeOnCommit(input_api, output_api):
[email protected]fe5f57c52009-06-05 14:25:54806 results = []
[email protected]1f7b4172010-01-28 01:17:34807 results.extend(_CommonChecks(input_api, output_api))
[email protected]dd805fe2009-10-01 08:11:51808 # TODO(thestig) temporarily disabled, doesn't work in third_party/
809 #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories(
810 # input_api, output_api, sources))
[email protected]fe5f57c52009-06-05 14:25:54811 # Make sure the tree is 'open'.
[email protected]806e98e2010-03-19 17:49:27812 results.extend(input_api.canned_checks.CheckTreeIsOpen(
[email protected]7f238152009-08-12 19:00:34813 input_api,
814 output_api,
[email protected]4efa42142010-08-26 01:29:26815 json_url='http://chromium-status.appspot.com/current?format=json'))
[email protected]806e98e2010-03-19 17:49:27816 results.extend(input_api.canned_checks.CheckRietveldTryJobExecution(input_api,
[email protected]4ddc5df2011-12-12 03:05:04817 output_api, 'http://codereview.chromium.org',
[email protected]c1ba4c52012-03-09 14:23:28818 ('win_rel', 'linux_rel', 'mac_rel, win:compile'),
819 '[email protected]'))
[email protected]806e98e2010-03-19 17:49:27820
[email protected]3e4eb112011-01-18 03:29:54821 results.extend(input_api.canned_checks.CheckChangeHasBugField(
822 input_api, output_api))
[email protected]c4b47562011-12-05 23:39:41823 results.extend(input_api.canned_checks.CheckChangeHasDescription(
824 input_api, output_api))
[email protected]b337cb5b2011-01-23 21:24:05825 results.extend(_CheckSubversionConfig(input_api, output_api))
[email protected]fe5f57c52009-06-05 14:25:54826 return results
[email protected]ca8d1982009-02-19 16:33:12827
828
[email protected]5efb2a822011-09-27 23:06:13829def GetPreferredTrySlaves(project, change):
[email protected]4ce995ea2012-06-27 02:13:10830 files = change.LocalPaths()
831
[email protected]751b05f2013-01-10 23:12:17832 if not files or all(re.search(r'[\\/]OWNERS$', f) for f in files):
[email protected]3019c902012-06-29 00:09:03833 return []
834
[email protected]d668899a2012-09-06 18:16:59835 if all(re.search('\.(m|mm)$|(^|[/_])mac[/_.]', f) for f in files):
[email protected]641f2e3e2012-09-03 11:16:24836 return ['mac_rel', 'mac_asan']
[email protected]d668899a2012-09-06 18:16:59837 if all(re.search('(^|[/_])win[/_.]', f) for f in files):
[email protected]4ce995ea2012-06-27 02:13:10838 return ['win_rel']
[email protected]d668899a2012-09-06 18:16:59839 if all(re.search('(^|[/_])android[/_.]', f) for f in files):
[email protected]3e2f0402012-11-02 16:28:01840 return ['android_dbg', 'android_clang_dbg']
[email protected]356aa542012-09-19 23:31:29841 if all(re.search('^native_client_sdk', f) for f in files):
842 return ['linux_nacl_sdk', 'win_nacl_sdk', 'mac_nacl_sdk']
[email protected]de142152012-10-03 23:02:45843 if all(re.search('[/_]ios[/_.]', f) for f in files):
844 return ['ios_rel_device', 'ios_dbg_simulator']
[email protected]4ce995ea2012-06-27 02:13:10845
[email protected]3e2f0402012-11-02 16:28:01846 trybots = [
847 'android_clang_dbg',
848 'android_dbg',
849 'ios_dbg_simulator',
850 'ios_rel_device',
851 'linux_asan',
[email protected]95c989162012-11-29 05:58:25852 'linux_aura',
[email protected]3e2f0402012-11-02 16:28:01853 'linux_chromeos',
854 'linux_clang:compile',
855 'linux_rel',
856 'mac_asan',
857 'mac_rel',
[email protected]95c989162012-11-29 05:58:25858 'win_aura',
[email protected]3e2f0402012-11-02 16:28:01859 'win_rel',
860 ]
[email protected]911753b2012-08-02 12:11:54861
862 # Match things like path/aura/file.cc and path/file_aura.cc.
[email protected]95c989162012-11-29 05:58:25863 # Same for chromeos.
864 if any(re.search('[/_](aura|chromeos)', f) for f in files):
[email protected]3e2f0402012-11-02 16:28:01865 trybots += ['linux_chromeos_clang:compile', 'linux_chromeos_asan']
[email protected]4ce995ea2012-06-27 02:13:10866
[email protected]4ce995ea2012-06-27 02:13:10867 return trybots