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 from results import NormalResultsFormatter, TemporaryRulesFormatter |
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 = 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 |dir_path|'s rules to |
| 250 |self.directory_rules|, and add None entries for any of its |
| 251 subdirectories that should be skipped. |
| 252 """ |
| 253 directory_rules, excluded_subdirs = self._ApplyDirectoryRules(parent_rules, |
| 254 dir_path) |
| 255 self.directory_rules[NormalizePath(dir_path)] = directory_rules |
| 256 for subdir in excluded_subdirs: |
| 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 norm_dir_path.startswith( |
| 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 On completion, self.results_formatter has the results of |
239 strings that can be printed as human-readable error messages. | 303 processing, and calling Report() will print a report of results. |
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 self._CheckDirectoryImpl(checkers, start_dir) |
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 violation_text = NormalResultsFormatter.FormatViolation( |
| 365 violation, self.verbose) |
| 366 problems.append((file_path, rule_type, violation_text)) |
352 return problems | 367 return problems |
353 | 368 |
354 def _AddGitSourceDirectories(self): | 369 def _AddGitSourceDirectories(self): |
355 """Adds any directories containing sources managed by git to | 370 """Adds any directories containing sources managed by git to |
356 self.git_source_directories. | 371 self.git_source_directories. |
357 """ | 372 """ |
358 if not os.path.exists(os.path.join(self.base_directory, ".git")): | 373 if not os.path.exists(os.path.join(self.base_directory, '.git')): |
359 return | 374 return |
360 | 375 |
361 popen_out = os.popen("cd %s && git ls-files --full-name ." % | 376 popen_out = os.popen('cd %s && git ls-files --full-name .' % |
362 subprocess.list2cmdline([self.base_directory])) | 377 subprocess.list2cmdline([self.base_directory])) |
363 for line in popen_out.readlines(): | 378 for line in popen_out.readlines(): |
364 dir_name = os.path.join(self.base_directory, os.path.dirname(line)) | 379 dir_name = os.path.join(self.base_directory, os.path.dirname(line)) |
365 # Add the directory as well as all the parent directories. Use | 380 # Add the directory as well as all the parent directories. Use |
366 # forward slashes and lower case to normalize paths. | 381 # forward slashes and lower case to normalize paths. |
367 while dir_name != self.base_directory: | 382 while dir_name != self.base_directory: |
368 self.git_source_directories.add(NormalizePath(dir_name)) | 383 self.git_source_directories.add(NormalizePath(dir_name)) |
369 dir_name = os.path.dirname(dir_name) | 384 dir_name = os.path.dirname(dir_name) |
370 self.git_source_directories.add(NormalizePath(self.base_directory)) | 385 self.git_source_directories.add(NormalizePath(self.base_directory)) |
371 | 386 |
372 | 387 |
373 def PrintUsage(): | 388 def PrintUsage(): |
374 print """Usage: python checkdeps.py [--root <root>] [tocheck] | 389 print """Usage: python checkdeps.py [--root <root>] [tocheck] |
375 | 390 |
376 --root Specifies the repository root. This defaults to "../../.." relative | 391 --root Specifies the repository root. This defaults to "../../.." relative |
377 to the script file. This will be correct given the normal location | 392 to the script file. This will be correct given the normal location |
378 of the script in "<root>/tools/checkdeps". | 393 of the script in "<root>/tools/checkdeps". |
379 | 394 |
380 tocheck Specifies the directory, relative to root, to check. This defaults | 395 tocheck Specifies the directory, relative to root, to check. This defaults |
381 to "." so it checks everything. Only one level deep is currently | 396 to "." so it checks everything. |
382 supported, so you can say "chrome" but not "chrome/browser". | |
383 | 397 |
384 Examples: | 398 Examples: |
385 python checkdeps.py | 399 python checkdeps.py |
386 python checkdeps.py --root c:\\source chrome""" | 400 python checkdeps.py --root c:\\source chrome""" |
387 | 401 |
388 | 402 |
389 def main(): | 403 def main(): |
390 option_parser = optparse.OptionParser() | 404 option_parser = optparse.OptionParser() |
391 option_parser.add_option("", "--root", default="", dest="base_directory", | 405 option_parser.add_option('', '--root', default='', dest='base_directory', |
392 help='Specifies the repository root. This defaults ' | 406 help='Specifies the repository root. This defaults ' |
393 'to "../../.." relative to the script file, which ' | 407 'to "../../.." relative to the script file, which ' |
394 'will normally be the repository root.') | 408 'will normally be the repository root.') |
395 option_parser.add_option("-v", "--verbose", action="store_true", | 409 option_parser.add_option('', '--temprules', action='store_true', |
396 default=False, help="Print debug logging") | 410 default=False, help='Print rules to temporarily ' |
| 411 'allow files that fail dependency checking.') |
| 412 option_parser.add_option('-v', '--verbose', action='store_true', |
| 413 default=False, help='Print debug logging') |
397 options, args = option_parser.parse_args() | 414 options, args = option_parser.parse_args() |
398 | 415 |
399 deps_checker = DepsChecker(options.base_directory, options.verbose) | 416 deps_checker = DepsChecker(options.base_directory, verbose=options.verbose) |
400 | 417 |
401 # Figure out which directory we have to check. | 418 # Figure out which directory we have to check. |
402 start_dir = deps_checker.base_directory | 419 start_dir = deps_checker.base_directory |
403 if len(args) == 1: | 420 if len(args) == 1: |
404 # Directory specified. Start here. It's supposed to be relative to the | 421 # Directory specified. Start here. It's supposed to be relative to the |
405 # base directory. | 422 # base directory. |
406 start_dir = os.path.abspath( | 423 start_dir = os.path.abspath( |
407 os.path.join(deps_checker.base_directory, args[0])) | 424 os.path.join(deps_checker.base_directory, args[0])) |
408 elif len(args) >= 2: | 425 elif len(args) >= 2: |
409 # More than one argument, we don't handle this. | 426 # More than one argument, we don't handle this. |
410 PrintUsage() | 427 PrintUsage() |
411 return 1 | 428 return 1 |
412 | 429 |
413 print "Using base directory:", deps_checker.base_directory | 430 print 'Using base directory:', deps_checker.base_directory |
414 print "Checking:", start_dir | 431 print 'Checking:', start_dir |
415 | 432 |
416 results = deps_checker.CheckDirectory(start_dir) | 433 if options.temprules: |
417 if results: | 434 deps_checker.results_formatter = TemporaryRulesFormatter() |
418 for result in results: | 435 deps_checker.CheckDirectory(start_dir) |
419 print result | 436 return deps_checker.Report() |
420 print "\nFAILED\n" | |
421 return 1 | |
422 print "\nSUCCESS\n" | |
423 return 0 | |
424 | 437 |
425 | 438 |
426 if '__main__' == __name__: | 439 if '__main__' == __name__: |
427 sys.exit(main()) | 440 sys.exit(main()) |
OLD | NEW |