| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Traverses the source tree, parses all found DEPS files, and constructs | 6 """Traverses the source tree, parses all found DEPS files, and constructs |
| 7 a dependency rule table to be used by subclasses. | 7 a dependency rule table to be used by subclasses. |
| 8 | 8 |
| 9 The format of the deps file: | 9 The format of the deps file: |
| 10 | 10 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 so you can modify or remove it using the normal include rules. | 68 so you can modify or remove it using the normal include rules. |
| 69 | 69 |
| 70 The rules are processed in order. This means you can explicitly allow a higher | 70 The rules are processed in order. This means you can explicitly allow a higher |
| 71 directory and then take away permissions from sub-parts, or the reverse. | 71 directory and then take away permissions from sub-parts, or the reverse. |
| 72 | 72 |
| 73 Note that all directory separators must be slashes (Unix-style) and not | 73 Note that all directory separators must be slashes (Unix-style) and not |
| 74 backslashes. All directories should be relative to the source root and use | 74 backslashes. All directories should be relative to the source root and use |
| 75 only lowercase. | 75 only lowercase. |
| 76 """ | 76 """ |
| 77 | 77 |
| 78 import copy |
| 78 import os | 79 import os |
| 79 import subprocess | 80 import subprocess |
| 80 import copy | |
| 81 | 81 |
| 82 from rules import Rule, Rules | 82 from rules import Rule, Rules |
| 83 | 83 |
| 84 | 84 |
| 85 # Variable name used in the DEPS file to add or subtract include files from | 85 # Variable name used in the DEPS file to add or subtract include files from |
| 86 # the module-level deps. | 86 # the module-level deps. |
| 87 INCLUDE_RULES_VAR_NAME = 'include_rules' | 87 INCLUDE_RULES_VAR_NAME = 'include_rules' |
| 88 | 88 |
| 89 # Variable name used in the DEPS file to add or subtract include files | 89 # Variable name used in the DEPS file to add or subtract include files |
| 90 # from module-level deps specific to files whose basename (last | 90 # from module-level deps specific to files whose basename (last |
| (...skipping 18 matching lines...) Expand all Loading... |
| 109 def __init__(self, | 109 def __init__(self, |
| 110 base_directory=None, | 110 base_directory=None, |
| 111 verbose=False, | 111 verbose=False, |
| 112 being_tested=False, | 112 being_tested=False, |
| 113 ignore_temp_rules=False, | 113 ignore_temp_rules=False, |
| 114 ignore_specific_rules=False): | 114 ignore_specific_rules=False): |
| 115 """Creates a new DepsBuilder. | 115 """Creates a new DepsBuilder. |
| 116 | 116 |
| 117 Args: | 117 Args: |
| 118 base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src. | 118 base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src. |
| 119 verbose: Set to true for debug output. | 119 verbose: Set to True for debug output. |
| 120 being_tested: Set to true to ignore the DEPS file at tools/checkdeps/DEPS. | 120 being_tested: Set to True to ignore the DEPS file at tools/checkdeps/DEPS. |
| 121 ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!"). | 121 ignore_temp_rules: Ignore rules that start with Rule.TEMP_ALLOW ("!"). |
| 122 """ | 122 """ |
| 123 base_directory = (base_directory or | 123 base_directory = (base_directory or |
| 124 os.path.join(os.path.dirname(__file__), '..', '..')) | 124 os.path.join(os.path.dirname(__file__), '..', '..')) |
| 125 self.base_directory = os.path.abspath(base_directory) | 125 self.base_directory = os.path.abspath(base_directory) |
| 126 self.verbose = verbose | 126 self.verbose = verbose |
| 127 self._under_test = being_tested | 127 self._under_test = being_tested |
| 128 self._ignore_temp_rules = ignore_temp_rules | 128 self._ignore_temp_rules = ignore_temp_rules |
| 129 self._ignore_specific_rules = ignore_specific_rules | 129 self._ignore_specific_rules = ignore_specific_rules |
| 130 | 130 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 146 from the "specific_include_rules" section of DEPS. | 146 from the "specific_include_rules" section of DEPS. |
| 147 cur_dir: The current directory, normalized path. We will create an | 147 cur_dir: The current directory, normalized path. We will create an |
| 148 implicit rule that allows inclusion from this directory. | 148 implicit rule that allows inclusion from this directory. |
| 149 | 149 |
| 150 Returns: A new set of rules combining the existing_rules with the other | 150 Returns: A new set of rules combining the existing_rules with the other |
| 151 arguments. | 151 arguments. |
| 152 """ | 152 """ |
| 153 rules = copy.deepcopy(existing_rules) | 153 rules = copy.deepcopy(existing_rules) |
| 154 | 154 |
| 155 # First apply the implicit "allow" rule for the current directory. | 155 # First apply the implicit "allow" rule for the current directory. |
| 156 if cur_dir.startswith( | 156 norm_base_dir = NormalizePath(os.path.normpath(self.base_directory)) |
| 157 NormalizePath(os.path.normpath(self.base_directory))): | 157 if not cur_dir.startswith(norm_base_dir): |
| 158 relative_dir = cur_dir[len(self.base_directory) + 1:] | 158 raise Exception( |
| 159 'Internal error: base directory is not at the beginning for\n' |
| 160 ' %s and base dir\n' |
| 161 ' %s' % (cur_dir, norm_base_dir)) |
| 162 relative_dir = os.path.relpath(cur_dir, norm_base_dir) |
| 159 | 163 |
| 160 source = relative_dir | 164 # Make the help string a little more meaningful. |
| 161 if len(source) == 0: | 165 source = relative_dir or 'top level' |
| 162 source = 'top level' # Make the help string a little more meaningful. | 166 rules.AddRule('+' + relative_dir, |
| 163 rules.AddRule('+' + relative_dir, | 167 relative_dir, |
| 164 relative_dir, | 168 'Default rule for ' + source) |
| 165 'Default rule for ' + source) | |
| 166 else: | |
| 167 raise Exception('Internal error: base directory is not at the beginning' + | |
| 168 ' for\n %s and base dir\n %s' % | |
| 169 (cur_dir, self.base_directory)) | |
| 170 | 169 |
| 171 def ApplyOneRule(rule_str, cur_dir, dependee_regexp=None): | 170 def ApplyOneRule(rule_str, cur_dir, dependee_regexp=None): |
| 172 """Deduces a sensible description for the rule being added, and | 171 """Deduces a sensible description for the rule being added, and |
| 173 adds the rule with its description to |rules|. | 172 adds the rule with its description to |rules|. |
| 174 | 173 |
| 175 If we are ignoring temporary rules, this function does nothing | 174 If we are ignoring temporary rules, this function does nothing |
| 176 for rules beginning with the Rule.TEMP_ALLOW character. | 175 for rules beginning with the Rule.TEMP_ALLOW character. |
| 177 """ | 176 """ |
| 178 if self._ignore_temp_rules and rule_str.startswith(Rule.TEMP_ALLOW): | 177 if self._ignore_temp_rules and rule_str.startswith(Rule.TEMP_ALLOW): |
| 179 return | 178 return |
| 180 | 179 |
| 181 rule_block_name = 'include_rules' | 180 rule_block_name = 'include_rules' |
| 182 if dependee_regexp: | 181 if dependee_regexp: |
| 183 rule_block_name = 'specific_include_rules' | 182 rule_block_name = 'specific_include_rules' |
| 184 if not relative_dir: | 183 if relative_dir: |
| 184 rule_description = relative_dir + "'s %s" % rule_block_name |
| 185 else: |
| 185 rule_description = 'the top level %s' % rule_block_name | 186 rule_description = 'the top level %s' % rule_block_name |
| 186 else: | |
| 187 rule_description = relative_dir + "'s %s" % rule_block_name | |
| 188 rules.AddRule(rule_str, relative_dir, rule_description, dependee_regexp) | 187 rules.AddRule(rule_str, relative_dir, rule_description, dependee_regexp) |
| 189 | 188 |
| 190 # Apply the additional explicit rules. | 189 # Apply the additional explicit rules. |
| 191 for (_, rule_str) in enumerate(includes): | 190 for rule_str in includes: |
| 192 ApplyOneRule(rule_str, cur_dir) | 191 ApplyOneRule(rule_str, cur_dir) |
| 193 | 192 |
| 194 # Finally, apply the specific rules. | 193 # Finally, apply the specific rules. |
| 195 if not self._ignore_specific_rules: | 194 if not self._ignore_specific_rules: |
| 196 for regexp, specific_rules in specific_includes.iteritems(): | 195 for regexp, specific_rules in specific_includes.iteritems(): |
| 197 for rule_str in specific_rules: | 196 for rule_str in specific_rules: |
| 198 ApplyOneRule(rule_str, cur_dir, regexp) | 197 ApplyOneRule(rule_str, cur_dir, regexp) |
| 199 | 198 |
| 200 return rules | 199 return rules |
| 201 | 200 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 215 | 214 |
| 216 Returns: A tuple containing: (1) the combined set of rules to apply to the | 215 Returns: A tuple containing: (1) the combined set of rules to apply to the |
| 217 sub-tree, and (2) a list of all subdirectories that should NOT be | 216 sub-tree, and (2) a list of all subdirectories that should NOT be |
| 218 checked, as specified in the DEPS file (if any). | 217 checked, as specified in the DEPS file (if any). |
| 219 """ | 218 """ |
| 220 norm_dir_name = NormalizePath(dir_name) | 219 norm_dir_name = NormalizePath(dir_name) |
| 221 | 220 |
| 222 # Check for a .svn directory in this directory or check this directory is | 221 # Check for a .svn directory in this directory or check this directory is |
| 223 # contained in git source direcotries. This will tell us if it's a source | 222 # contained in git source direcotries. This will tell us if it's a source |
| 224 # directory and should be checked. | 223 # directory and should be checked. |
| 225 if not (os.path.exists(os.path.join(dir_name, ".svn")) or | 224 if not (os.path.exists(os.path.join(dir_name, '.svn')) or |
| 226 (norm_dir_name in self.git_source_directories)): | 225 (norm_dir_name in self.git_source_directories)): |
| 227 return (None, []) | 226 return None, [] |
| 228 | 227 |
| 229 # Check the DEPS file in this directory. | 228 # Check the DEPS file in this directory. |
| 230 if self.verbose: | 229 if self.verbose: |
| 231 print 'Applying rules from', dir_name | 230 print 'Applying rules from', dir_name |
| 232 def FromImpl(_unused, _unused2): | 231 def FromImpl(*_): |
| 233 pass # NOP function so "From" doesn't fail. | 232 pass # NOP function so "From" doesn't fail. |
| 234 | 233 |
| 235 def FileImpl(_unused): | 234 def FileImpl(_): |
| 236 pass # NOP function so "File" doesn't fail. | 235 pass # NOP function so "File" doesn't fail. |
| 237 | 236 |
| 238 class _VarImpl: | 237 class _VarImpl: |
| 239 def __init__(self, local_scope): | 238 def __init__(self, local_scope): |
| 240 self._local_scope = local_scope | 239 self._local_scope = local_scope |
| 241 | 240 |
| 242 def Lookup(self, var_name): | 241 def Lookup(self, var_name): |
| 243 """Implements the Var syntax.""" | 242 """Implements the Var syntax.""" |
| 244 if var_name in self._local_scope.get('vars', {}): | 243 try: |
| 245 return self._local_scope['vars'][var_name] | 244 return self._local_scope['vars'][var_name] |
| 246 raise Exception('Var is not defined: %s' % var_name) | 245 except KeyError: |
| 246 raise Exception('Var is not defined: %s' % var_name) |
| 247 | 247 |
| 248 local_scope = {} | 248 local_scope = {} |
| 249 global_scope = { | 249 global_scope = { |
| 250 'File': FileImpl, | 250 'File': FileImpl, |
| 251 'From': FromImpl, | 251 'From': FromImpl, |
| 252 'Var': _VarImpl(local_scope).Lookup, | 252 'Var': _VarImpl(local_scope).Lookup, |
| 253 } | 253 } |
| 254 deps_file = os.path.join(dir_name, 'DEPS') | 254 deps_file = os.path.join(dir_name, 'DEPS') |
| 255 | 255 |
| 256 # The second conditional here is to disregard the | 256 # The second conditional here is to disregard the |
| 257 # tools/checkdeps/DEPS file while running tests. This DEPS file | 257 # tools/checkdeps/DEPS file while running tests. This DEPS file |
| 258 # has a skip_child_includes for 'testdata' which is necessary for | 258 # has a skip_child_includes for 'testdata' which is necessary for |
| 259 # running production tests, since there are intentional DEPS | 259 # running production tests, since there are intentional DEPS |
| 260 # violations under the testdata directory. On the other hand when | 260 # violations under the testdata directory. On the other hand when |
| 261 # running tests, we absolutely need to verify the contents of that | 261 # running tests, we absolutely need to verify the contents of that |
| 262 # directory to trigger those intended violations and see that they | 262 # directory to trigger those intended violations and see that they |
| 263 # are handled correctly. | 263 # are handled correctly. |
| 264 if os.path.isfile(deps_file) and ( | 264 if os.path.isfile(deps_file) and ( |
| 265 not self._under_test or not os.path.split(dir_name)[1] == 'checkdeps'): | 265 not self._under_test or not os.path.basename(dir_name) == 'checkdeps'): |
| 266 execfile(deps_file, global_scope, local_scope) | 266 execfile(deps_file, global_scope, local_scope) |
| 267 elif self.verbose: | 267 elif self.verbose: |
| 268 print ' No deps file found in', dir_name | 268 print ' No deps file found in', dir_name |
| 269 | 269 |
| 270 # Even if a DEPS file does not exist we still invoke ApplyRules | 270 # Even if a DEPS file does not exist we still invoke ApplyRules |
| 271 # to apply the implicit "allow" rule for the current directory | 271 # to apply the implicit "allow" rule for the current directory |
| 272 include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, []) | 272 include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, []) |
| 273 specific_include_rules = local_scope.get(SPECIFIC_INCLUDE_RULES_VAR_NAME, | 273 specific_include_rules = local_scope.get(SPECIFIC_INCLUDE_RULES_VAR_NAME, |
| 274 {}) | 274 {}) |
| 275 skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, []) | 275 skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, []) |
| (...skipping 27 matching lines...) Expand all Loading... |
| 303 """ | 303 """ |
| 304 norm_dir_path = NormalizePath(dir_path) | 304 norm_dir_path = NormalizePath(dir_path) |
| 305 | 305 |
| 306 if not norm_dir_path.startswith( | 306 if not norm_dir_path.startswith( |
| 307 NormalizePath(os.path.normpath(self.base_directory))): | 307 NormalizePath(os.path.normpath(self.base_directory))): |
| 308 dir_path = os.path.join(self.base_directory, dir_path) | 308 dir_path = os.path.join(self.base_directory, dir_path) |
| 309 norm_dir_path = NormalizePath(dir_path) | 309 norm_dir_path = NormalizePath(dir_path) |
| 310 | 310 |
| 311 parent_dir = os.path.dirname(dir_path) | 311 parent_dir = os.path.dirname(dir_path) |
| 312 parent_rules = None | 312 parent_rules = None |
| 313 if not norm_dir_path in self.directory_rules: | 313 if norm_dir_path not in self.directory_rules: |
| 314 parent_rules = self.GetDirectoryRules(parent_dir) | 314 parent_rules = self.GetDirectoryRules(parent_dir) |
| 315 | 315 |
| 316 # We need to check for an entry for our dir_path again, in case we | 316 # We need to check for an entry for our dir_path again, in case we |
| 317 # are at a path e.g. A/B/C where A/B/DEPS specifies the C | 317 # are at a path e.g. A/B/C where A/B/DEPS specifies the C |
| 318 # subdirectory to be skipped; in this case, the invocation to | 318 # subdirectory to be skipped; in this case, the invocation to |
| 319 # GetDirectoryRules(parent_dir) has already filled in an entry for | 319 # GetDirectoryRules(parent_dir) has already filled in an entry for |
| 320 # A/B/C. | 320 # A/B/C. |
| 321 if not norm_dir_path in self.directory_rules: | 321 if norm_dir_path not in self.directory_rules: |
| 322 if not parent_rules: | 322 if parent_rules: |
| 323 self._ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path) |
| 324 else: |
| 323 # If the parent directory should be skipped, then the current | 325 # If the parent directory should be skipped, then the current |
| 324 # directory should also be skipped. | 326 # directory should also be skipped. |
| 325 self.directory_rules[norm_dir_path] = None | 327 self.directory_rules[norm_dir_path] = None |
| 326 else: | |
| 327 self._ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path) | |
| 328 return self.directory_rules[norm_dir_path] | 328 return self.directory_rules[norm_dir_path] |
| 329 | 329 |
| 330 def _AddGitSourceDirectories(self): | 330 def _AddGitSourceDirectories(self): |
| 331 """Adds any directories containing sources managed by git to | 331 """Adds any directories containing sources managed by git to |
| 332 self.git_source_directories. | 332 self.git_source_directories. |
| 333 """ | 333 """ |
| 334 if not os.path.exists(os.path.join(self.base_directory, '.git')): | 334 if not os.path.exists(os.path.join(self.base_directory, '.git')): |
| 335 return | 335 return |
| 336 | 336 |
| 337 popen_out = os.popen('cd %s && git ls-files --full-name .' % | 337 popen_out = os.popen('cd %s && git ls-files --full-name .' % |
| 338 subprocess.list2cmdline([self.base_directory])) | 338 subprocess.list2cmdline([self.base_directory])) |
| 339 for line in popen_out.readlines(): | 339 for line in popen_out.readlines(): |
| 340 dir_name = os.path.join(self.base_directory, os.path.dirname(line)) | 340 dir_name = os.path.join(self.base_directory, os.path.dirname(line)) |
| 341 # Add the directory as well as all the parent directories. Use | 341 # Add the directory as well as all the parent directories. Use |
| 342 # forward slashes and lower case to normalize paths. | 342 # forward slashes and lower case to normalize paths. |
| 343 while dir_name != self.base_directory: | 343 while dir_name != self.base_directory: |
| 344 self.git_source_directories.add(NormalizePath(dir_name)) | 344 self.git_source_directories.add(NormalizePath(dir_name)) |
| 345 dir_name = os.path.dirname(dir_name) | 345 dir_name = os.path.dirname(dir_name) |
| 346 self.git_source_directories.add(NormalizePath(self.base_directory)) | 346 self.git_source_directories.add(NormalizePath(self.base_directory)) |
| OLD | NEW |