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 |