OLD | NEW |
---|---|
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Top-level presubmit script for Chromium. | 5 """Top-level presubmit script for Chromium. |
6 | 6 |
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts | 7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts |
8 for more details about the presubmit API built into depot_tools. | 8 for more details about the presubmit API built into depot_tools. |
9 """ | 9 """ |
10 | 10 |
(...skipping 410 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
421 def _CheckDCHECK_IS_ONHasBraces(input_api, output_api): | 421 def _CheckDCHECK_IS_ONHasBraces(input_api, output_api): |
422 """Checks to make sure DCHECK_IS_ON() does not skip the braces.""" | 422 """Checks to make sure DCHECK_IS_ON() does not skip the braces.""" |
423 errors = [] | 423 errors = [] |
424 pattern = input_api.re.compile(r'DCHECK_IS_ON(?!\(\))', | 424 pattern = input_api.re.compile(r'DCHECK_IS_ON(?!\(\))', |
425 input_api.re.MULTILINE) | 425 input_api.re.MULTILINE) |
426 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): | 426 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): |
427 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))): | 427 if (not f.LocalPath().endswith(('.cc', '.mm', '.h'))): |
428 continue | 428 continue |
429 for lnum, line in f.ChangedContents(): | 429 for lnum, line in f.ChangedContents(): |
430 if input_api.re.search(pattern, line): | 430 if input_api.re.search(pattern, line): |
431 errors.append(output_api.PresubmitError( | 431 errors.append(output_api.PresubmitError( |
432 ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' + | 432 ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' + |
433 'DCHECK_IS_ON()", not forgetting the braces.') | 433 'DCHECK_IS_ON()", not forgetting the braces.') |
434 % (f.LocalPath(), lnum))) | 434 % (f.LocalPath(), lnum))) |
435 return errors | 435 return errors |
436 | 436 |
437 | 437 |
438 def _FindHistogramNameInLine(histogram_name, line): | 438 def _FindHistogramNameInLine(histogram_name, line): |
439 """Tries to find a histogram name or prefix in a line.""" | 439 """Tries to find a histogram name or prefix in a line.""" |
440 if not "affected-histogram" in line: | 440 if not "affected-histogram" in line: |
441 return histogram_name in line | 441 return histogram_name in line |
442 # A histogram_suffixes tag type has an affected-histogram name as a prefix of | 442 # A histogram_suffixes tag type has an affected-histogram name as a prefix of |
443 # the histogram_name. | 443 # the histogram_name. |
444 if not '"' in line: | 444 if not '"' in line: |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
583 return True | 583 return True |
584 return False | 584 return False |
585 | 585 |
586 def CheckForMatch(affected_file, line_num, line, func_name, message, error): | 586 def CheckForMatch(affected_file, line_num, line, func_name, message, error): |
587 matched = False | 587 matched = False |
588 if func_name[0:1] == '/': | 588 if func_name[0:1] == '/': |
589 regex = func_name[1:] | 589 regex = func_name[1:] |
590 if input_api.re.search(regex, line): | 590 if input_api.re.search(regex, line): |
591 matched = True | 591 matched = True |
592 elif func_name in line: | 592 elif func_name in line: |
593 matched = True | 593 matched = True |
594 if matched: | 594 if matched: |
595 problems = warnings; | 595 problems = warnings |
596 if error: | 596 if error: |
597 problems = errors; | 597 problems = errors |
598 problems.append(' %s:%d:' % (affected_file.LocalPath(), line_num)) | 598 problems.append(' %s:%d:' % (affected_file.LocalPath(), line_num)) |
599 for message_line in message: | 599 for message_line in message: |
600 problems.append(' %s' % message_line) | 600 problems.append(' %s' % message_line) |
601 | 601 |
602 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h')) | 602 file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h')) |
603 for f in input_api.AffectedFiles(file_filter=file_filter): | 603 for f in input_api.AffectedFiles(file_filter=file_filter): |
604 for line_num, line in f.ChangedContents(): | 604 for line_num, line in f.ChangedContents(): |
605 for func_name, message, error in _BANNED_OBJC_FUNCTIONS: | 605 for func_name, message, error in _BANNED_OBJC_FUNCTIONS: |
606 CheckForMatch(f, line_num, line, func_name, message, error) | 606 CheckForMatch(f, line_num, line, func_name, message, error) |
607 | 607 |
(...skipping 784 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1392 import checkstyle | 1392 import checkstyle |
1393 finally: | 1393 finally: |
1394 # Restore sys.path to what it was before. | 1394 # Restore sys.path to what it was before. |
1395 sys.path = original_sys_path | 1395 sys.path = original_sys_path |
1396 | 1396 |
1397 return checkstyle.RunCheckstyle( | 1397 return checkstyle.RunCheckstyle( |
1398 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml', | 1398 input_api, output_api, 'tools/android/checkstyle/chromium-style-5.0.xml', |
1399 black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST) | 1399 black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST) |
1400 | 1400 |
1401 | 1401 |
1402 def _CheckIpcOwners(input_api, output_api): | |
1403 """Checks that affected files involving IPC have an IPC OWNERS rule. | |
1404 | |
1405 Whether or not a file affects IPC is determined by a simple whitelist of | |
1406 filename patterns.""" | |
1407 file_patterns = [ | |
1408 '*_messages.cc', | |
1409 '*_messages*.h', | |
1410 '*_param_traits*.*', | |
1411 '*.mojom', | |
1412 '*_struct_traits*.*', | |
1413 '*_type_converters*.*', | |
1414 ] | |
1415 | |
1416 # Dictionary mapping an OWNERS file path to Patterns. | |
1417 # Patterns is a dictionary mapping glob patterns (suitable for use in per-file | |
1418 # rules ) to a PatternEntry. | |
1419 # PatternEntry is a dictionary with two keys: | |
1420 # - 'files': the files that are matched by this pattern | |
1421 # - 'rules': the per-file rules needed for this pattern | |
1422 # For example, if we expect OWNERS file to contain rules for *.mojom and | |
1423 # *_struct_traits*.*, Patterns might look like this: | |
1424 # { | |
1425 # '*.mojom': { | |
1426 # 'files': ..., | |
1427 # 'rules': [ | |
1428 # 'per-file *.mojom=set noparent', | |
1429 # 'per-file *.mojom=file://ipc/SECURITY_OWNERS', | |
1430 # ], | |
1431 # }, | |
1432 # '*_struct_traits*.*': { | |
1433 # 'files': ..., | |
1434 # 'rules': [ | |
1435 # 'per-file *_struct_traits*.*=set noparent', | |
1436 # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS', | |
1437 # ], | |
1438 # }, | |
1439 # } | |
1440 to_check = {} | |
1441 | |
1442 # Iterate through the affected files to see what we actually need to check | |
1443 # for. We should only nag patch authors about per-file rules if a file in that | |
1444 # directory would match that pattern. If a directory only contains *.mojom | |
1445 # files and no *_messages*.h files, we should only nag about rules for | |
1446 # *.mojom files. | |
1447 for f in input_api.change.AffectedFiles(): | |
1448 for pattern in file_patterns: | |
1449 if input_api.fnmatch.fnmatch( | |
1450 input_api.os_path.basename(f.LocalPath()), pattern): | |
1451 owners_file = input_api.os_path.join( | |
1452 input_api.os_path.dirname(f.LocalPath()), 'OWNERS') | |
1453 if owners_file not in to_check: | |
1454 to_check[owners_file] = {} | |
1455 if pattern not in to_check[owners_file]: | |
1456 to_check[owners_file][pattern] = { | |
1457 'files': [], | |
1458 'rules': [ | |
1459 'per-file %s=set noparent' % pattern, | |
1460 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern, | |
1461 ] | |
1462 } | |
1463 to_check[owners_file][pattern]['files'].append(f) | |
1464 break | |
1465 | |
1466 # Now go through the OWNERS files we collected, filtering out rules that are | |
1467 # already present in that OWNERS file. | |
1468 for owners_file, patterns in to_check.iteritems(): | |
1469 try: | |
1470 with file(owners_file) as f: | |
1471 for line in f.read().splitlines(): | |
1472 for entry in patterns.itervalues(): | |
1473 entry['rules'] = [rule for rule in entry['rules'] if rule != line] | |
Dirk Pranke
2016/06/18 20:49:26
Maybe
lines = f.read().splitlines()
for entry
dcheng
2016/06/19 00:03:28
I tweaked this slightly and put the lines in a set
| |
1474 except IOError: | |
1475 # No OWNERS file, so all the rules are definitely missing. | |
1476 continue | |
1477 | |
1478 # All the remaining lines weren't found in OWNERS files, so emit an error. | |
1479 errors = [] | |
1480 for owners_file, patterns in to_check.iteritems(): | |
1481 missing_lines = [] | |
1482 files = [] | |
1483 for pattern, entry in patterns.iteritems(): | |
1484 missing_lines.extend(entry['rules']) | |
1485 files.extend([' %s' % f.LocalPath() for f in entry['files']]) | |
1486 if missing_lines: | |
1487 errors.append( | |
1488 '%s is missing the following lines:\n\n%s\n\nfor changed files:\n%s' % | |
1489 (owners_file, '\n'.join(missing_lines), '\n'.join(files))) | |
1490 | |
1491 results = [] | |
1492 if errors: | |
1493 results.append(output_api.PresubmitError( | |
1494 'Found changes to IPC files without a security OWNER!', | |
1495 long_text='\n\n'.join(errors))) | |
1496 | |
1497 return results | |
1498 | |
1499 | |
1402 def _CheckAndroidToastUsage(input_api, output_api): | 1500 def _CheckAndroidToastUsage(input_api, output_api): |
1403 """Checks that code uses org.chromium.ui.widget.Toast instead of | 1501 """Checks that code uses org.chromium.ui.widget.Toast instead of |
1404 android.widget.Toast (Chromium Toast doesn't force hardware | 1502 android.widget.Toast (Chromium Toast doesn't force hardware |
1405 acceleration on low-end devices, saving memory). | 1503 acceleration on low-end devices, saving memory). |
1406 """ | 1504 """ |
1407 toast_import_pattern = input_api.re.compile( | 1505 toast_import_pattern = input_api.re.compile( |
1408 r'^import android\.widget\.Toast;$') | 1506 r'^import android\.widget\.Toast;$') |
1409 | 1507 |
1410 errors = [] | 1508 errors = [] |
1411 | 1509 |
(...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1855 results.extend(_CheckNoDeprecatedCSS(input_api, output_api)) | 1953 results.extend(_CheckNoDeprecatedCSS(input_api, output_api)) |
1856 results.extend(_CheckNoDeprecatedJS(input_api, output_api)) | 1954 results.extend(_CheckNoDeprecatedJS(input_api, output_api)) |
1857 results.extend(_CheckParseErrors(input_api, output_api)) | 1955 results.extend(_CheckParseErrors(input_api, output_api)) |
1858 results.extend(_CheckForIPCRules(input_api, output_api)) | 1956 results.extend(_CheckForIPCRules(input_api, output_api)) |
1859 results.extend(_CheckForCopyrightedCode(input_api, output_api)) | 1957 results.extend(_CheckForCopyrightedCode(input_api, output_api)) |
1860 results.extend(_CheckForWindowsLineEndings(input_api, output_api)) | 1958 results.extend(_CheckForWindowsLineEndings(input_api, output_api)) |
1861 results.extend(_CheckSingletonInHeaders(input_api, output_api)) | 1959 results.extend(_CheckSingletonInHeaders(input_api, output_api)) |
1862 results.extend(_CheckNoDeprecatedCompiledResourcesGYP(input_api, output_api)) | 1960 results.extend(_CheckNoDeprecatedCompiledResourcesGYP(input_api, output_api)) |
1863 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api)) | 1961 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api)) |
1864 results.extend(_CheckJavaStyle(input_api, output_api)) | 1962 results.extend(_CheckJavaStyle(input_api, output_api)) |
1963 results.extend(_CheckIpcOwners(input_api, output_api)) | |
1865 | 1964 |
1866 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()): | 1965 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()): |
1867 results.extend(input_api.canned_checks.RunUnitTestsInDirectory( | 1966 results.extend(input_api.canned_checks.RunUnitTestsInDirectory( |
1868 input_api, output_api, | 1967 input_api, output_api, |
1869 input_api.PresubmitLocalPath(), | 1968 input_api.PresubmitLocalPath(), |
1870 whitelist=[r'^PRESUBMIT_test\.py$'])) | 1969 whitelist=[r'^PRESUBMIT_test\.py$'])) |
1871 return results | 1970 return results |
1872 | 1971 |
1873 | 1972 |
1874 def _CheckAuthorizedAuthor(input_api, output_api): | 1973 def _CheckAuthorizedAuthor(input_api, output_api): |
1875 """For non-googler/chromites committers, verify the author's email address is | 1974 """For non-googler/chromites committers, verify the author's email address is |
1876 in AUTHORS. | 1975 in AUTHORS. |
1877 """ | 1976 """ |
1878 # TODO(maruel): Add it to input_api? | |
1879 import fnmatch | |
1880 | |
1881 author = input_api.change.author_email | 1977 author = input_api.change.author_email |
1882 if not author: | 1978 if not author: |
1883 input_api.logging.info('No author, skipping AUTHOR check') | 1979 input_api.logging.info('No author, skipping AUTHOR check') |
1884 return [] | 1980 return [] |
1885 authors_path = input_api.os_path.join( | 1981 authors_path = input_api.os_path.join( |
1886 input_api.PresubmitLocalPath(), 'AUTHORS') | 1982 input_api.PresubmitLocalPath(), 'AUTHORS') |
1887 valid_authors = ( | 1983 valid_authors = ( |
1888 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line) | 1984 input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line) |
1889 for line in open(authors_path)) | 1985 for line in open(authors_path)) |
1890 valid_authors = [item.group(1).lower() for item in valid_authors if item] | 1986 valid_authors = [item.group(1).lower() for item in valid_authors if item] |
1891 if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors): | 1987 if not any(input_api.fnmatch.fnmatch(author.lower(), valid) |
1988 for valid in valid_authors): | |
1892 input_api.logging.info('Valid authors are %s', ', '.join(valid_authors)) | 1989 input_api.logging.info('Valid authors are %s', ', '.join(valid_authors)) |
1893 return [output_api.PresubmitPromptWarning( | 1990 return [output_api.PresubmitPromptWarning( |
1894 ('%s is not in AUTHORS file. If you are a new contributor, please visit' | 1991 ('%s is not in AUTHORS file. If you are a new contributor, please visit' |
1895 '\n' | 1992 '\n' |
1896 'http://www.chromium.org/developers/contributing-code and read the ' | 1993 'http://www.chromium.org/developers/contributing-code and read the ' |
1897 '"Legal" section\n' | 1994 '"Legal" section\n' |
1898 'If you are a chromite, verify the contributor signed the CLA.') % | 1995 'If you are a chromite, verify the contributor signed the CLA.') % |
1899 author)] | 1996 author)] |
1900 return [] | 1997 return [] |
1901 | 1998 |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2122 results.extend(input_api.canned_checks.CheckTreeIsOpen( | 2219 results.extend(input_api.canned_checks.CheckTreeIsOpen( |
2123 input_api, | 2220 input_api, |
2124 output_api, | 2221 output_api, |
2125 json_url='http://chromium-status.appspot.com/current?format=json')) | 2222 json_url='http://chromium-status.appspot.com/current?format=json')) |
2126 | 2223 |
2127 results.extend(input_api.canned_checks.CheckChangeHasBugField( | 2224 results.extend(input_api.canned_checks.CheckChangeHasBugField( |
2128 input_api, output_api)) | 2225 input_api, output_api)) |
2129 results.extend(input_api.canned_checks.CheckChangeHasDescription( | 2226 results.extend(input_api.canned_checks.CheckChangeHasDescription( |
2130 input_api, output_api)) | 2227 input_api, output_api)) |
2131 return results | 2228 return results |
OLD | NEW |