Don't require DEPS OWNERS when moving lines around in a DEPS file.
As a bonus, less regex than before and also correctly handles the
'!' prefix in DEPS files now.
Bug: 702851
Change-Id: Ic086cf0984bd96ba429dfdcac7dcce53616eab2d
Reviewed-on: https://chromium-review.googlesource.com/476026
Commit-Queue: Daniel Cheng <[email protected]>
Reviewed-by: Dirk Pranke <[email protected]>
Cr-Commit-Position: refs/heads/master@{#464648}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index ce91a438..819bf733 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1079,7 +1079,57 @@
return results
-def _FilesToCheckForIncomingDeps(re, changed_lines):
+def _ExtractAddRulesFromParsedDeps(parsed_deps):
+ """Extract the rules that add dependencies from a parsed DEPS file.
+
+ Args:
+ parsed_deps: the locals dictionary from evaluating the DEPS file."""
+ add_rules = set()
+ add_rules.update([
+ rule[1:] for rule in parsed_deps.get('include_rules', [])
+ if rule.startswith('+') or rule.startswith('!')
+ ])
+ for specific_file, rules in parsed_deps.get('specific_include_rules',
+ {}).iteritems():
+ add_rules.update([
+ rule[1:] for rule in rules
+ if rule.startswith('+') or rule.startswith('!')
+ ])
+ return add_rules
+
+
+def _ParseDeps(contents):
+ """Simple helper for parsing DEPS files."""
+ # Stubs for handling special syntax in the root DEPS file.
+ def FromImpl(*_):
+ pass # NOP function so "From" doesn't fail.
+
+ def FileImpl(_):
+ pass # NOP function so "File" doesn't fail.
+
+ class _VarImpl:
+
+ def __init__(self, local_scope):
+ self._local_scope = local_scope
+
+ def Lookup(self, var_name):
+ """Implements the Var syntax."""
+ try:
+ return self._local_scope['vars'][var_name]
+ except KeyError:
+ raise Exception('Var is not defined: %s' % var_name)
+
+ local_scope = {}
+ global_scope = {
+ 'File': FileImpl,
+ 'From': FromImpl,
+ 'Var': _VarImpl(local_scope).Lookup,
+ }
+ exec contents in global_scope, local_scope
+ return local_scope
+
+
+def _CalculateAddedDeps(os_path, old_contents, new_contents):
"""Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
a set of DEPS entries that we should look up.
@@ -1090,22 +1140,20 @@
# We ignore deps entries on auto-generated directories.
AUTO_GENERATED_DIRS = ['grit', 'jni']
- # This pattern grabs the path without basename in the first
- # parentheses, and the basename (if present) in the second. It
- # relies on the simple heuristic that if there is a basename it will
- # be a header file ending in ".h".
- pattern = re.compile(
- r"""['"]\+([^'"]+?)(/[a-zA-Z0-9_]+\.h)?['"].*""")
+ old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
+ new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
+
+ added_deps = new_deps.difference(old_deps)
+
results = set()
- for changed_line in changed_lines:
- m = pattern.match(changed_line)
- if m:
- path = m.group(1)
- if path.split('/')[0] not in AUTO_GENERATED_DIRS:
- if m.group(2):
- results.add('%s%s' % (path, m.group(2)))
- else:
- results.add('%s/DEPS' % path)
+ for added_dep in added_deps:
+ if added_dep.split('/')[0] in AUTO_GENERATED_DIRS:
+ continue
+ # Assume that a rule that ends in .h is a rule for a specific file.
+ if added_dep.endswith('.h'):
+ results.add(added_dep)
+ else:
+ results.add(os_path.join(added_dep, 'DEPS'))
return results
@@ -1115,7 +1163,7 @@
target file or directory, to avoid layering violations from being
introduced. This check verifies that this happens.
"""
- changed_lines = set()
+ virtual_depended_on_files = set()
file_filter = lambda f: not input_api.re.match(
r"^third_party[\\\/]WebKit[\\\/].*", f.LocalPath())
@@ -1123,14 +1171,11 @@
file_filter=file_filter):
filename = input_api.os_path.basename(f.LocalPath())
if filename == 'DEPS':
- changed_lines |= set(line.strip()
- for line_num, line
- in f.ChangedContents())
- if not changed_lines:
- return []
+ virtual_depended_on_files.update(_CalculateAddedDeps(
+ input_api.os_path,
+ '\n'.join(f.OldContents()),
+ '\n'.join(f.NewContents())))
- virtual_depended_on_files = _FilesToCheckForIncomingDeps(input_api.re,
- changed_lines)
if not virtual_depended_on_files:
return []
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 7f3ee91a..fdca995 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -3,6 +3,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import os.path
import re
import subprocess
import unittest
@@ -469,41 +470,77 @@
class CheckAddedDepsHaveTetsApprovalsTest(unittest.TestCase):
- def testFilesToCheckForIncomingDeps(self):
- changed_lines = [
- '"+breakpad",',
- '"+chrome/installer",',
- '"+chrome/plugin/chrome_content_plugin_client.h",',
- '"+chrome/utility/chrome_content_utility_client.h",',
- '"+chromeos/chromeos_paths.h",',
- '"+components/crash/content",',
- '"+components/nacl/common",',
- '"+content/public/browser/render_process_host.h",',
- '"+jni/fooblat.h",',
- '"+grit", # For generated headers',
- '"+grit/generated_resources.h",',
- '"+grit/",',
- '"+policy", # For generated headers and source',
- '"+sandbox",',
- '"+tools/memory_watcher",',
- '"+third_party/lss/linux_syscall_support.h",',
+
+ def calculate(self, old_include_rules, old_specific_include_rules,
+ new_include_rules, new_specific_include_rules):
+ return PRESUBMIT._CalculateAddedDeps(
+ os.path, 'include_rules = %r\nspecific_include_rules = %r' % (
+ old_include_rules, old_specific_include_rules),
+ 'include_rules = %r\nspecific_include_rules = %r' % (
+ new_include_rules, new_specific_include_rules))
+
+ def testCalculateAddedDeps(self):
+ old_include_rules = [
+ '+base',
+ '-chrome',
+ '+content',
+ '-grit',
+ '-grit/",',
+ '+jni/fooblat.h',
+ '!sandbox',
]
- files_to_check = PRESUBMIT._FilesToCheckForIncomingDeps(re, changed_lines)
+ old_specific_include_rules = {
+ 'compositor\.*': {
+ '+cc',
+ },
+ }
+
+ new_include_rules = [
+ '-ash',
+ '+base',
+ '+chrome',
+ '+components',
+ '+content',
+ '+grit',
+ '+grit/generated_resources.h",',
+ '+grit/",',
+ '+jni/fooblat.h',
+ '+policy',
+ '+third_party/WebKit',
+ ]
+ new_specific_include_rules = {
+ 'compositor\.*': {
+ '+cc',
+ },
+ 'widget\.*': {
+ '+gpu',
+ },
+ }
+
expected = set([
- 'breakpad/DEPS',
- 'chrome/installer/DEPS',
- 'chrome/plugin/chrome_content_plugin_client.h',
- 'chrome/utility/chrome_content_utility_client.h',
- 'chromeos/chromeos_paths.h',
- 'components/crash/content/DEPS',
- 'components/nacl/common/DEPS',
- 'content/public/browser/render_process_host.h',
- 'policy/DEPS',
- 'sandbox/DEPS',
- 'tools/memory_watcher/DEPS',
- 'third_party/lss/linux_syscall_support.h',
+ 'chrome/DEPS',
+ 'gpu/DEPS',
+ 'components/DEPS',
+ 'policy/DEPS',
+ 'third_party/WebKit/DEPS',
])
- self.assertEqual(expected, files_to_check);
+ self.assertEqual(
+ expected,
+ self.calculate(old_include_rules, old_specific_include_rules,
+ new_include_rules, new_specific_include_rules))
+
+ def testCalculateAddedDepsIgnoresPermutations(self):
+ old_include_rules = [
+ '+base',
+ '+chrome',
+ ]
+ new_include_rules = [
+ '+chrome',
+ '+base',
+ ]
+ self.assertEqual(set(),
+ self.calculate(old_include_rules, {}, new_include_rules,
+ {}))
class JSONParsingTest(unittest.TestCase):