OLD | NEW |
(Empty) | |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """Checks Java files for illegal imports.""" |
| 6 |
| 7 import codecs |
| 8 import os |
| 9 import re |
| 10 |
| 11 |
| 12 class JavaChecker(object): |
| 13 """Import checker for Java files. |
| 14 |
| 15 The CheckFile method uses real filesystem paths, but Java imports work in |
| 16 terms of package names. To deal with this, we have an extra "prescan" pass |
| 17 that reads all the .java files and builds a mapping of class name -> filepath. |
| 18 In CheckFile, we convert each import statement into a real filepath, and check |
| 19 that against the rules in the DEPS files. |
| 20 |
| 21 Note that in Java you can always use classes in the same directory without an |
| 22 explicit import statement, so these imports can't be blocked with DEPS files. |
| 23 But that shouldn't be a problem, because same-package imports are pretty much |
| 24 always correct by definition. (If we find a case where this is *not* correct, |
| 25 it probably means the package is too big and needs to be split up.) |
| 26 |
| 27 Properties: |
| 28 _classmap: dict of fully-qualified Java class name -> filepath |
| 29 """ |
| 30 |
| 31 EXTENSIONS = ['.java'] |
| 32 |
| 33 def __init__(self, base_directory, verbose): |
| 34 self._base_directory = base_directory |
| 35 self._verbose = verbose |
| 36 self._classmap = {} |
| 37 self._PrescanFiles() |
| 38 |
| 39 def _PrescanFiles(self): |
| 40 for root, dirs, files in os.walk(self._base_directory): |
| 41 # Skip unwanted subdirectories. TODO(husky): it would be better to do |
| 42 # this via the skip_child_includes flag in DEPS files. Maybe hoist this |
| 43 # prescan logic into checkdeps.py itself? |
| 44 for d in dirs: |
| 45 # Skip hidden directories. |
| 46 if d.startswith('.'): |
| 47 dirs.remove(d) |
| 48 # Skip the "out" directory, as dealing with generated files is awkward. |
| 49 # We don't want paths like "out/Release/lib.java" in our DEPS files. |
| 50 # TODO(husky): We need some way of determining the "real" path to |
| 51 # a generated file -- i.e., where it would be in source control if |
| 52 # it weren't generated. |
| 53 if d == 'out': |
| 54 dirs.remove(d) |
| 55 # Skip third-party directories. |
| 56 if d == 'third_party': |
| 57 dirs.remove(d) |
| 58 for f in files: |
| 59 if f.endswith('.java'): |
| 60 self._PrescanFile(os.path.join(root, f)) |
| 61 |
| 62 def _PrescanFile(self, filepath): |
| 63 if self._verbose: |
| 64 print 'Prescanning: ' + filepath |
| 65 with codecs.open(filepath, encoding='utf-8') as f: |
| 66 short_class_name, _ = os.path.splitext(os.path.basename(filepath)) |
| 67 for line in f: |
| 68 for package in re.findall('^package ([\w\.]+);', line): |
| 69 full_class_name = package + '.' + short_class_name |
| 70 if full_class_name in self._classmap: |
| 71 print 'WARNING: multiple definitions of %s:' % full_class_name |
| 72 print ' ' + filepath |
| 73 print ' ' + self._classmap[full_class_name] |
| 74 print |
| 75 else: |
| 76 self._classmap[full_class_name] = filepath |
| 77 return |
| 78 print 'WARNING: no package definition found in %s' % filepath |
| 79 |
| 80 def CheckFile(self, rules, filepath): |
| 81 if self._verbose: |
| 82 print 'Checking: ' + filepath |
| 83 |
| 84 result = '' |
| 85 with codecs.open(filepath, encoding='utf-8') as f: |
| 86 for line in f: |
| 87 for clazz in re.findall('^import\s+(?:static\s+)?([\w\.]+)\s*;', line): |
| 88 if clazz not in self._classmap: |
| 89 # Importing a class from outside the Chromium tree. That's fine -- |
| 90 # it's probably a Java or Android system class. |
| 91 continue |
| 92 include_path = os.path.relpath( |
| 93 self._classmap[clazz], self._base_directory) |
| 94 # Convert Windows paths to Unix style, as used in DEPS files. |
| 95 include_path = include_path.replace(os.path.sep, '/') |
| 96 (allowed, why_failed) = rules.DirAllowed(include_path) |
| 97 if not allowed: |
| 98 if self._verbose: |
| 99 result += '\nFor %s' % rules |
| 100 result += 'Illegal include: "%s"\n Because of %s\n' % ( |
| 101 include_path, why_failed) |
| 102 if '{' in line: |
| 103 # This is code, so we're finished reading imports for this file. |
| 104 break |
| 105 |
| 106 return result |
OLD | NEW |