Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Side by Side Diff: tools/checkdeps/rules.py

Issue 10823271: Add ability to write include rules specific to subsets of files in a directory. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge to head. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/checkdeps/results.py ('k') | tools/checkdeps/testdata/allowed/DEPS » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 """Base classes to represent dependency rules, used by checkdeps.py""" 5 """Base classes to represent dependency rules, used by checkdeps.py"""
6 6
7 7
8 import os
9 import re
10
11
8 class Rule(object): 12 class Rule(object):
9 """Specifies a single rule for an include, which can be one of 13 """Specifies a single rule for an include, which can be one of
10 ALLOW, DISALLOW and TEMP_ALLOW. 14 ALLOW, DISALLOW and TEMP_ALLOW.
11 """ 15 """
12 16
13 # These are the prefixes used to indicate each type of rule. These 17 # These are the prefixes used to indicate each type of rule. These
14 # are also used as values for self.allow to indicate which type of 18 # are also used as values for self.allow to indicate which type of
15 # rule this is. 19 # rule this is.
16 ALLOW = '+' 20 ALLOW = '+'
17 DISALLOW = '-' 21 DISALLOW = '-'
(...skipping 11 matching lines...) Expand all
29 """Returns true if the input string is an exact match or is a parent 33 """Returns true if the input string is an exact match or is a parent
30 of the current rule. For example, the input "foo" would match "foo/bar".""" 34 of the current rule. For example, the input "foo" would match "foo/bar"."""
31 return self._dir == other or self._dir.startswith(other + '/') 35 return self._dir == other or self._dir.startswith(other + '/')
32 36
33 def ChildOrMatch(self, other): 37 def ChildOrMatch(self, other):
34 """Returns true if the input string would be covered by this rule. For 38 """Returns true if the input string would be covered by this rule. For
35 example, the input "foo/bar" would match the rule "foo".""" 39 example, the input "foo/bar" would match the rule "foo"."""
36 return self._dir == other or other.startswith(self._dir + '/') 40 return self._dir == other or other.startswith(self._dir + '/')
37 41
38 42
39 class SpecificRule(Rule): 43 class MessageRule(Rule):
40 """A rule that has a specific reason not related to directory or 44 """A rule that has a simple message as the reason for failing,
41 source, for failing. 45 unrelated to directory or source.
42 """ 46 """
43 47
44 def __init__(self, reason): 48 def __init__(self, reason):
45 super(SpecificRule, self).__init__(Rule.DISALLOW, '', '') 49 super(MessageRule, self).__init__(Rule.DISALLOW, '', '')
46 self._reason = reason 50 self._reason = reason
47 51
48 def __str__(self): 52 def __str__(self):
49 return self._reason 53 return self._reason
50 54
51 55
52 def ParseRuleString(rule_string, source): 56 def ParseRuleString(rule_string, source):
53 """Returns a tuple of a boolean indicating whether the directory is an allow 57 """Returns a tuple of a character indicating what type of rule this
54 rule, and a string holding the directory name. 58 is, and a string holding the path the rule applies to.
55 """ 59 """
56 if not rule_string: 60 if not rule_string:
57 raise Exception('The rule string "%s" is empty\nin %s' % 61 raise Exception('The rule string "%s" is empty\nin %s' %
58 (rule_string, source)) 62 (rule_string, source))
59 63
60 if not rule_string[0] in [Rule.ALLOW, Rule.DISALLOW, Rule.TEMP_ALLOW]: 64 if not rule_string[0] in [Rule.ALLOW, Rule.DISALLOW, Rule.TEMP_ALLOW]:
61 raise Exception( 65 raise Exception(
62 'The rule string "%s" does not begin with a "+", "-" or "!".' % 66 'The rule string "%s" does not begin with a "+", "-" or "!".' %
63 rule_string) 67 rule_string)
64 68
65 return (rule_string[0], rule_string[1:]) 69 return (rule_string[0], rule_string[1:])
66 70
67 71
68 class Rules(object): 72 class Rules(object):
73 """Sets of rules for files in a directory.
74
75 By default, rules are added to the set of rules applicable to all
76 dependee files in the directory. Rules may also be added that apply
77 only to dependee files whose filename (last component of their path)
78 matches a given regular expression; hence there is one additional
79 set of rules per unique regular expression.
80 """
81
69 def __init__(self): 82 def __init__(self):
70 """Initializes the current rules with an empty rule list.""" 83 """Initializes the current rules with an empty rule list for all
71 self._rules = [] 84 files.
85 """
86 # We keep the general rules out of the specific rules dictionary,
87 # as we need to always process them last.
88 self._general_rules = []
89
90 # Keys are regular expression strings, values are arrays of rules
91 # that apply to dependee files whose basename matches the regular
92 # expression. These are applied before the general rules, but
93 # their internal order is arbitrary.
94 self._specific_rules = {}
72 95
73 def __str__(self): 96 def __str__(self):
74 return 'Rules = [\n%s]' % '\n'.join(' %s' % x for x in self._rules) 97 result = ['Rules = {\n (apply to all files): [\n%s\n ],' % '\n'.join(
98 ' %s' % x for x in self._general_rules)]
99 for regexp, rules in self._specific_rules.iteritems():
100 result.append(' (limited to files matching %s): [\n%s\n ]' % (
101 regexp, '\n'.join(' %s' % x for x in rules)))
102 result.append(' }')
103 return '\n'.join(result)
75 104
76 def AddRule(self, rule_string, source): 105 def AddRule(self, rule_string, source, dependee_regexp=None):
77 """Adds a rule for the given rule string. 106 """Adds a rule for the given rule string.
78 107
79 Args: 108 Args:
80 rule_string: The include_rule string read from the DEPS file to apply. 109 rule_string: The include_rule string read from the DEPS file to apply.
81 source: A string representing the location of that string (filename, etc.) 110 source: A string representing the location of that string (filename, etc.)
82 so that we can give meaningful errors. 111 so that we can give meaningful errors.
112 dependee_regexp: The rule will only be applied to dependee files
113 whose filename (last component of their path)
114 matches the expression. None to match all
115 dependee files.
83 """ 116 """
84 (add_rule, rule_dir) = ParseRuleString(rule_string, source) 117 (rule_type, rule_dir) = ParseRuleString(rule_string, source)
118
119 if not dependee_regexp:
120 rules_to_update = self._general_rules
121 else:
122 if dependee_regexp in self._specific_rules:
123 rules_to_update = self._specific_rules[dependee_regexp]
124 else:
125 rules_to_update = []
126
85 # Remove any existing rules or sub-rules that apply. For example, if we're 127 # Remove any existing rules or sub-rules that apply. For example, if we're
86 # passed "foo", we should remove "foo", "foo/bar", but not "foobar". 128 # passed "foo", we should remove "foo", "foo/bar", but not "foobar".
87 self._rules = [x for x in self._rules if not x.ParentOrMatch(rule_dir)] 129 rules_to_update = [x for x in rules_to_update
88 self._rules.insert(0, Rule(add_rule, rule_dir, source)) 130 if not x.ParentOrMatch(rule_dir)]
131 rules_to_update.insert(0, Rule(rule_type, rule_dir, source))
89 132
90 def RuleApplyingTo(self, allowed_dir): 133 if not dependee_regexp:
91 """Returns the rule that applies to 'allowed_dir'.""" 134 self._general_rules = rules_to_update
92 for rule in self._rules: 135 else:
93 if rule.ChildOrMatch(allowed_dir): 136 self._specific_rules[dependee_regexp] = rules_to_update
137
138 def RuleApplyingTo(self, include_path, dependee_path):
139 """Returns the rule that applies to |include_path| for a dependee
140 file located at |dependee_path|.
141 """
142 dependee_filename = os.path.basename(dependee_path)
143 for regexp, specific_rules in self._specific_rules.iteritems():
144 if re.match(regexp, dependee_filename):
145 for rule in specific_rules:
146 if rule.ChildOrMatch(include_path):
147 return rule
148 for rule in self._general_rules:
149 if rule.ChildOrMatch(include_path):
94 return rule 150 return rule
95 return SpecificRule('no rule applying.') 151 return MessageRule('no rule applying.')
OLDNEW
« no previous file with comments | « tools/checkdeps/results.py ('k') | tools/checkdeps/testdata/allowed/DEPS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698