summaryrefslogtreecommitdiffstats
path: root/tools/checkdeps/rules.py
diff options
context:
space:
mode:
authorjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-31 10:40:01 +0000
committerjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-31 10:40:01 +0000
commit0aa12e030026c0f3657744fc828b934f3b700e3f (patch)
tree28f539f827a4995f31383dc23497b229259547ca /tools/checkdeps/rules.py
parenta0a045252015fef2c45deaac991467c0d2bd4576 (diff)
downloadchromium_src-0aa12e030026c0f3657744fc828b934f3b700e3f.zip
chromium_src-0aa12e030026c0f3657744fc828b934f3b700e3f.tar.gz
chromium_src-0aa12e030026c0f3657744fc828b934f3b700e3f.tar.bz2
Implement ability to specify temporarily-allowed dependencies in DEPS
files, and use this ability in a few DEPS files where appropriate. This has no effect on the normal running of checkdeps; "!" dependencies are treated just like "+" dependencies when checkdeps is run on our bots. An upcoming change will use the new checkdeps.CheckAddedIncludes function, and will error out if you add a new include that violates a "-" rule, and show a presubmit warning when you add a new include that violates a "!" rule (the warning will say something like "We are in the process of removing dependencies from this directory to that file, can you avoid adding more?" While I was in there, fixed path handling so that checkdeps will work on case-sensitive platforms with paths that include upper-case characters (e.g. a checkout of Chrome at ~/c/Chrome/src rather than ~/c/chrome/src). Since the pipes.quote method seems unreliable on Windows (it failed on my setup), switched to subprocess.list2cmdline which I believe is stable. Added a small manual testing mode to checkdeps. It currently only verifies the CheckAddedIncludes function. TBR=jam@chromium.org BUG=138280 Review URL: https://chromiumcodereview.appspot.com/10805042 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149163 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/checkdeps/rules.py')
-rw-r--r--tools/checkdeps/rules.py89
1 files changed, 89 insertions, 0 deletions
diff --git a/tools/checkdeps/rules.py b/tools/checkdeps/rules.py
new file mode 100644
index 0000000..08dd13e
--- /dev/null
+++ b/tools/checkdeps/rules.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Base classes to represent dependency rules, used by checkdeps.py"""
+
+
+class Rule(object):
+ """Specifies a single rule for an include, which can be one of
+ ALLOW, DISALLOW and TEMP_ALLOW.
+ """
+
+ # These are the prefixes used to indicate each type of rule. These
+ # are also used as values for self.allow to indicate which type of
+ # rule this is.
+ ALLOW = "+"
+ DISALLOW = "-"
+ TEMP_ALLOW = "!"
+
+ def __init__(self, allow, directory, source):
+ self.allow = allow
+ self._dir = directory
+ self._source = source
+
+ def __str__(self):
+ return '"%s%s" from %s.' % (self.allow, self._dir, self._source)
+
+ def ParentOrMatch(self, other):
+ """Returns true if the input string is an exact match or is a parent
+ of the current rule. For example, the input "foo" would match "foo/bar"."""
+ return self._dir == other or self._dir.startswith(other + "/")
+
+ def ChildOrMatch(self, other):
+ """Returns true if the input string would be covered by this rule. For
+ example, the input "foo/bar" would match the rule "foo"."""
+ return self._dir == other or other.startswith(self._dir + "/")
+
+
+def ParseRuleString(rule_string, source):
+ """Returns a tuple of a boolean indicating whether the directory is an allow
+ rule, and a string holding the directory name.
+ """
+ if not rule_string:
+ raise Exception('The rule string "%s" is empty\nin %s' %
+ (rule_string, source))
+
+ if not rule_string[0] in [Rule.ALLOW, Rule.DISALLOW, Rule.TEMP_ALLOW]:
+ raise Exception(
+ 'The rule string "%s" does not begin with a "+", "-" or "!".' %
+ rule_string)
+
+ return (rule_string[0], rule_string[1:])
+
+
+class Rules(object):
+ def __init__(self):
+ """Initializes the current rules with an empty rule list."""
+ self._rules = []
+
+ def __str__(self):
+ return 'Rules = [\n%s]' % '\n'.join(' %s' % x for x in self._rules)
+
+ def AddRule(self, rule_string, source):
+ """Adds a rule for the given rule string.
+
+ Args:
+ rule_string: The include_rule string read from the DEPS file to apply.
+ source: A string representing the location of that string (filename, etc.)
+ so that we can give meaningful errors.
+ """
+ (add_rule, rule_dir) = ParseRuleString(rule_string, source)
+ # Remove any existing rules or sub-rules that apply. For example, if we're
+ # passed "foo", we should remove "foo", "foo/bar", but not "foobar".
+ self._rules = [x for x in self._rules if not x.ParentOrMatch(rule_dir)]
+ self._rules.insert(0, Rule(add_rule, rule_dir, source))
+
+ def DirAllowed(self, allowed_dir):
+ """Returns a tuple (success, message), where success indicates if the given
+ directory is allowed given the current set of rules, and the message tells
+ why if the comparison failed."""
+ for rule in self._rules:
+ if rule.ChildOrMatch(allowed_dir):
+ # This rule applies.
+ why_failed = ""
+ if rule.allow != Rule.ALLOW:
+ why_failed = str(rule)
+ return (rule.allow, why_failed)
+ # No rules apply, fail.
+ return (Rule.DISALLOW, "no rule applying")