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