OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 import copy | 60 import copy |
61 | 61 |
62 # Variable name used in the DEPS file to add or subtract include files from | 62 # Variable name used in the DEPS file to add or subtract include files from |
63 # the module-level deps. | 63 # the module-level deps. |
64 INCLUDE_RULES_VAR_NAME = "include_rules" | 64 INCLUDE_RULES_VAR_NAME = "include_rules" |
65 | 65 |
66 # Optionally present in the DEPS file to list subdirectories which should not | 66 # 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. | 67 # be checked. This allows us to skip third party code, for example. |
68 SKIP_SUBDIRS_VAR_NAME = "skip_child_includes" | 68 SKIP_SUBDIRS_VAR_NAME = "skip_child_includes" |
69 | 69 |
70 # The maximum number of lines to check in each source file before giving up. | 70 # The maximum number of non-include lines we can see before giving up. |
71 MAX_LINES = 150 | 71 MAX_UNINTERESTING_LINES = 50 |
72 | 72 |
73 # The maximum line length, this is to be efficient in the case of very long | 73 # The maximum line length, this is to be efficient in the case of very long |
74 # lines (which can't be #includes). | 74 # lines (which can't be #includes). |
75 MAX_LINE_LENGTH = 128 | 75 MAX_LINE_LENGTH = 128 |
76 | 76 |
77 # Set to true for more output. This is set by the command line options. | 77 # Set to true for more output. This is set by the command line options. |
78 VERBOSE = False | 78 VERBOSE = False |
79 | 79 |
80 # This regular expression will be used to extract filenames from include | 80 # This regular expression will be used to extract filenames from include |
81 # statements. | 81 # statements. |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
276 '.h', | 276 '.h', |
277 '.cc', | 277 '.cc', |
278 '.m', | 278 '.m', |
279 '.mm', | 279 '.mm', |
280 ] | 280 ] |
281 basename, extension = os.path.splitext(file_name) | 281 basename, extension = os.path.splitext(file_name) |
282 return extension in checked_extensions | 282 return extension in checked_extensions |
283 | 283 |
284 | 284 |
285 def CheckLine(rules, line): | 285 def CheckLine(rules, line): |
286 """Checks the given file with the given rule set. If the line is an #include | 286 """Checks the given file with the given rule set. |
287 directive and is illegal, a string describing the error will be returned. | 287 Returns a tuple (is_include, illegal_description). |
288 Otherwise, None will be returned.""" | 288 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 |
| 290 the error. Otherwise, it will be None.""" |
289 found_item = EXTRACT_INCLUDE_PATH.match(line) | 291 found_item = EXTRACT_INCLUDE_PATH.match(line) |
290 if not found_item: | 292 if not found_item: |
291 return None # Not a match | 293 return False, None # Not a match |
292 | 294 |
293 include_path = found_item.group(1) | 295 include_path = found_item.group(1) |
294 | 296 |
295 # Fix up backslashes in case somebody accidentally used them. | 297 # Fix up backslashes in case somebody accidentally used them. |
296 include_path.replace("\\", "/") | 298 include_path.replace("\\", "/") |
297 | 299 |
298 if include_path.find("/") < 0: | 300 if include_path.find("/") < 0: |
299 # Don't fail when no directory is specified. We may want to be more | 301 # Don't fail when no directory is specified. We may want to be more |
300 # strict about this in the future. | 302 # strict about this in the future. |
301 if VERBOSE: | 303 if VERBOSE: |
302 print " WARNING: directory specified with no path: " + include_path | 304 print " WARNING: directory specified with no path: " + include_path |
303 return None | 305 return True, None |
304 | 306 |
305 (allowed, why_failed) = rules.DirAllowed(include_path) | 307 (allowed, why_failed) = rules.DirAllowed(include_path) |
306 if not allowed: | 308 if not allowed: |
307 if VERBOSE: | 309 if VERBOSE: |
308 retval = "\nFor " + rules.__str__() | 310 retval = "\nFor " + rules.__str__() |
309 else: | 311 else: |
310 retval = "" | 312 retval = "" |
311 return retval + ('Illegal include: "%s"\n Because of %s' % | 313 return True, retval + ('Illegal include: "%s"\n Because of %s' % |
312 (include_path, why_failed)) | 314 (include_path, why_failed)) |
313 | 315 |
314 return None | 316 return True, None |
315 | 317 |
316 | 318 |
317 def CheckFile(rules, file_name): | 319 def CheckFile(rules, file_name): |
318 """Checks the given file with the given rule set. | 320 """Checks the given file with the given rule set. |
319 | 321 |
320 Args: | 322 Args: |
321 rules: The set of rules that apply to files in this directory. | 323 rules: The set of rules that apply to files in this directory. |
322 file_name: The source file to check. | 324 file_name: The source file to check. |
323 | 325 |
324 Returns: Either a string describing the error if there was one, or None if | 326 Returns: Either a string describing the error if there was one, or None if |
325 the file checked out OK. | 327 the file checked out OK. |
326 """ | 328 """ |
327 if VERBOSE: | 329 if VERBOSE: |
328 print "Checking: " + file_name | 330 print "Checking: " + file_name |
329 | 331 |
330 ret_val = "" # We'll collect the error messages in here | 332 ret_val = "" # We'll collect the error messages in here |
| 333 last_include = 0 |
331 try: | 334 try: |
332 cur_file = open(file_name, "r") | 335 cur_file = open(file_name, "r") |
333 in_if0 = 0 | 336 in_if0 = 0 |
334 for cur_line in range(MAX_LINES): | 337 for line_num in xrange(sys.maxint): |
335 cur_line = cur_file.readline(MAX_LINE_LENGTH).strip() | 338 if line_num - last_include > MAX_UNINTERESTING_LINES: |
| 339 break |
| 340 |
| 341 cur_line = cur_file.readline(MAX_LINE_LENGTH) |
| 342 if cur_line == "": |
| 343 break |
| 344 cur_line = cur_line.strip() |
336 | 345 |
337 # Check to see if we're at / inside a #if 0 block | 346 # Check to see if we're at / inside a #if 0 block |
338 if cur_line == '#if 0': | 347 if cur_line == '#if 0': |
339 in_if0 += 1 | 348 in_if0 += 1 |
340 continue | 349 continue |
341 if in_if0 > 0: | 350 if in_if0 > 0: |
342 if cur_line.startswith('#if'): | 351 if cur_line.startswith('#if'): |
343 in_if0 += 1 | 352 in_if0 += 1 |
344 elif cur_line == '#endif': | 353 elif cur_line == '#endif': |
345 in_if0 -= 1 | 354 in_if0 -= 1 |
346 continue | 355 continue |
347 | 356 |
348 line_status = CheckLine(rules, cur_line) | 357 is_include, line_status = CheckLine(rules, cur_line) |
| 358 if is_include: |
| 359 last_include = line_num |
349 if line_status is not None: | 360 if line_status is not None: |
350 if len(line_status) > 0: # Add newline to separate messages. | 361 if len(line_status) > 0: # Add newline to separate messages. |
351 line_status += "\n" | 362 line_status += "\n" |
352 ret_val += line_status | 363 ret_val += line_status |
353 cur_file.close() | 364 cur_file.close() |
354 | 365 |
355 except IOError: | 366 except IOError: |
356 if VERBOSE: | 367 if VERBOSE: |
357 print "Unable to open file: " + file_name | 368 print "Unable to open file: " + file_name |
358 cur_file.close() | 369 cur_file.close() |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
492 'to "../../.." relative to the script file, which ' | 503 'to "../../.." relative to the script file, which ' |
493 'will normally be the repository root.') | 504 'will normally be the repository root.') |
494 option_parser.add_option("-v", "--verbose", action="store_true", | 505 option_parser.add_option("-v", "--verbose", action="store_true", |
495 default=False, help="Print debug logging") | 506 default=False, help="Print debug logging") |
496 options, args = option_parser.parse_args() | 507 options, args = option_parser.parse_args() |
497 return checkdeps(options, args) | 508 return checkdeps(options, args) |
498 | 509 |
499 | 510 |
500 if '__main__' == __name__: | 511 if '__main__' == __name__: |
501 sys.exit(main()) | 512 sys.exit(main()) |
OLD | NEW |