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 |
301 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api): | 312 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api): |
302 """Attempts to prevent use of functions intended only for testing in | 313 """Attempts to prevent use of functions intended only for testing in |
303 non-testing code. For now this is just a best-effort implementation | 314 non-testing code. For now this is just a best-effort implementation |
304 that ignores header files and may have some false positives. A | 315 that ignores header files and may have some false positives. A |
305 better implementation would probably need a proper C++ parser. | 316 better implementation would probably need a proper C++ parser. |
306 """ | 317 """ |
307 # We only scan .cc files and the like, as the declaration of | 318 # We only scan .cc files and the like, as the declaration of |
308 # for-testing functions in header files are hard to distinguish from | 319 # for-testing functions in header files are hard to distinguish from |
309 # calls to such functions without a proper C++ parser. | 320 # calls to such functions without a proper C++ parser. |
310 file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS | 321 file_inclusion_pattern = r'.+%s' % _IMPLEMENTATION_EXTENSIONS |
(...skipping 1183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1494 results = [] | 1505 results = [] |
1495 if errors: | 1506 if errors: |
1496 results.append(output_api.PresubmitError( | 1507 results.append(output_api.PresubmitError( |
1497 'MDPI assets should be placed in /res/drawable-mdpi/ or ' | 1508 'MDPI assets should be placed in /res/drawable-mdpi/ or ' |
1498 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and' | 1509 '/res/drawable-ldrtl-mdpi/\ninstead of /res/drawable/ and' |
1499 '/res/drawable-ldrtl/.\n' | 1510 '/res/drawable-ldrtl/.\n' |
1500 'Contact newt@chromium.org if you have questions.', errors)) | 1511 'Contact newt@chromium.org if you have questions.', errors)) |
1501 return results | 1512 return results |
1502 | 1513 |
1503 | 1514 |
| 1515 class PydepsChecker(object): |
| 1516 def __init__(self, input_api, pydeps_files): |
| 1517 self._file_cache = {} |
| 1518 self._input_api = input_api |
| 1519 self._pydeps_files = pydeps_files |
| 1520 |
| 1521 def _LoadFile(self, path): |
| 1522 """Returns the list of paths within a .pydeps file relative to //.""" |
| 1523 if path not in self._file_cache: |
| 1524 with open(path) as f: |
| 1525 self._file_cache[path] = f.read() |
| 1526 return self._file_cache[path] |
| 1527 |
| 1528 def _ComputeNormalizedPydepsEntries(self, pydeps_path): |
| 1529 """Returns an interable of paths within the .pydep, relativized to //.""" |
| 1530 os_path = self._input_api.os_path |
| 1531 pydeps_dir = os_path.dirname(pydeps_path) |
| 1532 entries = (l.rstrip() for l in self._LoadFile(pydeps_path).splitlines() |
| 1533 if not l.startswith('*')) |
| 1534 return (os_path.normpath(os_path.join(pydeps_dir, e)) for e in entries) |
| 1535 |
| 1536 def _CreateFilesToPydepsMap(self): |
| 1537 """Returns a map of local_path -> list_of_pydeps.""" |
| 1538 ret = {} |
| 1539 for pydep_local_path in self._pydeps_files: |
| 1540 for path in self._ComputeNormalizedPydepsEntries(pydep_local_path): |
| 1541 ret.setdefault(path, []).append(pydep_local_path) |
| 1542 return ret |
| 1543 |
| 1544 def ComputeAffectedPydeps(self): |
| 1545 """Returns an iterable of .pydeps files that might need regenerating.""" |
| 1546 affected_pydeps = set() |
| 1547 file_to_pydeps_map = None |
| 1548 for f in self._input_api.AffectedFiles(include_deletes=True): |
| 1549 local_path = f.LocalPath() |
| 1550 if local_path == 'DEPS': |
| 1551 return self._pydeps_files |
| 1552 elif local_path.endswith('.pydeps'): |
| 1553 if local_path in self._pydeps_files: |
| 1554 affected_pydeps.add(local_path) |
| 1555 elif local_path.endswith('.py'): |
| 1556 if file_to_pydeps_map is None: |
| 1557 file_to_pydeps_map = self._CreateFilesToPydepsMap() |
| 1558 affected_pydeps.update(file_to_pydeps_map.get(local_path, ())) |
| 1559 return affected_pydeps |
| 1560 |
| 1561 def DetermineIfStale(self, pydeps_path): |
| 1562 """Runs print_python_deps.py to see if the files is stale.""" |
| 1563 old_pydeps_data = self._LoadFile(pydeps_path).splitlines() |
| 1564 cmd = old_pydeps_data[1][1:].strip() |
| 1565 new_pydeps_data = self._input_api.subprocess.check_output( |
| 1566 cmd + ' --output ""', shell=True) |
| 1567 if old_pydeps_data[2:] != new_pydeps_data.splitlines()[2:]: |
| 1568 return cmd |
| 1569 |
| 1570 |
| 1571 def _CheckPydepsNeedsUpdating(input_api, output_api, checker_for_tests=None): |
| 1572 """Checks if a .pydeps file needs to be regenerated.""" |
| 1573 # TODO(agrieve): Update when there's a better way to detect this: crbug/570091 |
| 1574 is_android = input_api.os_path.exists('third_party/android_tools') |
| 1575 pydeps_files = _ALL_PYDEPS_FILES if is_android else _GENERIC_PYDEPS_FILES |
| 1576 results = [] |
| 1577 # First, check for new / deleted .pydeps. |
| 1578 for f in input_api.AffectedFiles(include_deletes=True): |
| 1579 if f.LocalPath().endswith('.pydeps'): |
| 1580 if f.Action() == 'D' and f.LocalPath() in _ALL_PYDEPS_FILES: |
| 1581 results.append(output_api.PresubmitError( |
| 1582 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to ' |
| 1583 'remove %s' % f.LocalPath())) |
| 1584 elif f.Action() != 'D' and f.LocalPath() not in _ALL_PYDEPS_FILES: |
| 1585 results.append(output_api.PresubmitError( |
| 1586 'Please update _ALL_PYDEPS_FILES within //PRESUBMIT.py to ' |
| 1587 'include %s' % f.LocalPath())) |
| 1588 |
| 1589 if results: |
| 1590 return results |
| 1591 |
| 1592 checker = checker_for_tests or PydepsChecker(input_api, pydeps_files) |
| 1593 |
| 1594 for pydep_path in checker.ComputeAffectedPydeps(): |
| 1595 try: |
| 1596 cmd = checker.DetermineIfStale(pydep_path) |
| 1597 if cmd: |
| 1598 results.append(output_api.PresubmitError( |
| 1599 'File is stale: %s\nTo regenerate, run:\n\n %s' % |
| 1600 (pydep_path, cmd))) |
| 1601 except input_api.subprocess.CalledProcessError as error: |
| 1602 return [output_api.PresubmitError('Error running: %s' % error.cmd, |
| 1603 long_text=error.output)] |
| 1604 |
| 1605 return results |
| 1606 |
| 1607 |
1504 def _CheckForCopyrightedCode(input_api, output_api): | 1608 def _CheckForCopyrightedCode(input_api, output_api): |
1505 """Verifies that newly added code doesn't contain copyrighted material | 1609 """Verifies that newly added code doesn't contain copyrighted material |
1506 and is properly licensed under the standard Chromium license. | 1610 and is properly licensed under the standard Chromium license. |
1507 | 1611 |
1508 As there can be false positives, we maintain a whitelist file. This check | 1612 As there can be false positives, we maintain a whitelist file. This check |
1509 also verifies that the whitelist file is up to date. | 1613 also verifies that the whitelist file is up to date. |
1510 """ | 1614 """ |
1511 import sys | 1615 import sys |
1512 original_sys_path = sys.path | 1616 original_sys_path = sys.path |
1513 try: | 1617 try: |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1697 results.extend(_CheckCygwinShell(input_api, output_api)) | 1801 results.extend(_CheckCygwinShell(input_api, output_api)) |
1698 results.extend(_CheckUserActionUpdate(input_api, output_api)) | 1802 results.extend(_CheckUserActionUpdate(input_api, output_api)) |
1699 results.extend(_CheckNoDeprecatedCSS(input_api, output_api)) | 1803 results.extend(_CheckNoDeprecatedCSS(input_api, output_api)) |
1700 results.extend(_CheckNoDeprecatedJS(input_api, output_api)) | 1804 results.extend(_CheckNoDeprecatedJS(input_api, output_api)) |
1701 results.extend(_CheckParseErrors(input_api, output_api)) | 1805 results.extend(_CheckParseErrors(input_api, output_api)) |
1702 results.extend(_CheckForIPCRules(input_api, output_api)) | 1806 results.extend(_CheckForIPCRules(input_api, output_api)) |
1703 results.extend(_CheckForCopyrightedCode(input_api, output_api)) | 1807 results.extend(_CheckForCopyrightedCode(input_api, output_api)) |
1704 results.extend(_CheckForWindowsLineEndings(input_api, output_api)) | 1808 results.extend(_CheckForWindowsLineEndings(input_api, output_api)) |
1705 results.extend(_CheckSingletonInHeaders(input_api, output_api)) | 1809 results.extend(_CheckSingletonInHeaders(input_api, output_api)) |
1706 results.extend(_CheckNoDeprecatedCompiledResourcesGYP(input_api, output_api)) | 1810 results.extend(_CheckNoDeprecatedCompiledResourcesGYP(input_api, output_api)) |
| 1811 results.extend(_CheckPydepsNeedsUpdating(input_api, output_api)) |
1707 | 1812 |
1708 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()): | 1813 if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()): |
1709 results.extend(input_api.canned_checks.RunUnitTestsInDirectory( | 1814 results.extend(input_api.canned_checks.RunUnitTestsInDirectory( |
1710 input_api, output_api, | 1815 input_api, output_api, |
1711 input_api.PresubmitLocalPath(), | 1816 input_api.PresubmitLocalPath(), |
1712 whitelist=[r'^PRESUBMIT_test\.py$'])) | 1817 whitelist=[r'^PRESUBMIT_test\.py$'])) |
1713 return results | 1818 return results |
1714 | 1819 |
1715 | 1820 |
1716 def _CheckAuthorizedAuthor(input_api, output_api): | 1821 def _CheckAuthorizedAuthor(input_api, output_api): |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1978 results.extend(input_api.canned_checks.CheckTreeIsOpen( | 2083 results.extend(input_api.canned_checks.CheckTreeIsOpen( |
1979 input_api, | 2084 input_api, |
1980 output_api, | 2085 output_api, |
1981 json_url='http://chromium-status.appspot.com/current?format=json')) | 2086 json_url='http://chromium-status.appspot.com/current?format=json')) |
1982 | 2087 |
1983 results.extend(input_api.canned_checks.CheckChangeHasBugField( | 2088 results.extend(input_api.canned_checks.CheckChangeHasBugField( |
1984 input_api, output_api)) | 2089 input_api, output_api)) |
1985 results.extend(input_api.canned_checks.CheckChangeHasDescription( | 2090 results.extend(input_api.canned_checks.CheckChangeHasDescription( |
1986 input_api, output_api)) | 2091 input_api, output_api)) |
1987 return results | 2092 return results |
OLD | NEW |