Chromium Code Reviews| 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 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 65 """ | 65 """ |
| 66 | 66 |
| 67 import os | 67 import os |
| 68 import optparse | 68 import optparse |
| 69 import subprocess | 69 import subprocess |
| 70 import sys | 70 import sys |
| 71 import copy | 71 import copy |
| 72 | 72 |
| 73 import cpp_checker | 73 import cpp_checker |
| 74 import java_checker | 74 import java_checker |
| 75 import results | |
| 75 from rules import Rule, Rules | 76 from rules import Rule, Rules |
| 76 | 77 |
| 77 | 78 |
| 78 # Variable name used in the DEPS file to add or subtract include files from | 79 # Variable name used in the DEPS file to add or subtract include files from |
| 79 # the module-level deps. | 80 # the module-level deps. |
| 80 INCLUDE_RULES_VAR_NAME = "include_rules" | 81 INCLUDE_RULES_VAR_NAME = 'include_rules' |
| 81 | 82 |
| 82 # Optionally present in the DEPS file to list subdirectories which should not | 83 # Optionally present in the DEPS file to list subdirectories which should not |
| 83 # be checked. This allows us to skip third party code, for example. | 84 # be checked. This allows us to skip third party code, for example. |
| 84 SKIP_SUBDIRS_VAR_NAME = "skip_child_includes" | 85 SKIP_SUBDIRS_VAR_NAME = 'skip_child_includes' |
| 85 | 86 |
| 86 | 87 |
| 87 def NormalizePath(path): | 88 def NormalizePath(path): |
| 88 """Returns a path normalized to how we write DEPS rules and compare paths. | 89 """Returns a path normalized to how we write DEPS rules and compare paths. |
| 89 """ | 90 """ |
| 90 return path.lower().replace('\\', '/') | 91 return path.lower().replace('\\', '/') |
| 91 | 92 |
| 92 | 93 |
| 93 class DepsChecker(object): | 94 class DepsChecker(object): |
| 94 """Parses include_rules from DEPS files and can verify files in the | 95 """Parses include_rules from DEPS files and can verify files in the |
| 95 source tree against them. | 96 source tree against them. |
| 96 """ | 97 """ |
| 97 | 98 |
| 98 def __init__(self, base_directory=None, verbose=False, being_tested=False): | 99 def __init__(self, base_directory=None, verbose=False, being_tested=False): |
| 99 """Creates a new DepsChecker. | 100 """Creates a new DepsChecker. |
| 100 | 101 |
| 101 Args: | 102 Args: |
| 102 base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src. | 103 base_directory: OS-compatible path to root of checkout, e.g. C:\chr\src. |
| 103 verbose: Set to true for debug output. | 104 verbose: Set to true for debug output. |
| 104 being_tested: Set to true to ignore the DEPS file at tools/checkdeps/DEPS. | 105 being_tested: Set to true to ignore the DEPS file at tools/checkdeps/DEPS. |
| 105 """ | 106 """ |
| 106 self.base_directory = base_directory | 107 self.base_directory = base_directory |
| 107 if not base_directory: | 108 if not base_directory: |
| 108 self.base_directory = os.path.abspath( | 109 self.base_directory = os.path.abspath( |
| 109 os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..')) | 110 os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..')) |
| 110 | 111 |
| 111 self.verbose = verbose | 112 self.verbose = verbose |
| 113 self.results_formatter = results.NormalResultsFormatter(verbose) | |
| 114 | |
| 115 self._under_test = being_tested | |
| 112 | 116 |
| 113 self.git_source_directories = set() | 117 self.git_source_directories = set() |
| 114 self._AddGitSourceDirectories() | 118 self._AddGitSourceDirectories() |
| 115 | 119 |
| 116 self._under_test = being_tested | 120 # Map of normalized directory paths to rules to use for those |
| 121 # directories, or None for directories that should be skipped. | |
| 122 self.directory_rules = {} | |
| 123 self._ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory) | |
| 124 | |
| 125 def Report(self): | |
| 126 """Prints a report of results, and returns an exit code for the process.""" | |
| 127 if self.results_formatter.GetResults(): | |
| 128 self.results_formatter.PrintResults() | |
| 129 return 1 | |
| 130 print '\nSUCCESS\n' | |
| 131 return 0 | |
| 117 | 132 |
| 118 def _ApplyRules(self, existing_rules, includes, cur_dir): | 133 def _ApplyRules(self, existing_rules, includes, cur_dir): |
| 119 """Applies the given include rules, returning the new rules. | 134 """Applies the given include rules, returning the new rules. |
| 120 | 135 |
| 121 Args: | 136 Args: |
| 122 existing_rules: A set of existing rules that will be combined. | 137 existing_rules: A set of existing rules that will be combined. |
| 123 include: The list of rules from the "include_rules" section of DEPS. | 138 include: The list of rules from the "include_rules" section of DEPS. |
| 124 cur_dir: The current directory, normalized path. We will create an | 139 cur_dir: The current directory, normalized path. We will create an |
| 125 implicit rule that allows inclusion from this directory. | 140 implicit rule that allows inclusion from this directory. |
| 126 | 141 |
| 127 Returns: A new set of rules combining the existing_rules with the other | 142 Returns: A new set of rules combining the existing_rules with the other |
| 128 arguments. | 143 arguments. |
| 129 """ | 144 """ |
| 130 rules = copy.copy(existing_rules) | 145 rules = copy.copy(existing_rules) |
| 131 | 146 |
| 132 # First apply the implicit "allow" rule for the current directory. | 147 # First apply the implicit "allow" rule for the current directory. |
| 133 if cur_dir.startswith( | 148 if cur_dir.startswith( |
| 134 NormalizePath(os.path.normpath(self.base_directory))): | 149 NormalizePath(os.path.normpath(self.base_directory))): |
| 135 relative_dir = cur_dir[len(self.base_directory) + 1:] | 150 relative_dir = cur_dir[len(self.base_directory) + 1:] |
| 136 | 151 |
| 137 source = relative_dir | 152 source = relative_dir |
| 138 if len(source) == 0: | 153 if len(source) == 0: |
| 139 source = "top level" # Make the help string a little more meaningful. | 154 source = 'top level' # Make the help string a little more meaningful. |
| 140 rules.AddRule("+" + relative_dir, "Default rule for " + source) | 155 rules.AddRule('+' + relative_dir, 'Default rule for ' + source) |
| 141 else: | 156 else: |
| 142 raise Exception("Internal error: base directory is not at the beginning" + | 157 raise Exception('Internal error: base directory is not at the beginning' + |
| 143 " for\n %s and base dir\n %s" % | 158 ' for\n %s and base dir\n %s' % |
| 144 (cur_dir, self.base_directory)) | 159 (cur_dir, self.base_directory)) |
| 145 | 160 |
| 146 # Last, apply the additional explicit rules. | 161 # Last, apply the additional explicit rules. |
| 147 for (_, rule_str) in enumerate(includes): | 162 for (_, rule_str) in enumerate(includes): |
| 148 if not relative_dir: | 163 if not relative_dir: |
| 149 rule_description = "the top level include_rules" | 164 rule_description = 'the top level include_rules' |
| 150 else: | 165 else: |
| 151 rule_description = relative_dir + "'s include_rules" | 166 rule_description = relative_dir + "'s include_rules" |
| 152 rules.AddRule(rule_str, rule_description) | 167 rules.AddRule(rule_str, rule_description) |
| 153 | 168 |
| 154 return rules | 169 return rules |
| 155 | 170 |
| 156 def _ApplyDirectoryRules(self, existing_rules, dir_name): | 171 def _ApplyDirectoryRules(self, existing_rules, dir_name): |
| 157 """Combines rules from the existing rules and the new directory. | 172 """Combines rules from the existing rules and the new directory. |
| 158 | 173 |
| 159 Any directory can contain a DEPS file. Toplevel DEPS files can contain | 174 Any directory can contain a DEPS file. Toplevel DEPS files can contain |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 175 | 190 |
| 176 # Check for a .svn directory in this directory or check this directory is | 191 # Check for a .svn directory in this directory or check this directory is |
| 177 # contained in git source direcotries. This will tell us if it's a source | 192 # contained in git source direcotries. This will tell us if it's a source |
| 178 # directory and should be checked. | 193 # directory and should be checked. |
| 179 if not (os.path.exists(os.path.join(dir_name, ".svn")) or | 194 if not (os.path.exists(os.path.join(dir_name, ".svn")) or |
| 180 (norm_dir_name in self.git_source_directories)): | 195 (norm_dir_name in self.git_source_directories)): |
| 181 return (None, []) | 196 return (None, []) |
| 182 | 197 |
| 183 # Check the DEPS file in this directory. | 198 # Check the DEPS file in this directory. |
| 184 if self.verbose: | 199 if self.verbose: |
| 185 print "Applying rules from", dir_name | 200 print 'Applying rules from', dir_name |
| 186 def FromImpl(_unused, _unused2): | 201 def FromImpl(_unused, _unused2): |
| 187 pass # NOP function so "From" doesn't fail. | 202 pass # NOP function so "From" doesn't fail. |
| 188 | 203 |
| 189 def FileImpl(_unused): | 204 def FileImpl(_unused): |
| 190 pass # NOP function so "File" doesn't fail. | 205 pass # NOP function so "File" doesn't fail. |
| 191 | 206 |
| 192 class _VarImpl: | 207 class _VarImpl: |
| 193 def __init__(self, local_scope): | 208 def __init__(self, local_scope): |
| 194 self._local_scope = local_scope | 209 self._local_scope = local_scope |
| 195 | 210 |
| 196 def Lookup(self, var_name): | 211 def Lookup(self, var_name): |
| 197 """Implements the Var syntax.""" | 212 """Implements the Var syntax.""" |
| 198 if var_name in self._local_scope.get("vars", {}): | 213 if var_name in self._local_scope.get('vars', {}): |
| 199 return self._local_scope["vars"][var_name] | 214 return self._local_scope['vars'][var_name] |
| 200 raise Exception("Var is not defined: %s" % var_name) | 215 raise Exception('Var is not defined: %s' % var_name) |
| 201 | 216 |
| 202 local_scope = {} | 217 local_scope = {} |
| 203 global_scope = { | 218 global_scope = { |
| 204 "File": FileImpl, | 219 'File': FileImpl, |
| 205 "From": FromImpl, | 220 'From': FromImpl, |
| 206 "Var": _VarImpl(local_scope).Lookup, | 221 'Var': _VarImpl(local_scope).Lookup, |
| 207 } | 222 } |
| 208 deps_file = os.path.join(dir_name, "DEPS") | 223 deps_file = os.path.join(dir_name, 'DEPS') |
| 209 | 224 |
| 210 # The second conditional here is to disregard the | 225 # The second conditional here is to disregard the |
| 211 # tools/checkdeps/DEPS file while running tests. This DEPS file | 226 # tools/checkdeps/DEPS file while running tests. This DEPS file |
| 212 # has a skip_child_includes for 'testdata' which is necessary for | 227 # has a skip_child_includes for 'testdata' which is necessary for |
| 213 # running production tests, since there are intentional DEPS | 228 # running production tests, since there are intentional DEPS |
| 214 # violations under the testdata directory. On the other hand when | 229 # violations under the testdata directory. On the other hand when |
| 215 # running tests, we absolutely need to verify the contents of that | 230 # running tests, we absolutely need to verify the contents of that |
| 216 # directory to trigger those intended violations and see that they | 231 # directory to trigger those intended violations and see that they |
| 217 # are handled correctly. | 232 # are handled correctly. |
| 218 if os.path.isfile(deps_file) and ( | 233 if os.path.isfile(deps_file) and ( |
| 219 not self._under_test or not os.path.split(dir_name)[1] == 'checkdeps'): | 234 not self._under_test or not os.path.split(dir_name)[1] == 'checkdeps'): |
| 220 execfile(deps_file, global_scope, local_scope) | 235 execfile(deps_file, global_scope, local_scope) |
| 221 elif self.verbose: | 236 elif self.verbose: |
| 222 print " No deps file found in", dir_name | 237 print ' No deps file found in', dir_name |
| 223 | 238 |
| 224 # Even if a DEPS file does not exist we still invoke ApplyRules | 239 # Even if a DEPS file does not exist we still invoke ApplyRules |
| 225 # to apply the implicit "allow" rule for the current directory | 240 # to apply the implicit "allow" rule for the current directory |
| 226 include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, []) | 241 include_rules = local_scope.get(INCLUDE_RULES_VAR_NAME, []) |
| 227 skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, []) | 242 skip_subdirs = local_scope.get(SKIP_SUBDIRS_VAR_NAME, []) |
| 228 | 243 |
| 229 return (self._ApplyRules(existing_rules, include_rules, norm_dir_name), | 244 return (self._ApplyRules(existing_rules, include_rules, norm_dir_name), |
| 230 skip_subdirs) | 245 skip_subdirs) |
| 231 | 246 |
| 247 def _ApplyDirectoryRulesAndSkipSubdirs(self, parent_rules, dir_path): | |
| 248 """Given |parent_rules| and a subdirectory |dir_path| from the | |
| 249 directory that owns the |parent_rules|, add the current | |
|
erikwright (departed)
2012/07/31 17:22:45
'current directory' is a bit confusing here. I ass
Jói
2012/08/01 15:22:58
Done.
| |
| 250 directory's rules to |self.directory_rules|, and add None entries | |
| 251 for any of its subdirectories that should be skipped. | |
| 252 """ | |
| 253 rules_tuple = self._ApplyDirectoryRules(parent_rules, dir_path) | |
|
erikwright (departed)
2012/07/31 17:22:45
For documentation, might I suggest something like:
Jói
2012/08/01 15:22:58
Done.
| |
| 254 self.directory_rules[NormalizePath(dir_path)] = rules_tuple[0] | |
| 255 for subdir in rules_tuple[1]: | |
| 256 # We skip this one case for running tests. | |
|
erikwright (departed)
2012/07/31 17:22:45
This comment is confusing and probably unnecessary
Jói
2012/08/01 15:22:58
Whoops, it was something I meant to remove along w
| |
| 257 self.directory_rules[NormalizePath( | |
| 258 os.path.normpath(os.path.join(dir_path, subdir)))] = None | |
| 259 | |
| 260 def GetDirectoryRules(self, dir_path): | |
| 261 """Returns a Rules object to use for the given directory, or None | |
| 262 if the given directory should be skipped. This takes care of | |
| 263 first building rules for parent directories (up to | |
| 264 self.base_directory) if needed. | |
| 265 | |
| 266 Args: | |
| 267 dir_path: A real (non-normalized) path to the directory you want | |
| 268 rules for. | |
| 269 """ | |
| 270 norm_dir_path = NormalizePath(dir_path) | |
| 271 | |
| 272 if not dir_path.startswith( | |
|
erikwright (departed)
2012/07/31 17:22:45
shouldn't this be 'if not norm_dir_path.startswith
Jói
2012/08/01 15:22:58
Quite right, fixed.
| |
| 273 NormalizePath(os.path.normpath(self.base_directory))): | |
| 274 dir_path = os.path.join(self.base_directory, dir_path) | |
| 275 norm_dir_path = NormalizePath(dir_path) | |
| 276 | |
| 277 parent_dir = os.path.dirname(dir_path) | |
| 278 parent_rules = None | |
| 279 if not norm_dir_path in self.directory_rules: | |
| 280 parent_rules = self.GetDirectoryRules(parent_dir) | |
| 281 | |
| 282 # We need to check for an entry for our dir_path again, in case we | |
| 283 # are at a path e.g. A/B/C where A/B/DEPS specifies the C | |
| 284 # subdirectory to be skipped; in this case, the invocation to | |
| 285 # GetDirectoryRules(parent_dir) has already filled in an entry for | |
| 286 # A/B/C. | |
| 287 if not norm_dir_path in self.directory_rules: | |
| 288 if not parent_rules: | |
| 289 # If the parent directory should be skipped, then the current | |
| 290 # directory should also be skipped. | |
| 291 self.directory_rules[norm_dir_path] = None | |
| 292 else: | |
| 293 self._ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path) | |
| 294 return self.directory_rules[norm_dir_path] | |
| 295 | |
| 232 def CheckDirectory(self, start_dir): | 296 def CheckDirectory(self, start_dir): |
| 233 """Checks all relevant source files in the specified directory and | 297 """Checks all relevant source files in the specified directory and |
| 234 its subdirectories for compliance with DEPS rules throughout the | 298 its subdirectories for compliance with DEPS rules throughout the |
| 235 tree (starting at |self.base_directory|). |start_dir| must be a | 299 tree (starting at |self.base_directory|). |start_dir| must be a |
| 236 subdirectory of |self.base_directory|. | 300 subdirectory of |self.base_directory|. |
| 237 | 301 |
| 238 Returns an empty array on success. On failure, the array contains | 302 Returns an empty array on success. On failure, the array contains |
| 239 strings that can be printed as human-readable error messages. | 303 strings that can be printed as human-readable error messages. |
| 240 """ | 304 """ |
| 241 # TODO(joi): Make this work for start_dir != base_dir (I have a | |
| 242 # subsequent change in flight to do this). | |
| 243 base_rules = Rules() | |
| 244 java = java_checker.JavaChecker(self.base_directory, self.verbose) | 305 java = java_checker.JavaChecker(self.base_directory, self.verbose) |
| 245 cpp = cpp_checker.CppChecker(self.verbose) | 306 cpp = cpp_checker.CppChecker(self.verbose) |
| 246 checkers = dict( | 307 checkers = dict( |
| 247 (extension, checker) | 308 (extension, checker) |
| 248 for checker in [java, cpp] for extension in checker.EXTENSIONS) | 309 for checker in [java, cpp] for extension in checker.EXTENSIONS) |
| 249 return self._CheckDirectoryImpl(base_rules, checkers, start_dir) | 310 return self._CheckDirectoryImpl(checkers, start_dir) |
|
erikwright (departed)
2012/07/31 17:22:46
It seems that you need to change this statement to
Jói
2012/08/01 15:22:58
Done.
| |
| 250 | 311 |
| 251 def _CheckDirectoryImpl(self, parent_rules, checkers, dir_name): | 312 def _CheckDirectoryImpl(self, checkers, dir_name): |
| 252 (rules, skip_subdirs) = self._ApplyDirectoryRules(parent_rules, dir_name) | 313 rules = self.GetDirectoryRules(dir_name) |
| 253 if rules == None: | 314 if rules == None: |
| 254 return [] | 315 return [] |
| 255 | 316 |
| 256 # Collect a list of all files and directories to check. | 317 # Collect a list of all files and directories to check. |
| 257 files_to_check = [] | 318 files_to_check = [] |
| 258 dirs_to_check = [] | 319 dirs_to_check = [] |
| 259 results = [] | |
| 260 contents = os.listdir(dir_name) | 320 contents = os.listdir(dir_name) |
| 261 for cur in contents: | 321 for cur in contents: |
| 262 if cur in skip_subdirs: | |
| 263 continue # Don't check children that DEPS has asked us to skip. | |
| 264 full_name = os.path.join(dir_name, cur) | 322 full_name = os.path.join(dir_name, cur) |
| 265 if os.path.isdir(full_name): | 323 if os.path.isdir(full_name): |
| 266 dirs_to_check.append(full_name) | 324 dirs_to_check.append(full_name) |
| 267 elif os.path.splitext(full_name)[1] in checkers: | 325 elif os.path.splitext(full_name)[1] in checkers: |
| 268 files_to_check.append(full_name) | 326 files_to_check.append(full_name) |
| 269 | 327 |
| 270 # First check all files in this directory. | 328 # First check all files in this directory. |
| 271 for cur in files_to_check: | 329 for cur in files_to_check: |
| 272 checker = checkers[os.path.splitext(cur)[1]] | 330 checker = checkers[os.path.splitext(cur)[1]] |
| 273 file_status = checker.CheckFile(rules, cur) | 331 file_status = checker.CheckFile(rules, cur) |
| 274 if file_status: | 332 if file_status.HasViolations(): |
| 275 results.append("ERROR in " + cur + "\n" + file_status) | 333 self.results_formatter.AddError(file_status) |
| 276 | 334 |
| 277 # Next recurse into the subdirectories. | 335 # Next recurse into the subdirectories. |
| 278 for cur in dirs_to_check: | 336 for cur in dirs_to_check: |
| 279 results.extend(self._CheckDirectoryImpl(rules, checkers, cur)) | 337 self._CheckDirectoryImpl(checkers, cur) |
| 280 return results | |
| 281 | 338 |
| 282 def CheckAddedCppIncludes(self, added_includes): | 339 def CheckAddedCppIncludes(self, added_includes): |
| 283 """This is used from PRESUBMIT.py to check new #include statements added in | 340 """This is used from PRESUBMIT.py to check new #include statements added in |
| 284 the change being presubmit checked. | 341 the change being presubmit checked. |
| 285 | 342 |
| 286 Args: | 343 Args: |
| 287 added_includes: ((file_path, (include_line, include_line, ...), ...) | 344 added_includes: ((file_path, (include_line, include_line, ...), ...) |
| 288 | 345 |
| 289 Return: | 346 Return: |
| 290 A list of tuples, (bad_file_path, rule_type, rule_description) | 347 A list of tuples, (bad_file_path, rule_type, rule_description) |
| 291 where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and | 348 where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and |
| 292 rule_description is human-readable. Empty if no problems. | 349 rule_description is human-readable. Empty if no problems. |
| 293 """ | 350 """ |
| 294 # Map of normalized directory paths to rules to use for those | |
| 295 # directories, or None for directories that should be skipped. | |
| 296 directory_rules = {} | |
| 297 | |
| 298 def ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path): | |
| 299 rules_tuple = self._ApplyDirectoryRules(parent_rules, dir_path) | |
| 300 directory_rules[NormalizePath(dir_path)] = rules_tuple[0] | |
| 301 for subdir in rules_tuple[1]: | |
| 302 # We skip this one case for running tests. | |
| 303 directory_rules[NormalizePath( | |
| 304 os.path.normpath(os.path.join(dir_path, subdir)))] = None | |
| 305 | |
| 306 ApplyDirectoryRulesAndSkipSubdirs(Rules(), self.base_directory) | |
| 307 | |
| 308 def GetDirectoryRules(dir_path): | |
| 309 """Returns a Rules object to use for the given directory, or None | |
| 310 if the given directory should be skipped. | |
| 311 """ | |
| 312 norm_dir_path = NormalizePath(dir_path) | |
| 313 | |
| 314 if not dir_path.startswith( | |
| 315 NormalizePath(os.path.normpath(self.base_directory))): | |
| 316 dir_path = os.path.join(self.base_directory, dir_path) | |
| 317 norm_dir_path = NormalizePath(dir_path) | |
| 318 | |
| 319 parent_dir = os.path.dirname(dir_path) | |
| 320 parent_rules = None | |
| 321 if not norm_dir_path in directory_rules: | |
| 322 parent_rules = GetDirectoryRules(parent_dir) | |
| 323 | |
| 324 # We need to check for an entry for our dir_path again, in case we | |
| 325 # are at a path e.g. A/B/C where A/B/DEPS specifies the C | |
| 326 # subdirectory to be skipped; in this case, the invocation to | |
| 327 # GetDirectoryRules(parent_dir) has already filled in an entry for | |
| 328 # A/B/C. | |
| 329 if not norm_dir_path in directory_rules: | |
| 330 if not parent_rules: | |
| 331 # If the parent directory should be skipped, then the current | |
| 332 # directory should also be skipped. | |
| 333 directory_rules[norm_dir_path] = None | |
| 334 else: | |
| 335 ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path) | |
| 336 return directory_rules[norm_dir_path] | |
| 337 | |
| 338 cpp = cpp_checker.CppChecker(self.verbose) | 351 cpp = cpp_checker.CppChecker(self.verbose) |
| 339 | |
| 340 problems = [] | 352 problems = [] |
| 341 for file_path, include_lines in added_includes: | 353 for file_path, include_lines in added_includes: |
| 342 # TODO(joi): Make this cover Java as well. | 354 # TODO(joi): Make this cover Java as well. |
| 343 if not cpp.IsCppFile(file_path): | 355 if not cpp.IsCppFile(file_path): |
| 344 pass | 356 pass |
| 345 rules_for_file = GetDirectoryRules(os.path.dirname(file_path)) | 357 rules_for_file = self.GetDirectoryRules(os.path.dirname(file_path)) |
| 346 if rules_for_file: | 358 if rules_for_file: |
| 347 for line in include_lines: | 359 for line in include_lines: |
| 348 is_include, line_status, rule_type = cpp.CheckLine( | 360 is_include, violation = cpp.CheckLine(rules_for_file, line, True) |
| 349 rules_for_file, line, True) | 361 if violation: |
| 350 if rule_type != Rule.ALLOW: | 362 rule_type = violation.violated_rule.allow |
| 351 problems.append((file_path, rule_type, line_status)) | 363 if rule_type != Rule.ALLOW: |
| 364 formatter = results.NormalResultsFormatter(False) | |
| 365 violation_lines = formatter.FormatViolation(violation) | |
| 366 problems.append((file_path, rule_type, | |
| 367 '\n'.join(violation_lines))) | |
| 352 return problems | 368 return problems |
| 353 | 369 |
| 354 def _AddGitSourceDirectories(self): | 370 def _AddGitSourceDirectories(self): |
| 355 """Adds any directories containing sources managed by git to | 371 """Adds any directories containing sources managed by git to |
| 356 self.git_source_directories. | 372 self.git_source_directories. |
| 357 """ | 373 """ |
| 358 if not os.path.exists(os.path.join(self.base_directory, ".git")): | 374 if not os.path.exists(os.path.join(self.base_directory, '.git')): |
| 359 return | 375 return |
| 360 | 376 |
| 361 popen_out = os.popen("cd %s && git ls-files --full-name ." % | 377 popen_out = os.popen('cd %s && git ls-files --full-name .' % |
| 362 subprocess.list2cmdline([self.base_directory])) | 378 subprocess.list2cmdline([self.base_directory])) |
| 363 for line in popen_out.readlines(): | 379 for line in popen_out.readlines(): |
| 364 dir_name = os.path.join(self.base_directory, os.path.dirname(line)) | 380 dir_name = os.path.join(self.base_directory, os.path.dirname(line)) |
| 365 # Add the directory as well as all the parent directories. Use | 381 # Add the directory as well as all the parent directories. Use |
| 366 # forward slashes and lower case to normalize paths. | 382 # forward slashes and lower case to normalize paths. |
| 367 while dir_name != self.base_directory: | 383 while dir_name != self.base_directory: |
| 368 self.git_source_directories.add(NormalizePath(dir_name)) | 384 self.git_source_directories.add(NormalizePath(dir_name)) |
| 369 dir_name = os.path.dirname(dir_name) | 385 dir_name = os.path.dirname(dir_name) |
| 370 self.git_source_directories.add(NormalizePath(self.base_directory)) | 386 self.git_source_directories.add(NormalizePath(self.base_directory)) |
| 371 | 387 |
| 372 | 388 |
| 373 def PrintUsage(): | 389 def PrintUsage(): |
| 374 print """Usage: python checkdeps.py [--root <root>] [tocheck] | 390 print """Usage: python checkdeps.py [--root <root>] [tocheck] |
| 375 | 391 |
| 376 --root Specifies the repository root. This defaults to "../../.." relative | 392 --root Specifies the repository root. This defaults to "../../.." relative |
| 377 to the script file. This will be correct given the normal location | 393 to the script file. This will be correct given the normal location |
| 378 of the script in "<root>/tools/checkdeps". | 394 of the script in "<root>/tools/checkdeps". |
| 379 | 395 |
| 380 tocheck Specifies the directory, relative to root, to check. This defaults | 396 tocheck Specifies the directory, relative to root, to check. This defaults |
| 381 to "." so it checks everything. Only one level deep is currently | 397 to "." so it checks everything. |
| 382 supported, so you can say "chrome" but not "chrome/browser". | |
| 383 | 398 |
| 384 Examples: | 399 Examples: |
| 385 python checkdeps.py | 400 python checkdeps.py |
| 386 python checkdeps.py --root c:\\source chrome""" | 401 python checkdeps.py --root c:\\source chrome""" |
| 387 | 402 |
| 388 | 403 |
| 389 def main(): | 404 def main(): |
| 390 option_parser = optparse.OptionParser() | 405 option_parser = optparse.OptionParser() |
| 391 option_parser.add_option("", "--root", default="", dest="base_directory", | 406 option_parser.add_option('', '--root', default='', dest='base_directory', |
| 392 help='Specifies the repository root. This defaults ' | 407 help='Specifies the repository root. This defaults ' |
| 393 'to "../../.." relative to the script file, which ' | 408 'to "../../.." relative to the script file, which ' |
| 394 'will normally be the repository root.') | 409 'will normally be the repository root.') |
| 395 option_parser.add_option("-v", "--verbose", action="store_true", | 410 option_parser.add_option('', '--temprules', action='store_true', |
| 396 default=False, help="Print debug logging") | 411 default=False, help='Print rules to temporarily ' |
| 412 'allow files that fail dependency checking.') | |
| 413 option_parser.add_option('-v', '--verbose', action='store_true', | |
| 414 default=False, help='Print debug logging') | |
| 397 options, args = option_parser.parse_args() | 415 options, args = option_parser.parse_args() |
| 398 | 416 |
| 399 deps_checker = DepsChecker(options.base_directory, options.verbose) | 417 deps_checker = DepsChecker(options.base_directory, verbose=options.verbose) |
| 400 | 418 |
| 401 # Figure out which directory we have to check. | 419 # Figure out which directory we have to check. |
| 402 start_dir = deps_checker.base_directory | 420 start_dir = deps_checker.base_directory |
| 403 if len(args) == 1: | 421 if len(args) == 1: |
| 404 # Directory specified. Start here. It's supposed to be relative to the | 422 # Directory specified. Start here. It's supposed to be relative to the |
| 405 # base directory. | 423 # base directory. |
| 406 start_dir = os.path.abspath( | 424 start_dir = os.path.abspath( |
| 407 os.path.join(deps_checker.base_directory, args[0])) | 425 os.path.join(deps_checker.base_directory, args[0])) |
| 408 elif len(args) >= 2: | 426 elif len(args) >= 2: |
| 409 # More than one argument, we don't handle this. | 427 # More than one argument, we don't handle this. |
| 410 PrintUsage() | 428 PrintUsage() |
| 411 return 1 | 429 return 1 |
| 412 | 430 |
| 413 print "Using base directory:", deps_checker.base_directory | 431 print 'Using base directory:', deps_checker.base_directory |
| 414 print "Checking:", start_dir | 432 print 'Checking:', start_dir |
| 415 | 433 |
| 416 results = deps_checker.CheckDirectory(start_dir) | 434 if options.temprules: |
| 417 if results: | 435 deps_checker.results_formatter = results.TemporaryRulesFormatter() |
| 418 for result in results: | 436 deps_checker.CheckDirectory(start_dir) |
| 419 print result | 437 return deps_checker.Report() |
| 420 print "\nFAILED\n" | |
| 421 return 1 | |
| 422 print "\nSUCCESS\n" | |
| 423 return 0 | |
| 424 | 438 |
| 425 | 439 |
| 426 if '__main__' == __name__: | 440 if '__main__' == __name__: |
| 427 sys.exit(main()) | 441 sys.exit(main()) |
| OLD | NEW |