Index: tools/checkdeps/rules.py |
diff --git a/tools/checkdeps/rules.py b/tools/checkdeps/rules.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..08dd13ec0e497b83b04a110a2d3a72499fc9d1c4 |
--- /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") |