Chromium Code Reviews| Index: tools/checkdeps/java_checker.py |
| diff --git a/tools/checkdeps/java_checker.py b/tools/checkdeps/java_checker.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..48cdf2092372a67dc5e3a9bf16d7606a75423ea3 |
| --- /dev/null |
| +++ b/tools/checkdeps/java_checker.py |
| @@ -0,0 +1,105 @@ |
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Checks Java files for illegal imports.""" |
| + |
| +import codecs |
| +import os |
| +import re |
| +import sys |
| + |
| + |
| +class JavaChecker(object): |
| + """Import checker for Java files. |
| + |
| + The CheckFile method uses real filesystem paths, but Java imports work in |
| + terms of package names. To deal with this, we have an extra "prescan" pass |
| + that reads all the .java files and builds a mapping of class name -> filepath. |
| + In CheckFile, we convert each import statement into a real filepath, and check |
| + that against the rules in the DEPS files. |
| + |
| + Note that in Java you can always use classes in the same directory without an |
| + explicit import statement, so these imports can't be blocked with DEPS files. |
| + But that shouldn't be a problem, because same-package imports are pretty much |
| + always correct by definition. (If we find a case where this is *not* correct, |
| + it probably means the package is too big and needs to be split up.) |
| + |
| + Properties: |
| + _classmap: dict of fully-qualified Java class name -> filepath |
| + """ |
| + |
| + EXTENSIONS = ['.java'] |
| + |
| + def __init__(self, base_directory, verbose): |
| + self._base_directory = base_directory |
| + self._verbose = verbose |
| + self._classmap = {} |
| + self._PrescanFiles() |
| + |
| + def _PrescanFiles(self): |
| + for root, dirs, files in os.walk(self._base_directory): |
| + # Skip unwanted subdirectories. TODO(husky): it would be better to do |
| + # this via the skip_child_includes flag in DEPS files. Maybe hoist this |
| + # prescan logic into checkdeps.py itself? |
| + for d in dirs: |
| + # Skip hidden directories. |
| + if d.startswith('.'): |
| + dirs.remove(d) |
| + # Skip the "out" directory, as dealing with generated files is awkward. |
| + # We don't want paths like "out/Release/lib.java" in our DEPS files. |
| + # TODO(husky): We need some way of determining the "real" path to |
| + # a generated file -- i.e., where it would be in source control if |
| + # it weren't generated. |
| + if d == 'out': |
| + dirs.remove(d) |
| + # Skip third-party directories. |
| + if d == 'third_party': |
| + dirs.remove(d) |
| + for f in files: |
| + if f.endswith('.java'): |
| + self._PrescanFile(os.path.join(root, f)) |
| + |
| + def _PrescanFile(self, filepath): |
| + filepath = os.path.relpath(filepath, self._base_directory) |
| + if self._verbose: |
| + print 'Prescanning: ' + filepath |
| + with codecs.open(filepath, encoding='utf-8') as f: |
| + short_class_name, _ = os.path.splitext(os.path.basename(filepath)) |
| + for line in f: |
| + for package in re.findall('^package ([\w\.]+);', line): |
| + full_class_name = package + '.' + short_class_name |
| + if full_class_name in self._classmap: |
| + print 'WARNING: multiple definitions of %s:' % full_class_name |
| + print ' ' + filepath |
| + print ' ' + self._classmap[full_class_name] |
| + else: |
| + self._classmap[full_class_name] = filepath |
| + return |
| + print 'WARNING: no package definition found in %s' % filepath |
| + |
| + def CheckFile(self, rules, filepath): |
| + if self._verbose: |
| + print 'Checking: ' + filepath |
| + |
| + result = '' |
| + with codecs.open(filepath, encoding='utf-8') as f: |
| + for line in f: |
| + for clazz in re.findall('^import\s+(?:static\s+)?([\w\.]+)\s*;', line): |
| + if clazz not in self._classmap: |
| + # Importing a class from outside the Chromium tree. That's fine -- |
| + # it's probably a Java or Android system class. |
| + continue |
| + include_path = self._classmap[clazz] |
| + (allowed, why_failed) = rules.DirAllowed(include_path) |
| + if not allowed: |
| + if self._verbose: |
| + result += '\nFor ' + rules.__str__() |
|
M-A Ruel
2012/07/18 14:19:37
Same.
Iain Merrick
2012/07/19 12:44:34
Done.
|
| + result += 'Illegal include: "%s"\n Because of %s\n' % ( |
| + include_path, why_failed) |
| + if '{' in line: |
| + # This is code, so we're finished reading imports for this file. |
| + break |
| + |
| + return result |