Index: scons-2.0.1/engine/SCons/Tool/JavaCommon.py |
=================================================================== |
--- scons-2.0.1/engine/SCons/Tool/JavaCommon.py (revision 0) |
+++ scons-2.0.1/engine/SCons/Tool/JavaCommon.py (revision 0) |
@@ -0,0 +1,323 @@ |
+"""SCons.Tool.JavaCommon |
+ |
+Stuff for processing Java. |
+ |
+""" |
+ |
+# |
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation |
+# |
+# Permission is hereby granted, free of charge, to any person obtaining |
+# a copy of this software and associated documentation files (the |
+# "Software"), to deal in the Software without restriction, including |
+# without limitation the rights to use, copy, modify, merge, publish, |
+# distribute, sublicense, and/or sell copies of the Software, and to |
+# permit persons to whom the Software is furnished to do so, subject to |
+# the following conditions: |
+# |
+# The above copyright notice and this permission notice shall be included |
+# in all copies or substantial portions of the Software. |
+# |
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+# |
+ |
+__revision__ = "src/engine/SCons/Tool/JavaCommon.py 5134 2010/08/16 23:02:40 bdeegan" |
+ |
+import os |
+import os.path |
+import re |
+ |
+java_parsing = 1 |
+ |
+default_java_version = '1.4' |
+ |
+if java_parsing: |
+ # Parse Java files for class names. |
+ # |
+ # This is a really cool parser from Charles Crain |
+ # that finds appropriate class names in Java source. |
+ |
+ # A regular expression that will find, in a java file: |
+ # newlines; |
+ # double-backslashes; |
+ # a single-line comment "//"; |
+ # single or double quotes preceeded by a backslash; |
+ # single quotes, double quotes, open or close braces, semi-colons, |
+ # periods, open or close parentheses; |
+ # floating-point numbers; |
+ # any alphanumeric token (keyword, class name, specifier); |
+ # any alphanumeric token surrounded by angle brackets (generics); |
+ # the multi-line comment begin and end tokens /* and */; |
+ # array declarations "[]". |
+ _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + |
+ r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' + |
+ r'/\*|\*/|\[\])') |
+ |
+ class OuterState(object): |
+ """The initial state for parsing a Java file for classes, |
+ interfaces, and anonymous inner classes.""" |
+ def __init__(self, version=default_java_version): |
+ |
+ if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', |
+ '5', '6'): |
+ msg = "Java version %s not supported" % version |
+ raise NotImplementedError(msg) |
+ |
+ self.version = version |
+ self.listClasses = [] |
+ self.listOutputs = [] |
+ self.stackBrackets = [] |
+ self.brackets = 0 |
+ self.nextAnon = 1 |
+ self.localClasses = [] |
+ self.stackAnonClassBrackets = [] |
+ self.anonStacksStack = [[0]] |
+ self.package = None |
+ |
+ def trace(self): |
+ pass |
+ |
+ def __getClassState(self): |
+ try: |
+ return self.classState |
+ except AttributeError: |
+ ret = ClassState(self) |
+ self.classState = ret |
+ return ret |
+ |
+ def __getPackageState(self): |
+ try: |
+ return self.packageState |
+ except AttributeError: |
+ ret = PackageState(self) |
+ self.packageState = ret |
+ return ret |
+ |
+ def __getAnonClassState(self): |
+ try: |
+ return self.anonState |
+ except AttributeError: |
+ self.outer_state = self |
+ ret = SkipState(1, AnonClassState(self)) |
+ self.anonState = ret |
+ return ret |
+ |
+ def __getSkipState(self): |
+ try: |
+ return self.skipState |
+ except AttributeError: |
+ ret = SkipState(1, self) |
+ self.skipState = ret |
+ return ret |
+ |
+ def __getAnonStack(self): |
+ return self.anonStacksStack[-1] |
+ |
+ def openBracket(self): |
+ self.brackets = self.brackets + 1 |
+ |
+ def closeBracket(self): |
+ self.brackets = self.brackets - 1 |
+ if len(self.stackBrackets) and \ |
+ self.brackets == self.stackBrackets[-1]: |
+ self.listOutputs.append('$'.join(self.listClasses)) |
+ self.localClasses.pop() |
+ self.listClasses.pop() |
+ self.anonStacksStack.pop() |
+ self.stackBrackets.pop() |
+ if len(self.stackAnonClassBrackets) and \ |
+ self.brackets == self.stackAnonClassBrackets[-1]: |
+ self.__getAnonStack().pop() |
+ self.stackAnonClassBrackets.pop() |
+ |
+ def parseToken(self, token): |
+ if token[:2] == '//': |
+ return IgnoreState('\n', self) |
+ elif token == '/*': |
+ return IgnoreState('*/', self) |
+ elif token == '{': |
+ self.openBracket() |
+ elif token == '}': |
+ self.closeBracket() |
+ elif token in [ '"', "'" ]: |
+ return IgnoreState(token, self) |
+ elif token == "new": |
+ # anonymous inner class |
+ if len(self.listClasses) > 0: |
+ return self.__getAnonClassState() |
+ return self.__getSkipState() # Skip the class name |
+ elif token in ['class', 'interface', 'enum']: |
+ if len(self.listClasses) == 0: |
+ self.nextAnon = 1 |
+ self.stackBrackets.append(self.brackets) |
+ return self.__getClassState() |
+ elif token == 'package': |
+ return self.__getPackageState() |
+ elif token == '.': |
+ # Skip the attribute, it might be named "class", in which |
+ # case we don't want to treat the following token as |
+ # an inner class name... |
+ return self.__getSkipState() |
+ return self |
+ |
+ def addAnonClass(self): |
+ """Add an anonymous inner class""" |
+ if self.version in ('1.1', '1.2', '1.3', '1.4'): |
+ clazz = self.listClasses[0] |
+ self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) |
+ elif self.version in ('1.5', '1.6', '5', '6'): |
+ self.stackAnonClassBrackets.append(self.brackets) |
+ className = [] |
+ className.extend(self.listClasses) |
+ self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 |
+ for anon in self.__getAnonStack(): |
+ className.append(str(anon)) |
+ self.listOutputs.append('$'.join(className)) |
+ |
+ self.nextAnon = self.nextAnon + 1 |
+ self.__getAnonStack().append(0) |
+ |
+ def setPackage(self, package): |
+ self.package = package |
+ |
+ class AnonClassState(object): |
+ """A state that looks for anonymous inner classes.""" |
+ def __init__(self, old_state): |
+ # outer_state is always an instance of OuterState |
+ self.outer_state = old_state.outer_state |
+ self.old_state = old_state |
+ self.brace_level = 0 |
+ def parseToken(self, token): |
+ # This is an anonymous class if and only if the next |
+ # non-whitespace token is a bracket. Everything between |
+ # braces should be parsed as normal java code. |
+ if token[:2] == '//': |
+ return IgnoreState('\n', self) |
+ elif token == '/*': |
+ return IgnoreState('*/', self) |
+ elif token == '\n': |
+ return self |
+ elif token[0] == '<' and token[-1] == '>': |
+ return self |
+ elif token == '(': |
+ self.brace_level = self.brace_level + 1 |
+ return self |
+ if self.brace_level > 0: |
+ if token == 'new': |
+ # look further for anonymous inner class |
+ return SkipState(1, AnonClassState(self)) |
+ elif token in [ '"', "'" ]: |
+ return IgnoreState(token, self) |
+ elif token == ')': |
+ self.brace_level = self.brace_level - 1 |
+ return self |
+ if token == '{': |
+ self.outer_state.addAnonClass() |
+ return self.old_state.parseToken(token) |
+ |
+ class SkipState(object): |
+ """A state that will skip a specified number of tokens before |
+ reverting to the previous state.""" |
+ def __init__(self, tokens_to_skip, old_state): |
+ self.tokens_to_skip = tokens_to_skip |
+ self.old_state = old_state |
+ def parseToken(self, token): |
+ self.tokens_to_skip = self.tokens_to_skip - 1 |
+ if self.tokens_to_skip < 1: |
+ return self.old_state |
+ return self |
+ |
+ class ClassState(object): |
+ """A state we go into when we hit a class or interface keyword.""" |
+ def __init__(self, outer_state): |
+ # outer_state is always an instance of OuterState |
+ self.outer_state = outer_state |
+ def parseToken(self, token): |
+ # the next non-whitespace token should be the name of the class |
+ if token == '\n': |
+ return self |
+ # If that's an inner class which is declared in a method, it |
+ # requires an index prepended to the class-name, e.g. |
+ # 'Foo$1Inner' (Tigris Issue 2087) |
+ if self.outer_state.localClasses and \ |
+ self.outer_state.stackBrackets[-1] > \ |
+ self.outer_state.stackBrackets[-2]+1: |
+ locals = self.outer_state.localClasses[-1] |
+ try: |
+ idx = locals[token] |
+ locals[token] = locals[token]+1 |
+ except KeyError: |
+ locals[token] = 1 |
+ token = str(locals[token]) + token |
+ self.outer_state.localClasses.append({}) |
+ self.outer_state.listClasses.append(token) |
+ self.outer_state.anonStacksStack.append([0]) |
+ return self.outer_state |
+ |
+ class IgnoreState(object): |
+ """A state that will ignore all tokens until it gets to a |
+ specified token.""" |
+ def __init__(self, ignore_until, old_state): |
+ self.ignore_until = ignore_until |
+ self.old_state = old_state |
+ def parseToken(self, token): |
+ if self.ignore_until == token: |
+ return self.old_state |
+ return self |
+ |
+ class PackageState(object): |
+ """The state we enter when we encounter the package keyword. |
+ We assume the next token will be the package name.""" |
+ def __init__(self, outer_state): |
+ # outer_state is always an instance of OuterState |
+ self.outer_state = outer_state |
+ def parseToken(self, token): |
+ self.outer_state.setPackage(token) |
+ return self.outer_state |
+ |
+ def parse_java_file(fn, version=default_java_version): |
+ return parse_java(open(fn, 'r').read(), version) |
+ |
+ def parse_java(contents, version=default_java_version, trace=None): |
+ """Parse a .java file and return a double of package directory, |
+ plus a list of .class files that compiling that .java file will |
+ produce""" |
+ package = None |
+ initial = OuterState(version) |
+ currstate = initial |
+ for token in _reToken.findall(contents): |
+ # The regex produces a bunch of groups, but only one will |
+ # have anything in it. |
+ currstate = currstate.parseToken(token) |
+ if trace: trace(token, currstate) |
+ if initial.package: |
+ package = initial.package.replace('.', os.sep) |
+ return (package, initial.listOutputs) |
+ |
+else: |
+ # Don't actually parse Java files for class names. |
+ # |
+ # We might make this a configurable option in the future if |
+ # Java-file parsing takes too long (although it shouldn't relative |
+ # to how long the Java compiler itself seems to take...). |
+ |
+ def parse_java_file(fn): |
+ """ "Parse" a .java file. |
+ |
+ This actually just splits the file name, so the assumption here |
+ is that the file name matches the public class name, and that |
+ the path to the file is the same as the package name. |
+ """ |
+ return os.path.split(file) |
+ |
+# Local Variables: |
+# tab-width:4 |
+# indent-tabs-mode:nil |
+# End: |
+# vim: set expandtab tabstop=4 shiftwidth=4: |
Property changes on: scons-2.0.1/engine/SCons/Tool/JavaCommon.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |