| 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 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 'OS_NACL_NONSFI', | 291 'OS_NACL_NONSFI', |
| 292 'OS_NACL_SFI', | 292 'OS_NACL_SFI', |
| 293 'OS_OPENBSD', | 293 'OS_OPENBSD', |
| 294 'OS_POSIX', | 294 'OS_POSIX', |
| 295 'OS_QNX', | 295 'OS_QNX', |
| 296 'OS_SOLARIS', | 296 'OS_SOLARIS', |
| 297 'OS_WIN', | 297 'OS_WIN', |
| 298 ) | 298 ) |
| 299 | 299 |
| 300 | 300 |
| 301 _ANDROID_SPECIFIC_PYDEPS_FILES = [ | |
| 302 'build/android/test_runner.pydeps', | |
| 303 ] | |
| 304 | |
| 305 _GENERIC_PYDEPS_FILES = [ | |
| 306 'build/secondary/tools/swarming_client/isolate.pydeps', | |
| 307 ] | |
| 308 | |
| 309 _ALL_PYDEPS_FILES = _ANDROID_SPECIFIC_PYDEPS_FILES + _GENERIC_PYDEPS_FILES | |
| 310 | |
| 311 | |
| 312 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api): | 301 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api): |
| 313 """Attempts to prevent use of functions intended only for testing in | 302 """Attempts to prevent use of functions intended only for testing in |
| 314 non-testing code. For now this is just a best-effort implementation | 303 non-testing code. For now this is just a best-effort implementation |
| 315 that ignores header files and may have some false positives. A | 304 that ignores header files and may have some false positives. A |
| 316 better implementation would probably need a proper C++ parser. | 305 better implementation would probably need a proper C++ parser. |
| 317 """ | 306 """ |
| 318 # We only scan .cc files and the like, as the declaration of | 307 # We only scan .cc files and the like, as the declaration of |
| 319 # for-testing functions in header files are hard to distinguish from | 308 # for-testing functions in header files are hard to distinguish from |
| 320 # calls to such functions without a proper C++ parser. | 309 # calls to such functions without a proper C++ parser. |
| 321 file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS | 310 file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS |
| (...skipping 1180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1502 results = [] | 1491 results = [] |
| 1503 if errors: | 1492 if errors: |
| 1504 results.append(output_api.PresubmitError( | 1493 results.append(output_api.PresubmitError( |
| 1505 'MDPI assets should be placed in /res/drawable-mdpi/ or ' | 1494 'MDPI assets should be placed in /res/drawable-mdpi/ or ' |
| 1506 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and' | 1495 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and' |
| 1507 '/res/drawable-ldrtl/.\n' | 1496 '/res/drawable-ldrtl/.\n' |
| 1508 'Contact newt@chromium.org if you have questions.', errors)) | 1497 'Contact newt@chromium.org if you have questions.', errors)) |
| 1509 return results | 1498 return results |
| 1510 | 1499 |
| 1511 | 1500 |
| 1512 class PydepsChecker(object): | |
| 1513 def __init__(self, input_api, pydeps_files): | |
| 1514 self._file_cache = {} | |
| 1515 self._input_api = input_api | |
| 1516 self._pydeps_files = pydeps_files | |
| 1517 | |
| 1518 def _LoadFile(self, path): | |
| 1519 """Returns the list of paths within a .pydeps file relative to //.""" | |
| 1520 if path not in self._file_cache: | |
| 1521 with open(path) as f: | |
| 1522 self._file_cache[path] = f.read() | |
| 1523 return self._file_cache[path] | |
| 1524 | |
| 1525 def _ComputeNormalizedPydepsEntries(self, pydeps_path): | |
| 1526 """Returns an interable of paths within the .pydep, relativized to //.""" | |
| 1527 os_path = self._input_api.os_path | |
| 1528 pydeps_dir = os_path.dirname(pydeps_path) | |
| 1529 entries = (l.rstrip() for l in self._LoadFile(pydeps_path).splitlines() | |
| 1530 if not l.startswith('*')) | |
| 1531 return (os_path.normpath(os_path.join(pydeps_dir, e)) for e in entries) | |
| 1532 | |
| 1533 def _CreateFilesToPydepsMap(self): | |
| 1534 """Returns a map of local_path -> list_of_pydeps.""" | |
| 1535 ret = {} | |
| 1536 for pydep_local_path in self._pydeps_files: | |
| 1537 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path): | |
| 1538 ret.setdefault(path, []).append(pydep_local_path) | |
| 1539 return ret | |
| 1540 | |
| 1541 def ComputeAffectedPydeps(self): | |
| 1542 """Returns an iterable of .pydeps files that might need regenerating.""" | |
| 1543 affected_pydeps = set() | |
| 1544 file_to_pydeps_map = None | |
| 1545 for f in self._input_api.AffectedFiles(include_deletes=True): | |
| 1546 local_path = f.LocalPath() | |
| 1547 if local_path == 'DEPS': | |
| 1548 return self._pydeps_files | |
| 1549 elif local_path.endswith('.pydeps'): | |
| 1550 if local_path in self._pydeps_files: | |
| 1551 affected_pydeps.add(local_path) | |
| 1552 elif local_path.endswith('.py'): | |
| 1553 if file_to_pydeps_map is None: | |
| 1554 file_to_pydeps_map = self._CreateFilesToPydepsMap() | |
| 1555 affected_pydeps.update(file_to_pydeps_map.get(local_path, ())) | |
| 1556 return affected_pydeps | |
| 1557 | |
| 1558 def DetermineIfStale(self, pydeps_path): | |
| 1559 """Runs print_python_deps.py to see if the files is stale.""" | |
| 1560 old_pydeps_data = self._LoadFile(pydeps_path) | |
| 1561 | |
| 1562 m = self._input_api.re.search(r'# target: //(.*)', old_pydeps_data) | |
| 1563 if not m: | |
| 1564 return ['COULD NOT FIND .pydeps TARGET'] | |
| 1565 target = m.group(1) | |
| 1566 m = self._input_api.re.search(r'# root: //(.*)', old_pydeps_data) | |
| 1567 if not m: | |
| 1568 return ['COULD NOT FIND .pydeps ROOT'] | |
| 1569 root = m.group(1) or '.' | |
| 1570 | |
| 1571 cmd = ['build/print_python_deps.py', '--root', root, target] | |
| 1572 new_pydeps_data = self._input_api.subprocess.check_output(cmd) | |
| 1573 if old_pydeps_data != new_pydeps_data: | |
| 1574 return cmd[:-1] + ['--output', pydeps_path] + cmd[-1:] | |
| 1575 | |
| 1576 | |
| 1577 def _CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None): | |
| 1578 """Checks if a .pydeps file needs to be regenerated.""" | |
| 1579 # TODO(agrieve): Update when there's a better way to detect this: crbug/570091 | |
| 1580 is_android = input_api.os_path.exists('third_party/android_tools') | |
| 1581 pydeps_files = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES | |
| 1582 results = [] | |
| 1583 # First, check for new / deleted .pydeps. | |
| 1584 for f in input_api.AffectedFiles(include_deletes=True): | |
| 1585 if f.LocalPath().endswith('.pydeps'): | |
| 1586 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES: | |
| 1587 results.append(output_api.PresubmitError( | |
| 1588 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to ' | |
| 1589 'remove %s' % f.LocalPath())) | |
| 1590 elif f.Action() != 'D' and f.LocalPath() not in _ALL_PYDEPS_FILES: | |
| 1591 results.append(output_api.PresubmitError( | |
| 1592 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to ' | |
| 1593 'include %s' % f.LocalPath())) | |
| 1594 | |
| 1595 if results: | |
| 1596 return results | |
| 1597 | |
| 1598 checker = checker_for_tests or PydepsChecker(input_api, pydeps_files) | |
| 1599 | |
| 1600 for pydep_path in checker.ComputeAffectedPydeps(): | |
| 1601 try: | |
| 1602 cmd = checker.DetermineIfStale(pydep_path) | |
| 1603 if cmd: | |
| 1604 results.append(output_api.PresubmitError( | |
| 1605 'File is stale: %s\nTo regenerate, run:\n%s' % (pydep_path, | |
| 1606 ' '.join(cmd)))) | |
| 1607 except input_api.subprocess.CalledProcessError as error: | |
| 1608 return [output_api.PresubmitError('Error running ' + ' '.join(error.cmd), | |
| 1609 long_text=error.output)] | |
| 1610 | |
| 1611 return results | |
| 1612 | |
| 1613 | |
| 1614 def _CheckForCopyrightedCode(input_api, output_api): | 1501 def _CheckForCopyrightedCode(input_api, output_api): |
| 1615 """Verifies that newly added code doesn't contain copyrighted material | 1502 """Verifies that newly added code doesn't contain copyrighted material |
| 1616 and is properly licensed under the standard Chromium license. | 1503 and is properly licensed under the standard Chromium license. |
| 1617 | 1504 |
| 1618 As there can be false positives, we maintain a whitelist file. This check | 1505 As there can be false positives, we maintain a whitelist file. This check |
| 1619 also verifies that the whitelist file is up to date. | 1506 also verifies that the whitelist file is up to date. |
| 1620 """ | 1507 """ |
| 1621 import sys | 1508 import sys |
| 1622 original_sys_path = sys.path | 1509 original_sys_path = sys.path |
| 1623 try: | 1510 try: |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1757 (fpath.LocalPath(), lnum, deprecated, replacement))) | 1644 (fpath.LocalPath(), lnum, deprecated, replacement))) |
| 1758 return results | 1645 return results |
| 1759 | 1646 |
| 1760 | 1647 |
| 1761 def _AndroidSpecificOnUploadChecks(input_api, output_api): | 1648 def _AndroidSpecificOnUploadChecks(input_api, output_api): |
| 1762 """Groups checks that target android code.""" | 1649 """Groups checks that target android code.""" |
| 1763 results = [] | 1650 results = [] |
| 1764 results.extend(_CheckAndroidCrLogUsage(input_api, output_api)) | 1651 results.extend(_CheckAndroidCrLogUsage(input_api, output_api)) |
| 1765 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api)) | 1652 results.extend(_CheckAndroidNewMdpiAssetLocation(input_api, output_api)) |
| 1766 results.extend(_CheckAndroidToastUsage(input_api, output_api)) | 1653 results.extend(_CheckAndroidToastUsage(input_api, output_api)) |
| 1767 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api)) | |
| 1768 return results | 1654 return results |
| 1769 | 1655 |
| 1770 | 1656 |
| 1771 def _CommonChecks(input_api, output_api): | 1657 def _CommonChecks(input_api, output_api): |
| 1772 """Checks common to both upload and commit.""" | 1658 """Checks common to both upload and commit.""" |
| 1773 results = [] | 1659 results = [] |
| 1774 results.extend(input_api.canned_checks.PanProjectChecks( | 1660 results.extend(input_api.canned_checks.PanProjectChecks( |
| 1775 input_api, output_api, | 1661 input_api, output_api, |
| 1776 excluded_paths=_EXCLUDED_PATHS + _TESTRUNNER_PATHS)) | 1662 excluded_paths=_EXCLUDED_PATHS + _TESTRUNNER_PATHS)) |
| 1777 results.extend(_CheckAuthorizedAuthor(input_api, output_api)) | 1663 results.extend(_CheckAuthorizedAuthor(input_api, output_api)) |
| (...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2089 results.extend(input_api.canned_checks.CheckTreeIsOpen( | 1975 results.extend(input_api.canned_checks.CheckTreeIsOpen( |
| 2090 input_api, | 1976 input_api, |
| 2091 output_api, | 1977 output_api, |
| 2092 json_url='http://chromium-status.appspot.com/current?format=json')) | 1978 json_url='http://chromium-status.appspot.com/current?format=json')) |
| 2093 | 1979 |
| 2094 results.extend(input_api.canned_checks.CheckChangeHasBugField( | 1980 results.extend(input_api.canned_checks.CheckChangeHasBugField( |
| 2095 input_api, output_api)) | 1981 input_api, output_api)) |
| 2096 results.extend(input_api.canned_checks.CheckChangeHasDescription( | 1982 results.extend(input_api.canned_checks.CheckChangeHasDescription( |
| 2097 input_api, output_api)) | 1983 input_api, output_api)) |
| 2098 return results | 1984 return results |
| OLD | NEW |