| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 """Makes sure that files include headers from allowed directories. | 6 """Makes sure that files include headers from allowed directories. |
| 7 | 7 |
| 8 Checks DEPS files in the source tree for rules, and applies those rules to | 8 Checks DEPS files in the source tree for rules, and applies those rules to |
| 9 "#include" commands in source files. Any source file including something not | 9 "#include" commands in source files. Any source file including something not |
| 10 permitted by the DEPS files will fail. | 10 permitted by the DEPS files will fail. |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 | 40 |
| 41 # And it can include files from this other directory even though there is | 41 # And it can include files from this other directory even though there is |
| 42 # no deps rule for it. | 42 # no deps rule for it. |
| 43 "+tools/crime_fighter", | 43 "+tools/crime_fighter", |
| 44 | 44 |
| 45 # This dependency is allowed for now but work is ongoing to remove it, | 45 # This dependency is allowed for now but work is ongoing to remove it, |
| 46 # so you shouldn't add further dependencies on it. | 46 # so you shouldn't add further dependencies on it. |
| 47 "!base/evil/ok_for_now.h", | 47 "!base/evil/ok_for_now.h", |
| 48 } | 48 } |
| 49 | 49 |
| 50 If you have certain include rules that should only be applied for some |
| 51 files within this directory and subdirectories, you can write a |
| 52 section named specific_include_rules that is a hash map of regular |
| 53 expressions to the list of rules that should apply to files matching |
| 54 them. Note that such rules will always be applied before the rules |
| 55 from 'include_rules' have been applied, but the order in which rules |
| 56 associated with different regular expressions is applied is arbitrary. |
| 57 |
| 58 specific_include_rules = { |
| 59 ".*_(unit|browser|api)test\.cc": [ |
| 60 "+libraries/testsupport", |
| 61 ], |
| 62 } |
| 63 |
| 50 DEPS files may be placed anywhere in the tree. Each one applies to all | 64 DEPS files may be placed anywhere in the tree. Each one applies to all |
| 51 subdirectories, where there may be more DEPS files that provide additions or | 65 subdirectories, where there may be more DEPS files that provide additions or |
| 52 subtractions for their own sub-trees. | 66 subtractions for their own sub-trees. |
| 53 | 67 |
| 54 There is an implicit rule for the current directory (where the DEPS file lives) | 68 There is an implicit rule for the current directory (where the DEPS file lives) |
| 55 and all of its subdirectories. This prevents you from having to explicitly | 69 and all of its subdirectories. This prevents you from having to explicitly |
| 56 allow the current directory everywhere. This implicit rule is applied first, | 70 allow the current directory everywhere. This implicit rule is applied first, |
| 57 so you can modify or remove it using the normal include rules. | 71 so you can modify or remove it using the normal include rules. |
| 58 | 72 |
| 59 The rules are processed in order. This means you can explicitly allow a higher | 73 The rules are processed in order. This means you can explicitly allow a higher |
| (...skipping 13 matching lines...) Expand all Loading... |
| 73 import cpp_checker | 87 import cpp_checker |
| 74 import java_checker | 88 import java_checker |
| 75 from results import NormalResultsFormatter, TemporaryRulesFormatter | 89 from results import NormalResultsFormatter, TemporaryRulesFormatter |
| 76 from rules import Rule, Rules | 90 from rules import Rule, Rules |
| 77 | 91 |
| 78 | 92 |
| 79 # Variable name used in the DEPS file to add or subtract include files from | 93 # Variable name used in the DEPS file to add or subtract include files from |
| 80 # the module-level deps. | 94 # the module-level deps. |
| 81 INCLUDE_RULES_VAR_NAME = 'include_rules' | 95 INCLUDE_RULES_VAR_NAME = 'include_rules' |
| 82 | 96 |
| 97 # Variable name used in the DEPS file to add or subtract include files |
| 98 # from module-level deps specific to files whose basename (last |
| 99 # component of path) matches a given regular expression. |
| 100 SPECIFIC_INCLUDE_RULES_VAR_NAME = 'specific_include_rules' |
| 101 |
| 83 # Optionally present in the DEPS file to list subdirectories which should not | 102 # Optionally present in the DEPS file to list subdirectories which should not |
| 84 # be checked. This allows us to skip third party code, for example. | 103 # be checked. This allows us to skip third party code, for example. |
| 85 SKIP_SUBDIRS_VAR_NAME = 'skip_child_includes' | 104 SKIP_SUBDIRS_VAR_NAME = 'skip_child_includes' |
| 86 | 105 |
| 87 | 106 |
| 88 def NormalizePath(path): | 107 def NormalizePath(path): |
| 89 """Returns a path normalized to how we write DEPS rules and compare paths. | 108 """Returns a path normalized to how we write DEPS rules and compare paths. |
| 90 """ | 109 """ |
| 91 return path.lower().replace('\\', '/') | 110 return path.lower().replace('\\', '/') |
| 92 | 111 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 123 self._ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory) | 142 self._ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory) |
| 124 | 143 |
| 125 def Report(self): | 144 def Report(self): |
| 126 """Prints a report of results, and returns an exit code for the process.""" | 145 """Prints a report of results, and returns an exit code for the process.""" |
| 127 if self.results_formatter.GetResults(): | 146 if self.results_formatter.GetResults(): |
| 128 self.results_formatter.PrintResults() | 147 self.results_formatter.PrintResults() |
| 129 return 1 | 148 return 1 |
| 130 print '\nSUCCESS\n' | 149 print '\nSUCCESS\n' |
| 131 return 0 | 150 return 0 |
| 132 | 151 |
| 133 def _ApplyRules(self, existing_rules, includes, cur_dir): | 152 def _ApplyRules(self, existing_rules, includes, specific_includes, cur_dir): |
| 134 """Applies the given include rules, returning the new rules. | 153 """Applies the given include rules, returning the new rules. |
| 135 | 154 |
| 136 Args: | 155 Args: |
| 137 existing_rules: A set of existing rules that will be combined. | 156 existing_rules: A set of existing rules that will be combined. |
| 138 include: The list of rules from the "include_rules" section of DEPS. | 157 include: The list of rules from the "include_rules" section of DEPS. |
| 158 specific_includes: E.g. {'.*_unittest\.cc': ['+foo', '-blat']} rules |
| 159 from the "specific_include_rules" section of DEPS. |
| 139 cur_dir: The current directory, normalized path. We will create an | 160 cur_dir: The current directory, normalized path. We will create an |
| 140 implicit rule that allows inclusion from this directory. | 161 implicit rule that allows inclusion from this directory. |
| 141 | 162 |
| 142 Returns: A new set of rules combining the existing_rules with the other | 163 Returns: A new set of rules combining the existing_rules with the other |
| 143 arguments. | 164 arguments. |
| 144 """ | 165 """ |
| 145 rules = copy.copy(existing_rules) | 166 rules = copy.copy(existing_rules) |
| 146 | 167 |
| 147 # First apply the implicit "allow" rule for the current directory. | 168 # First apply the implicit "allow" rule for the current directory. |
| 148 if cur_dir.startswith( | 169 if cur_dir.startswith( |
| 149 NormalizePath(os.path.normpath(self.base_directory))): | 170 NormalizePath(os.path.normpath(self.base_directory))): |
| 150 relative_dir = cur_dir[len(self.base_directory) + 1:] | 171 relative_dir = cur_dir[len(self.base_directory) + 1:] |
| 151 | 172 |
| 152 source = relative_dir | 173 source = relative_dir |
| 153 if len(source) == 0: | 174 if len(source) == 0: |
| 154 source = 'top level' # Make the help string a little more meaningful. | 175 source = 'top level' # Make the help string a little more meaningful. |
| 155 rules.AddRule('+' + relative_dir, 'Default rule for ' + source) | 176 rules.AddRule('+' + relative_dir, 'Default rule for ' + source) |
| 156 else: | 177 else: |
| 157 raise Exception('Internal error: base directory is not at the beginning' + | 178 raise Exception('Internal error: base directory is not at the beginning' + |
| 158 ' for\n %s and base dir\n %s' % | 179 ' for\n %s and base dir\n %s' % |
| 159 (cur_dir, self.base_directory)) | 180 (cur_dir, self.base_directory)) |
| 160 | 181 |
| 161 # Last, apply the additional explicit rules. | 182 def AddRuleWithDescription(rule_str, dependee_regexp=None): |
| 183 rule_block_name = 'include_rules' |
| 184 if dependee_regexp: |
| 185 rule_block_name = 'specific_include_rules' |
| 186 if not relative_dir: |
| 187 rule_description = 'the top level %s' % rule_block_name |
| 188 else: |
| 189 rule_description = relative_dir + "'s %s" % rule_block_name |
| 190 rules.AddRule(rule_str, rule_description, dependee_regexp) |
| 191 |
| 192 # Apply the additional explicit rules. |
| 162 for (_, rule_str) in enumerate(includes): | 193 for (_, rule_str) in enumerate(includes): |
| 163 if not relative_dir: | 194 AddRuleWithDescription(rule_str) |
| 164 rule_description = 'the top level include_rules' | 195 |
| 165 else: | 196 # Finally, apply the specific rules. |
| 166 rule_description = relative_dir + "'s include_rules" | 197 for regexp, specific_rules in specific_includes.iteritems(): |
| 167 rules.AddRule(rule_str, rule_description) | 198 for rule_str in specific_rules: |
| 199 AddRuleWithDescription(rule_str, regexp) |
| 168 | 200 |
| 169 return rules | 201 return rules |
| 170 | 202 |
| 171 def _ApplyDirectoryRules(self, existing_rules, dir_name): | 203 def _ApplyDirectoryRules(self, existing_rules, dir_name): |
| 172 """Combines rules from the existing rules and the new directory. | 204 """Combines rules from the existing rules and the new directory. |
| 173 | 205 |
| 174 Any directory can contain a DEPS file. Toplevel DEPS files can contain | 206 Any directory can contain a DEPS file. Toplevel DEPS files can contain |
| 175 module dependencies which are used by gclient. We use these, along with | 207 module dependencies which are used by gclient. We use these, along with |
| 176 additional include rules and implicit rules for the given directory, to | 208 additional include rules and implicit rules for the given directory, to |
| 177 come up with a combined set of rules to apply for the directory. | 209 come up with a combined set of rules to apply for the directory. |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 232 # are handled correctly. | 264 # are handled correctly. |
| 233 if os.path.isfile(deps_file) and ( | 265 if os.path.isfile(deps_file) and ( |
| 234 not self._under_test or not os.path.split(dir_name)[1] == 'checkdeps'): | 266 not self._under_test or not os.path.split(dir_name)[1] == 'checkdeps'): |
| 235 execfile(deps_file, global_scope, local_scope) | 267 execfile(deps_file, global_scope, local_scope) |
| 236 elif self.verbose: | 268 elif self.verbose: |
| 237 print ' No deps file found in', dir_name | 269 print ' No deps file found in', dir_name |
| 238 | 270 |
| 239 # Even if a DEPS file does not exist we still invoke ApplyRules | 271 # Even if a DEPS file does not exist we still invoke ApplyRules |
| 240 # to apply the implicit "allow" rule for the current directory | 272 # to apply the implicit "allow" rule for the current directory |
| 241 include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, []) | 273 include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, []) |
| 274 specific_include_rules = local_scope.get(SPECIFIC_INCLUDE_RULES_VAR_NAME, |
| 275 {}) |
| 242 skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, []) | 276 skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, []) |
| 243 | 277 |
| 244 return (self._ApplyRules(existing_rules, include_rules, norm_dir_name), | 278 return (self._ApplyRules(existing_rules, include_rules, |
| 279 specific_include_rules, norm_dir_name), |
| 245 skip_subdirs) | 280 skip_subdirs) |
| 246 | 281 |
| 247 def _ApplyDirectoryRulesAndSkipSubdirs(self, parent_rules, dir_path): | 282 def _ApplyDirectoryRulesAndSkipSubdirs(self, parent_rules, dir_path): |
| 248 """Given |parent_rules| and a subdirectory |dir_path| from the | 283 """Given |parent_rules| and a subdirectory |dir_path| from the |
| 249 directory that owns the |parent_rules|, add |dir_path|'s rules to | 284 directory that owns the |parent_rules|, add |dir_path|'s rules to |
| 250 |self.directory_rules|, and add None entries for any of its | 285 |self.directory_rules|, and add None entries for any of its |
| 251 subdirectories that should be skipped. | 286 subdirectories that should be skipped. |
| 252 """ | 287 """ |
| 253 directory_rules, excluded_subdirs = self._ApplyDirectoryRules(parent_rules, | 288 directory_rules, excluded_subdirs = self._ApplyDirectoryRules(parent_rules, |
| 254 dir_path) | 289 dir_path) |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 350 """ | 385 """ |
| 351 cpp = cpp_checker.CppChecker(self.verbose) | 386 cpp = cpp_checker.CppChecker(self.verbose) |
| 352 problems = [] | 387 problems = [] |
| 353 for file_path, include_lines in added_includes: | 388 for file_path, include_lines in added_includes: |
| 354 # TODO(joi): Make this cover Java as well. | 389 # TODO(joi): Make this cover Java as well. |
| 355 if not cpp.IsCppFile(file_path): | 390 if not cpp.IsCppFile(file_path): |
| 356 pass | 391 pass |
| 357 rules_for_file = self.GetDirectoryRules(os.path.dirname(file_path)) | 392 rules_for_file = self.GetDirectoryRules(os.path.dirname(file_path)) |
| 358 if rules_for_file: | 393 if rules_for_file: |
| 359 for line in include_lines: | 394 for line in include_lines: |
| 360 is_include, violation = cpp.CheckLine(rules_for_file, line, True) | 395 is_include, violation = cpp.CheckLine( |
| 396 rules_for_file, line, file_path, True) |
| 361 if violation: | 397 if violation: |
| 362 rule_type = violation.violated_rule.allow | 398 rule_type = violation.violated_rule.allow |
| 363 if rule_type != Rule.ALLOW: | 399 if rule_type != Rule.ALLOW: |
| 364 violation_text = NormalResultsFormatter.FormatViolation( | 400 violation_text = NormalResultsFormatter.FormatViolation( |
| 365 violation, self.verbose) | 401 violation, self.verbose) |
| 366 problems.append((file_path, rule_type, violation_text)) | 402 problems.append((file_path, rule_type, violation_text)) |
| 367 return problems | 403 return problems |
| 368 | 404 |
| 369 def _AddGitSourceDirectories(self): | 405 def _AddGitSourceDirectories(self): |
| 370 """Adds any directories containing sources managed by git to | 406 """Adds any directories containing sources managed by git to |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 431 print 'Checking:', start_dir | 467 print 'Checking:', start_dir |
| 432 | 468 |
| 433 if options.temprules: | 469 if options.temprules: |
| 434 deps_checker.results_formatter = TemporaryRulesFormatter() | 470 deps_checker.results_formatter = TemporaryRulesFormatter() |
| 435 deps_checker.CheckDirectory(start_dir) | 471 deps_checker.CheckDirectory(start_dir) |
| 436 return deps_checker.Report() | 472 return deps_checker.Report() |
| 437 | 473 |
| 438 | 474 |
| 439 if '__main__' == __name__: | 475 if '__main__' == __name__: |
| 440 sys.exit(main()) | 476 sys.exit(main()) |
| OLD | NEW |