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

Side by Side Diff: tools/checkdeps/checkdeps.py

Issue 10805042: Implement ability to specify temporarily-allowed dependencies in DEPS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 5 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 | « chrome/test/DEPS ('k') | no next file » | 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.
11 11
12 The format of the deps file: 12 The format of the deps file:
13 13
14 First you have the normal module-level deps. These are the ones used by 14 First you have the normal module-level deps. These are the ones used by
15 gclient. An example would be: 15 gclient. An example would be:
16 16
17 deps = { 17 deps = {
18 "base":"http://foo.bar/trunk/base" 18 "base":"http://foo.bar/trunk/base"
19 } 19 }
20 20
21 DEPS files not in the top-level of a module won't need this. Then you have 21 DEPS files not in the top-level of a module won't need this. Then you
22 any additional include rules. You can add (using "+") or subtract (using "-") 22 have any additional include rules. You can add (using "+") or subtract
23 from the previously specified rules (including module-level deps). 23 (using "-") from the previously specified rules (including
24 module-level deps). You can also specify a path that is allowed for
25 now but that we intend to remove, using "!"; this is treated the same
26 as "+" when check_deps is run by our bots, but a presubmit step will
27 show a warning if you add a new include of a file that is only allowed
jam 2012/07/20 20:08:49 it seems that this should be an error for presubmi
Jói 2012/07/20 21:57:39 In some cases, adding no new includes may be diffi
jam 2012/07/20 23:23:34 I don't feel strongly about this either, it's a pe
28 by "!".
24 29
25 include_rules = { 30 include_rules = {
26 # Code should be able to use base (it's specified in the module-level 31 # Code should be able to use base (it's specified in the module-level
27 # deps above), but nothing in "base/evil" because it's evil. 32 # deps above), but nothing in "base/evil" because it's evil.
28 "-base/evil", 33 "-base/evil",
29 34
30 # But this one subdirectory of evil is OK. 35 # But this one subdirectory of evil is OK.
31 "+base/evil/not", 36 "+base/evil/not",
32 37
33 # And it can include files from this other directory even though there is 38 # And it can include files from this other directory even though there is
34 # no deps rule for it. 39 # no deps rule for it.
35 "+tools/crime_fighter" 40 "+tools/crime_fighter",
41
42 # This dependency is allowed for now but work is ongoing to remove it,
43 # so you shouldn't add further dependencies on it.
44 "!base/evil/ok_for_now.h",
36 } 45 }
37 46
38 DEPS files may be placed anywhere in the tree. Each one applies to all 47 DEPS files may be placed anywhere in the tree. Each one applies to all
39 subdirectories, where there may be more DEPS files that provide additions or 48 subdirectories, where there may be more DEPS files that provide additions or
40 subtractions for their own sub-trees. 49 subtractions for their own sub-trees.
41 50
42 There is an implicit rule for the current directory (where the DEPS file lives) 51 There is an implicit rule for the current directory (where the DEPS file lives)
43 and all of its subdirectories. This prevents you from having to explicitly 52 and all of its subdirectories. This prevents you from having to explicitly
44 allow the current directory everywhere. This implicit rule is applied first, 53 allow the current directory everywhere. This implicit rule is applied first,
45 so you can modify or remove it using the normal include rules. 54 so you can modify or remove it using the normal include rules.
46 55
47 The rules are processed in order. This means you can explicitly allow a higher 56 The rules are processed in order. This means you can explicitly allow a higher
48 directory and then take away permissions from sub-parts, or the reverse. 57 directory and then take away permissions from sub-parts, or the reverse.
49 58
50 Note that all directory separators must be slashes (Unix-style) and not 59 Note that all directory separators must be slashes (Unix-style) and not
51 backslashes. All directories should be relative to the source root and use 60 backslashes. All directories should be relative to the source root and use
52 only lowercase. 61 only lowercase.
53 """ 62 """
54 63
55 import os 64 import os
56 import optparse 65 import optparse
57 import pipes
58 import re 66 import re
67 import subprocess
59 import sys 68 import sys
60 import copy 69 import copy
61 70
62 # Variable name used in the DEPS file to add or subtract include files from 71 # Variable name used in the DEPS file to add or subtract include files from
63 # the module-level deps. 72 # the module-level deps.
64 INCLUDE_RULES_VAR_NAME = "include_rules" 73 INCLUDE_RULES_VAR_NAME = "include_rules"
65 74
66 # Optionally present in the DEPS file to list subdirectories which should not 75 # Optionally present in the DEPS file to list subdirectories which should not
67 # be checked. This allows us to skip third party code, for example. 76 # be checked. This allows us to skip third party code, for example.
68 SKIP_SUBDIRS_VAR_NAME = "skip_child_includes" 77 SKIP_SUBDIRS_VAR_NAME = "skip_child_includes"
69 78
70 # The maximum number of non-include lines we can see before giving up. 79 # The maximum number of non-include lines we can see before giving up.
71 MAX_UNINTERESTING_LINES = 50 80 MAX_UNINTERESTING_LINES = 50
72 81
73 # The maximum line length, this is to be efficient in the case of very long 82 # The maximum line length, this is to be efficient in the case of very long
74 # lines (which can't be #includes). 83 # lines (which can't be #includes).
75 MAX_LINE_LENGTH = 128 84 MAX_LINE_LENGTH = 128
76 85
77 # Set to true for more output. This is set by the command line options. 86 # Set to true for more output. This is set by the command line options.
78 VERBOSE = False 87 VERBOSE = False
79 88
80 # This regular expression will be used to extract filenames from include 89 # This regular expression will be used to extract filenames from include
81 # statements. 90 # statements.
82 EXTRACT_INCLUDE_PATH = re.compile('[ \t]*#[ \t]*(?:include|import)[ \t]+"(.*)"') 91 EXTRACT_INCLUDE_PATH = re.compile('[ \t]*#[ \t]*(?:include|import)[ \t]+"(.*)"')
83 92
84 # In lowercase, using forward slashes as directory separators, ending in a 93 # OS-compatible path for base directory (e.g. C:\chrome\src). Set
85 # forward slash. Set by the command line options. 94 # implicitly or by command-line option.
86 BASE_DIRECTORY = "" 95 BASE_DIRECTORY = ""
87 96
88 # The directories which contain the sources managed by git. 97 # The directories which contain the sources managed by git.
89 GIT_SOURCE_DIRECTORY = set() 98 GIT_SOURCE_DIRECTORY = set()
90 99
91 100
92 # Specifies a single rule for an include, which can be either allow or disallow. 101 # Specifies a single rule for an include, which can be either allow or disallow.
93 class Rule(object): 102 class Rule(object):
103
104 # These are the prefixes used to indicate each type of rule. These
105 # are also used as values for self._allow to indicate which type of
106 # rule this is.
107 ALLOW = "+"
108 DISALLOW = "-"
109 TEMP_ALLOW = "!"
110
94 def __init__(self, allow, dir, source): 111 def __init__(self, allow, dir, source):
95 self._allow = allow 112 self._allow = allow
96 self._dir = dir 113 self._dir = dir
97 self._source = source 114 self._source = source
98 115
99 def __str__(self): 116 def __str__(self):
100 if (self._allow): 117 return '"%s%s" from %s.' % (self._allow, self._dir, self._source)
101 return '"+%s" from %s.' % (self._dir, self._source)
102 return '"-%s" from %s.' % (self._dir, self._source)
103 118
104 def ParentOrMatch(self, other): 119 def ParentOrMatch(self, other):
105 """Returns true if the input string is an exact match or is a parent 120 """Returns true if the input string is an exact match or is a parent
106 of the current rule. For example, the input "foo" would match "foo/bar".""" 121 of the current rule. For example, the input "foo" would match "foo/bar"."""
107 return self._dir == other or self._dir.startswith(other + "/") 122 return self._dir == other or self._dir.startswith(other + "/")
108 123
109 def ChildOrMatch(self, other): 124 def ChildOrMatch(self, other):
110 """Returns true if the input string would be covered by this rule. For 125 """Returns true if the input string would be covered by this rule. For
111 example, the input "foo/bar" would match the rule "foo".""" 126 example, the input "foo/bar" would match the rule "foo"."""
112 return self._dir == other or other.startswith(self._dir + "/") 127 return self._dir == other or other.startswith(self._dir + "/")
113 128
114 129
115 def ParseRuleString(rule_string, source): 130 def ParseRuleString(rule_string, source):
116 """Returns a tuple of a boolean indicating whether the directory is an allow 131 """Returns a tuple of a boolean indicating whether the directory is an allow
117 rule, and a string holding the directory name. 132 rule, and a string holding the directory name.
118 """ 133 """
119 if len(rule_string) < 1: 134 if len(rule_string) < 1:
120 raise Exception('The rule string "%s" is too short\nin %s' % 135 raise Exception('The rule string "%s" is too short\nin %s' %
121 (rule_string, source)) 136 (rule_string, source))
122 137
123 if rule_string[0] == "+": 138 if not rule_string[0] in [Rule.ALLOW, Rule.DISALLOW, Rule.TEMP_ALLOW]:
124 return (True, rule_string[1:]) 139 raise Exception(
125 if rule_string[0] == "-": 140 'The rule string "%s" does not begin with a "+", "-" or "!".' %
126 return (False, rule_string[1:]) 141 rule_string)
127 raise Exception('The rule string "%s" does not begin with a "+" or a "-"' % 142
128 rule_string) 143 return (rule_string[0], rule_string[1:])
129 144
130 145
131 class Rules: 146 class Rules:
132 def __init__(self): 147 def __init__(self):
133 """Initializes the current rules with an empty rule list.""" 148 """Initializes the current rules with an empty rule list."""
134 self._rules = [] 149 self._rules = []
135 150
136 def __str__(self): 151 def __str__(self):
137 ret = "Rules = [\n" 152 ret = "Rules = [\n"
138 ret += "\n".join([" %s" % x for x in self._rules]) 153 ret += "\n".join([" %s" % x for x in self._rules])
(...skipping 14 matching lines...) Expand all
153 self._rules = [x for x in self._rules if not x.ParentOrMatch(rule_dir)] 168 self._rules = [x for x in self._rules if not x.ParentOrMatch(rule_dir)]
154 self._rules.insert(0, Rule(add_rule, rule_dir, source)) 169 self._rules.insert(0, Rule(add_rule, rule_dir, source))
155 170
156 def DirAllowed(self, allowed_dir): 171 def DirAllowed(self, allowed_dir):
157 """Returns a tuple (success, message), where success indicates if the given 172 """Returns a tuple (success, message), where success indicates if the given
158 directory is allowed given the current set of rules, and the message tells 173 directory is allowed given the current set of rules, and the message tells
159 why if the comparison failed.""" 174 why if the comparison failed."""
160 for rule in self._rules: 175 for rule in self._rules:
161 if rule.ChildOrMatch(allowed_dir): 176 if rule.ChildOrMatch(allowed_dir):
162 # This rule applies. 177 # This rule applies.
163 if rule._allow: 178 why_failed = ""
164 return (True, "") 179 if rule._allow != Rule.ALLOW:
165 return (False, rule.__str__()) 180 why_failed = rule.__str__()
181 return (rule._allow, why_failed)
166 # No rules apply, fail. 182 # No rules apply, fail.
167 return (False, "no rule applying") 183 return (Rule.DISALLOW, "no rule applying")
168 184
169 185
170 def ApplyRules(existing_rules, includes, cur_dir): 186 def ApplyRules(existing_rules, includes, cur_dir):
171 """Applies the given include rules, returning the new rules. 187 """Applies the given include rules, returning the new rules.
172 188
173 Args: 189 Args:
174 existing_rules: A set of existing rules that will be combined. 190 existing_rules: A set of existing rules that will be combined.
175 include: The list of rules from the "include_rules" section of DEPS. 191 include: The list of rules from the "include_rules" section of DEPS.
176 cur_dir: The current directory. We will create an implicit rule that 192 cur_dir: The current directory. We will create an implicit rule that
177 allows inclusion from this directory. 193 allows inclusion from this directory.
178 194
179 Returns: A new set of rules combining the existing_rules with the other 195 Returns: A new set of rules combining the existing_rules with the other
180 arguments. 196 arguments.
181 """ 197 """
182 rules = copy.copy(existing_rules) 198 rules = copy.copy(existing_rules)
183 199
184 # First apply the implicit "allow" rule for the current directory. 200 # First apply the implicit "allow" rule for the current directory.
185 if cur_dir.lower().startswith(BASE_DIRECTORY): 201 if os.path.normcase(os.path.normpath(cur_dir)).startswith(
202 os.path.normcase(os.path.normpath(BASE_DIRECTORY))):
186 relative_dir = cur_dir[len(BASE_DIRECTORY) + 1:] 203 relative_dir = cur_dir[len(BASE_DIRECTORY) + 1:]
187 # Normalize path separators to slashes.
188 relative_dir = relative_dir.replace("\\", "/")
189 source = relative_dir 204 source = relative_dir
190 if len(source) == 0: 205 if len(source) == 0:
191 source = "top level" # Make the help string a little more meaningful. 206 source = "top level" # Make the help string a little more meaningful.
192 rules.AddRule("+" + relative_dir, "Default rule for " + source) 207 rules.AddRule("+" + relative_dir, "Default rule for " + source)
193 else: 208 else:
194 raise Exception("Internal error: base directory is not at the beginning" + 209 raise Exception("Internal error: base directory is not at the beginning" +
195 " for\n %s and base dir\n %s" % 210 " for\n %s and base dir\n %s" %
196 (cur_dir, BASE_DIRECTORY)) 211 (cur_dir, BASE_DIRECTORY))
197 212
198 # Last, apply the additional explicit rules. 213 # Last, apply the additional explicit rules.
(...skipping 21 matching lines...) Expand all
220 This will also be used to generate the implicit rules. 235 This will also be used to generate the implicit rules.
221 236
222 Returns: A tuple containing: (1) the combined set of rules to apply to the 237 Returns: A tuple containing: (1) the combined set of rules to apply to the
223 sub-tree, and (2) a list of all subdirectories that should NOT be 238 sub-tree, and (2) a list of all subdirectories that should NOT be
224 checked, as specified in the DEPS file (if any). 239 checked, as specified in the DEPS file (if any).
225 """ 240 """
226 # Check for a .svn directory in this directory or check this directory is 241 # Check for a .svn directory in this directory or check this directory is
227 # contained in git source direcotries. This will tell us if it's a source 242 # contained in git source direcotries. This will tell us if it's a source
228 # directory and should be checked. 243 # directory and should be checked.
229 if not (os.path.exists(os.path.join(dir_name, ".svn")) or 244 if not (os.path.exists(os.path.join(dir_name, ".svn")) or
230 (dir_name.lower() in GIT_SOURCE_DIRECTORY)): 245 (dir_name in GIT_SOURCE_DIRECTORY)):
M-A Ruel 2012/07/20 19:57:01 are you sure?
Jói 2012/07/20 20:01:41 Yes, or Linux (and Mac, I guess) is broken for che
231 return (None, []) 246 return (None, [])
232 247
233 # Check the DEPS file in this directory. 248 # Check the DEPS file in this directory.
234 if VERBOSE: 249 if VERBOSE:
235 print "Applying rules from", dir_name 250 print "Applying rules from", dir_name
236 def FromImpl(unused, unused2): 251 def FromImpl(unused, unused2):
237 pass # NOP function so "From" doesn't fail. 252 pass # NOP function so "From" doesn't fail.
238 253
239 def FileImpl(unused): 254 def FileImpl(unused):
240 pass # NOP function so "File" doesn't fail. 255 pass # NOP function so "File" doesn't fail.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 checked_extensions = [ 290 checked_extensions = [
276 '.h', 291 '.h',
277 '.cc', 292 '.cc',
278 '.m', 293 '.m',
279 '.mm', 294 '.mm',
280 ] 295 ]
281 basename, extension = os.path.splitext(file_name) 296 basename, extension = os.path.splitext(file_name)
282 return extension in checked_extensions 297 return extension in checked_extensions
283 298
284 299
285 def CheckLine(rules, line): 300 def CheckLine(rules, line, fail_on_temp_allow=False):
286 """Checks the given file with the given rule set. 301 """Checks the given line with the given rule set.
287 Returns a tuple (is_include, illegal_description). 302 Returns a triplet (is_include, illegal_description, rule_type).
288 If the line is an #include directive the first value will be True. 303 If the line is an #include directive the first value will be True.
289 If it is also an illegal include, the second value will be a string describing 304 If it is also an illegal include, the second value will be a string describing
290 the error. Otherwise, it will be None.""" 305 the error. Otherwise, it will be None.
306 """
291 found_item = EXTRACT_INCLUDE_PATH.match(line) 307 found_item = EXTRACT_INCLUDE_PATH.match(line)
292 if not found_item: 308 if not found_item:
293 return False, None # Not a match 309 return False, None, Rule.ALLOW # Not a match
294 310
295 include_path = found_item.group(1) 311 include_path = found_item.group(1)
296 312
297 # Fix up backslashes in case somebody accidentally used them. 313 # Fix up backslashes in case somebody accidentally used them.
298 include_path.replace("\\", "/") 314 include_path.replace("\\", "/")
299 315
300 if include_path.find("/") < 0: 316 if include_path.find("/") < 0:
301 # Don't fail when no directory is specified. We may want to be more 317 # Don't fail when no directory is specified. We may want to be more
302 # strict about this in the future. 318 # strict about this in the future.
303 if VERBOSE: 319 if VERBOSE:
304 print " WARNING: directory specified with no path: " + include_path 320 print " WARNING: directory specified with no path: " + include_path
305 return True, None 321 return True, None, Rule.ALLOW
306 322
307 (allowed, why_failed) = rules.DirAllowed(include_path) 323 (allowed, why_failed) = rules.DirAllowed(include_path)
308 if not allowed: 324 if (allowed == Rule.DISALLOW or
325 (fail_on_temp_allow and allowed == Rule.TEMP_ALLOW)):
309 if VERBOSE: 326 if VERBOSE:
310 retval = "\nFor " + rules.__str__() 327 retval = "\nFor " + rules.__str__()
311 else: 328 else:
312 retval = "" 329 retval = ""
313 return True, retval + ('Illegal include: "%s"\n Because of %s' % 330 return True, retval + ('Illegal include: "%s"\n Because of %s' %
314 (include_path, why_failed)) 331 (include_path, why_failed)), allowed
315 332
316 return True, None 333 return True, None, Rule.ALLOW
317 334
318 335
319 def CheckFile(rules, file_name): 336 def CheckFile(rules, file_name):
320 """Checks the given file with the given rule set. 337 """Checks the given file with the given rule set.
321 338
322 Args: 339 Args:
323 rules: The set of rules that apply to files in this directory. 340 rules: The set of rules that apply to files in this directory.
324 file_name: The source file to check. 341 file_name: The source file to check.
325 342
326 Returns: Either a string describing the error if there was one, or None if 343 Returns: Either a string describing the error if there was one, or None if
(...skipping 20 matching lines...) Expand all
347 if cur_line == '#if 0': 364 if cur_line == '#if 0':
348 in_if0 += 1 365 in_if0 += 1
349 continue 366 continue
350 if in_if0 > 0: 367 if in_if0 > 0:
351 if cur_line.startswith('#if'): 368 if cur_line.startswith('#if'):
352 in_if0 += 1 369 in_if0 += 1
353 elif cur_line == '#endif': 370 elif cur_line == '#endif':
354 in_if0 -= 1 371 in_if0 -= 1
355 continue 372 continue
356 373
357 is_include, line_status = CheckLine(rules, cur_line) 374 is_include, line_status, rule_type = CheckLine(rules, cur_line)
358 if is_include: 375 if is_include:
359 last_include = line_num 376 last_include = line_num
360 if line_status is not None: 377 if line_status is not None:
361 if len(line_status) > 0: # Add newline to separate messages. 378 if len(line_status) > 0: # Add newline to separate messages.
362 line_status += "\n" 379 line_status += "\n"
363 ret_val += line_status 380 ret_val += line_status
364 cur_file.close() 381 cur_file.close()
365 382
366 except IOError: 383 except IOError:
367 if VERBOSE: 384 if VERBOSE:
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 success = False 418 success = False
402 419
403 # Next recurse into the subdirectories. 420 # Next recurse into the subdirectories.
404 for cur in dirs_to_check: 421 for cur in dirs_to_check:
405 if not CheckDirectory(rules, cur): 422 if not CheckDirectory(rules, cur):
406 success = False 423 success = False
407 424
408 return success 425 return success
409 426
410 427
428 def CheckAddedIncludes(added_includes):
429 """This is used from PRESUBMIT.py to check new #include statements added in
430 the change being presubmit checked.
431
432 Args:
433 added_includes: ((file_path, (include_line, include_line, ...), ...)
434
435 Return:
436 A list of tuples, (bad_file_path, rule_type, rule_description)
437 where rule_type is one of Rule.DISALLOW or Rule.TEMP_ALLOW and
438 rule_description is human-readable. Empty if no problems.
439 """
440 CommonSetup()
441
442 # Map of directory paths to rules to use for those directories, or
443 # None for directories that should be skipped.
444 directory_rules = {}
445
446 def ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path):
447 rules_tuple = ApplyDirectoryRules(parent_rules, dir_path)
448 directory_rules[dir_path] = rules_tuple[0]
449 for subdir in rules_tuple[1]:
450 directory_rules[os.path.join(dir_path, subdir)] = None
451
452 ApplyDirectoryRulesAndSkipSubdirs(Rules(), BASE_DIRECTORY)
453
454 def GetDirectoryRules(dir_path):
455 """Returns a Rules object to use for the given directory, or None
456 if the given directory should be skipped.
457 """
458 if not dir_path.startswith(BASE_DIRECTORY):
459 dir_path = os.path.join(BASE_DIRECTORY, dir_path)
460
461 parent_dir = os.path.dirname(dir_path)
462 parent_rules = None
463 if not dir_path in directory_rules:
464 parent_rules = GetDirectoryRules(parent_dir)
465
466 # We need to check for an entry for our dir_path again, in case we
467 # are at a path e.g. A/B/C where A/B/DEPS specifies the C
468 # subdirectory to be skipped; in this case, the invocation to
469 # GetDirectoryRules(parent_dir) has already filled in an entry for
470 # A/B/C.
471 if not dir_path in directory_rules:
472 if not parent_rules:
473 # If the parent directory should be skipped, then the current
474 # directory should also be skipped.
475 directory_rules[dir_path] = None
476 else:
477 ApplyDirectoryRulesAndSkipSubdirs(parent_rules, dir_path)
478 return directory_rules[dir_path]
479
480 problems = []
481 for file_path, include_lines in added_includes:
482 if not ShouldCheckFile(file_path):
483 pass
484 rules_for_file = GetDirectoryRules(os.path.dirname(file_path))
485 if rules_for_file:
486 for line in include_lines:
487 is_include, line_status, rule_type = CheckLine(
488 rules_for_file, line, True)
489 if rule_type != Rule.ALLOW:
490 problems.append((file_path, rule_type, line_status))
491 return problems
492
493
494 def TestCheckAddedIncludes():
495 """Run this by giving the argument 'manualtest' to checkdeps."""
496 # Note that these tests depend on the rules currently in DEPS files
497 # checked into the tree, which may change. Therefore the test is
498 # only run manually and may need to be updated from time to time.
499 problems = CheckAddedIncludes(
500 # The first of these should fail as a DISALLOW, the second should
501 # fail as a TEMP_ALLOW, because of rules in chrome/browser/DEPS.
502 [['chrome/browser/bingo.cc',
503 ['#include "chrome/browser/ui/views/boondog.h"',
504 '#include "chrome/browser/ui/views/ash/panel_view_aura.h"']],
505 # This should fail because of no rule applying.
506 ['chrome/browser/lifetime/bongo.cc', ['#include "fantastic/food.h"']],
507 # The following two shouldn't fail as they are under a directory
508 # that is skipped for checking, in src/DEPS.
509 ['breakpad/fooblat.cc', ['#include "fantastic/food.h"']],
510 ['breakpad/bingo/bongo/fooblat.cc', ['#include "fantastic/food.h"']],
511 ])
512
513 for problem in problems:
514 print problem[0]
515 print problem[1]
516 print problem[2]
517 print
518 print
519
520
411 def GetGitSourceDirectory(root): 521 def GetGitSourceDirectory(root):
412 """Returns a set of the directories to be checked. 522 """Returns a set of the directories to be checked.
413 523
414 Args: 524 Args:
415 root: The repository root where .git directory exists. 525 root: The repository root where .git directory exists.
416 526
417 Returns: 527 Returns:
418 A set of directories which contain sources managed by git. 528 A set of directories which contain sources managed by git.
419 """ 529 """
420 git_source_directory = set() 530 git_source_directory = set()
421 popen_out = os.popen("cd %s && git ls-files --full-name ." % 531 popen_out = os.popen("cd %s && git ls-files --full-name ." %
422 pipes.quote(root)) 532 subprocess.list2cmdline([root]))
423 for line in popen_out.readlines(): 533 for line in popen_out.readlines():
424 dir_name = os.path.join(root, os.path.dirname(line)) 534 dir_name = os.path.join(root, os.path.dirname(line))
425 # Add the directory as well as all the parent directories. 535 # Add the directory as well as all the parent directories.
426 while dir_name != root: 536 while dir_name != root:
427 git_source_directory.add(dir_name) 537 git_source_directory.add(dir_name)
428 dir_name = os.path.dirname(dir_name) 538 dir_name = os.path.dirname(dir_name)
429 git_source_directory.add(root) 539 git_source_directory.add(root)
430 return git_source_directory 540 return git_source_directory
431 541
432 542
543 def CommonSetup(base_dir=None):
544 """Setup that is in common between the main() entrypoint to this
545 script and the CheckAddedIncludes (presubmit check) entrypoint.
546 """
547 global BASE_DIRECTORY
548 global GIT_SOURCE_DIRECTORY
549
550 if base_dir:
551 BASE_DIRECTORY = base_dir
552 else:
553 BASE_DIRECTORY = os.path.abspath(
554 os.path.join(os.path.abspath(os.path.dirname(__file__)), "../.."))
555
556 if os.path.exists(os.path.join(BASE_DIRECTORY, ".git")):
557 GIT_SOURCE_DIRECTORY = GetGitSourceDirectory(BASE_DIRECTORY)
558
559
433 def PrintUsage(): 560 def PrintUsage():
434 print """Usage: python checkdeps.py [--root <root>] [tocheck] 561 print """Usage: python checkdeps.py [--root <root>] [tocheck]
435 --root Specifies the repository root. This defaults to "../../.." relative 562 --root Specifies the repository root. This defaults to "../../.." relative
436 to the script file. This will be correct given the normal location 563 to the script file. This will be correct given the normal location
437 of the script in "<root>/tools/checkdeps". 564 of the script in "<root>/tools/checkdeps".
438 565
439 tocheck Specifies the directory, relative to root, to check. This defaults 566 tocheck Specifies the directory, relative to root, to check. This defaults
440 to "." so it checks everything. Only one level deep is currently 567 to "." so it checks everything. Only one level deep is currently
441 supported, so you can say "chrome" but not "chrome/browser". 568 supported, so you can say "chrome" but not "chrome/browser".
442 569
443 Examples: 570 Examples:
444 python checkdeps.py 571 python checkdeps.py
445 python checkdeps.py --root c:\\source chrome""" 572 python checkdeps.py --root c:\\source chrome"""
446 573
447 574
448 def checkdeps(options, args): 575 def checkdeps(options, args):
576 global BASE_DIRECTORY
449 global VERBOSE 577 global VERBOSE
450 if options.verbose: 578 if options.verbose:
451 VERBOSE = True 579 VERBOSE = True
452 580
453 # Optional base directory of the repository. 581 # Optional base directory of the repository.
454 global BASE_DIRECTORY
455 if not options.base_directory: 582 if not options.base_directory:
456 BASE_DIRECTORY = os.path.abspath( 583 CommonSetup()
457 os.path.join(os.path.abspath(os.path.dirname(__file__)), "../.."))
458 else: 584 else:
459 BASE_DIRECTORY = os.path.abspath(options.base_directory) 585 CommonSetup(os.path.abspath(options.base_directory))
460 586
461 # Figure out which directory we have to check. 587 # Figure out which directory we have to check.
462 if len(args) == 0: 588 if len(args) == 0:
463 # No directory to check specified, use the repository root. 589 # No directory to check specified, use the repository root.
464 start_dir = BASE_DIRECTORY 590 start_dir = BASE_DIRECTORY
465 elif len(args) == 1: 591 elif len(args) == 1:
466 # Directory specified. Start here. It's supposed to be relative to the 592 # Directory specified. Start here. It's supposed to be relative to the
467 # base directory. 593 # base directory.
468 start_dir = os.path.abspath(os.path.join(BASE_DIRECTORY, args[0])) 594 start_dir = os.path.abspath(os.path.join(BASE_DIRECTORY, args[0]))
469 else: 595 else:
470 # More than one argument, we don't handle this. 596 # More than one argument, we don't handle this.
471 PrintUsage() 597 PrintUsage()
472 return 1 598 return 1
473 599
474 print "Using base directory:", BASE_DIRECTORY 600 print "Using base directory:", BASE_DIRECTORY
475 print "Checking:", start_dir 601 print "Checking:", start_dir
476 602
477 base_rules = Rules() 603 base_rules = Rules()
478
479 # The base directory should be lower case from here on since it will be used
480 # for substring matching on the includes, and we compile on case-insensitive
481 # systems. Plus, we always use slashes here since the include parsing code
482 # will also normalize to slashes.
483 BASE_DIRECTORY = BASE_DIRECTORY.lower()
484 BASE_DIRECTORY = BASE_DIRECTORY.replace("\\", "/")
485 start_dir = start_dir.replace("\\", "/")
486
487 if os.path.exists(os.path.join(BASE_DIRECTORY, ".git")):
488 global GIT_SOURCE_DIRECTORY
489 GIT_SOURCE_DIRECTORY = GetGitSourceDirectory(BASE_DIRECTORY)
490
491 success = CheckDirectory(base_rules, start_dir) 604 success = CheckDirectory(base_rules, start_dir)
492 if not success: 605 if not success:
493 print "\nFAILED\n" 606 print "\nFAILED\n"
494 return 1 607 return 1
495 print "\nSUCCESS\n" 608 print "\nSUCCESS\n"
496 return 0 609 return 0
497 610
498 611
499 def main(): 612 def main():
500 option_parser = optparse.OptionParser() 613 option_parser = optparse.OptionParser()
501 option_parser.add_option("", "--root", default="", dest="base_directory", 614 option_parser.add_option("", "--root", default="", dest="base_directory",
502 help='Specifies the repository root. This defaults ' 615 help='Specifies the repository root. This defaults '
503 'to "../../.." relative to the script file, which ' 616 'to "../../.." relative to the script file, which '
504 'will normally be the repository root.') 617 'will normally be the repository root.')
505 option_parser.add_option("-v", "--verbose", action="store_true", 618 option_parser.add_option("-v", "--verbose", action="store_true",
506 default=False, help="Print debug logging") 619 default=False, help="Print debug logging")
507 options, args = option_parser.parse_args() 620 options, args = option_parser.parse_args()
508 return checkdeps(options, args) 621 if args and args[0] == "manualtest":
622 return TestCheckAddedIncludes()
623 else:
624 return checkdeps(options, args)
509 625
510 626
511 if '__main__' == __name__: 627 if '__main__' == __name__:
512 sys.exit(main()) 628 sys.exit(main())
OLDNEW
« no previous file with comments | « chrome/test/DEPS ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698