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

Side by Side Diff: tools/checkdeps/checkdeps.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 | « no previous file | tools/checkdeps/checkdeps_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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())
OLDNEW
« no previous file with comments | « no previous file | tools/checkdeps/checkdeps_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698