diff options
author | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-31 10:40:01 +0000 |
---|---|---|
committer | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-31 10:40:01 +0000 |
commit | 0aa12e030026c0f3657744fc828b934f3b700e3f (patch) | |
tree | 28f539f827a4995f31383dc23497b229259547ca /tools/checkdeps/rules.py | |
parent | a0a045252015fef2c45deaac991467c0d2bd4576 (diff) | |
download | chromium_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.py | 89 |
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") |