diff options
Diffstat (limited to 'src/engine/SCons/Tool/JavaCommon.py')
| -rw-r--r-- | src/engine/SCons/Tool/JavaCommon.py | 232 | 
1 files changed, 210 insertions, 22 deletions
diff --git a/src/engine/SCons/Tool/JavaCommon.py b/src/engine/SCons/Tool/JavaCommon.py index 5c2507d..1e58af6 100644 --- a/src/engine/SCons/Tool/JavaCommon.py +++ b/src/engine/SCons/Tool/JavaCommon.py @@ -5,7 +5,7 @@ Stuff for processing Java.  """  # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation  #  # Permission is hereby granted, free of charge, to any person obtaining  # a copy of this software and associated documentation files (the @@ -27,16 +27,48 @@ Stuff for processing Java.  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  # -__revision__ = "src/engine/SCons/Tool/JavaCommon.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/JavaCommon.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan"  import os  import os.path  import re +import glob  java_parsing = 1  default_java_version = '1.4' +# a switch for which jdk versions to use the Scope state for smarter +# anonymous inner class parsing. +scopeStateVersions = ('1.8') + +# Glob patterns for use in finding where the JDK is. +# These are pairs, *dir_glob used in the general case, +# *version_dir_glob if matching only a specific version. +# For now only used for Windows. +java_win32_dir_glob = 'C:/Program Files*/Java/jdk*/bin' +# On windows, since Java 9, there is a dash between 'jdk' and the version +# string that wasn't there before. this glob should catch either way. +java_win32_version_dir_glob = 'C:/Program Files*/Java/jdk*%s*/bin' + +# Glob patterns for use in finding where the JDK headers are. +# These are pairs, *dir_glob used in the general case, +# *version_dir_glob if matching only a specific version. +java_macos_include_dir_glob = '/System/Library/Frameworks/JavaVM.framework/Headers/' +java_macos_version_include_dir_glob = '/System/Library/Frameworks/JavaVM.framework/Versions/%s*/Headers/' + +java_linux_include_dirs_glob = [ +    '/usr/lib/jvm/default-java/include', +    '/usr/lib/jvm/java-*/include' +] +# Need to match path like below (from Centos 7) +# /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64/include/ +java_linux_version_include_dirs_glob = [ +    '/usr/lib/jvm/java-*-sun-%s*/include', +    '/usr/lib/jvm/java-%s*-openjdk*/include', +    '/usr/java/jdk%s*/include' +] +  if java_parsing:      # Parse Java files for class names.      # @@ -59,13 +91,15 @@ if java_parsing:                            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', '1.7', -                               '1.8', '5', '6'): +            if version not in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', +                               '1.8', '5', '6', '9.0', '10.0', '11.0', '12.0'):                  msg = "Java version %s not supported" % version                  raise NotImplementedError(msg) @@ -115,8 +149,8 @@ if java_parsing:                  ret = SkipState(1, self)                  self.skipState = ret                  return ret -         -        def __getAnonStack(self): + +        def _getAnonStack(self):              return self.anonStacksStack[-1]          def openBracket(self): @@ -125,15 +159,16 @@ if java_parsing:          def closeBracket(self):              self.brackets = self.brackets - 1              if len(self.stackBrackets) and \ -               self.brackets == self.stackBrackets[-1]: +                    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.brackets == self.stackAnonClassBrackets[-1] and \ +                    self.version not in scopeStateVersions: +                self._getAnonStack().pop()                  self.stackAnonClassBrackets.pop()          def parseToken(self, token): @@ -145,13 +180,13 @@ if java_parsing:                  self.openBracket()              elif token == '}':                  self.closeBracket() -            elif token in [ '"', "'" ]: +            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 +                return self.__getSkipState()  # Skip the class name              elif token in ['class', 'interface', 'enum']:                  if len(self.listClasses) == 0:                      self.nextAnon = 1 @@ -171,28 +206,99 @@ if java_parsing:              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', '1.7', '1.8', '5', '6'): +            elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0', '11.0', '12.0'):                  self.stackAnonClassBrackets.append(self.brackets)                  className = []                  className.extend(self.listClasses) -                self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 -                for anon in self.__getAnonStack(): +                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) +            self._getAnonStack().append(0)          def setPackage(self, package):              self.package = package + +    class ScopeState(object): +        """ +        A state that parses code within a scope normally, +        within the confines of a scope. +        """ + +        def __init__(self, old_state): +            self.outer_state = old_state.outer_state +            self.old_state = old_state +            self.brackets = 0 + +        def __getClassState(self): +            try: +                return self.classState +            except AttributeError: +                ret = ClassState(self) +                self.classState = ret +                return ret + +        def __getAnonClassState(self): +            try: +                return self.anonState +            except AttributeError: +                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 openBracket(self): +            self.brackets = self.brackets + 1 + +        def closeBracket(self): +            self.brackets = self.brackets - 1 + +        def parseToken(self, token): +            # if self.brackets == 0: +            #     return self.old_state.parseToken(token) +            if token[:2] == '//': +                return IgnoreState('\n', self) +            elif token == '/*': +                return IgnoreState('*/', self) +            elif token == '{': +                self.openBracket() +            elif token == '}': +                self.closeBracket() +                if self.brackets == 0: +                    self.outer_state._getAnonStack().pop() +                    return self.old_state +            elif token in ['"', "'"]: +                return IgnoreState(token, self) +            elif token == "new": +                # anonymous inner class +                return self.__getAnonClassState() +            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 + +      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 @@ -212,32 +318,40 @@ if java_parsing:                  if token == 'new':                      # look further for anonymous inner class                      return SkipState(1, AnonClassState(self)) -                elif token in [ '"', "'" ]: +                elif token in ['"', "'"]:                      return IgnoreState(token, self)                  elif token == ')':                      self.brace_level = self.brace_level - 1                  return self              if token == '{':                  self.outer_state.addAnonClass() +                if self.outer_state.version in scopeStateVersions: +                    return ScopeState(old_state=self.old_state).parseToken(token)              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': @@ -245,14 +359,14 @@ if java_parsing:              # 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' -            # http://scons.tigris.org/issues/show_bug.cgi?id=2087 +            # https://github.com/SCons/scons/issues/2087              if self.outer_state.localClasses and \ -                self.outer_state.stackBrackets[-1] > \ -                self.outer_state.stackBrackets[-2]+1: +                    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 +                    locals[token] = locals[token] + 1                  except KeyError:                      locals[token] = 1                  token = str(locals[token]) + token @@ -261,29 +375,39 @@ if java_parsing:              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) +        with open(fn, 'r') as f: +            data = f.read() +        return parse_java(data, version) +      def parse_java(contents, version=default_java_version, trace=None):          """Parse a .java file and return a double of package directory, @@ -315,7 +439,71 @@ else:          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) +        return os.path.split(fn) + + +def get_java_install_dirs(platform, version=None): +    """ +    Find the java jdk installation directories. + +    This list is intended to supply as "default paths" for use when looking +    up actual java binaries. + +    :param platform: selector for search algorithm. +    :param version: If specified, only look for java sdk's of this version +    :return: list of default paths for java. +    """ + +    paths = [] +    if platform == 'win32': +        if version: +            paths = glob.glob(java_win32_version_dir_glob % version) +        else: +            paths = glob.glob(java_win32_dir_glob) +    else: +        # other platforms, do nothing for now +        pass + +    return sorted(paths) + + +def get_java_include_paths(env, javac, version): +    """ +    Find java include paths for JNI building. + +    :param env: construction environment, used to extract platform. +    :param javac: path to detected javac. +    :return: list of paths. +    """ + +    paths = [] +    if not javac: +        # there are no paths if we've not detected javac. +        pass +    elif env['PLATFORM'] == 'win32': +        # on Windows, we have the right path to javac, so look locally +        javac_bin_dir = os.path.dirname(javac) +        java_inc_dir = os.path.normpath(os.path.join(javac_bin_dir, '..', 'include')) +        paths = [java_inc_dir, os.path.join(java_inc_dir, 'win32')] +    elif env['PLATFORM'] == 'darwin': +        if not version: +            paths = [java_macos_include_dir_glob] +        else: +            paths = sorted(glob.glob(java_macos_version_include_dir_glob % version)) +    else: +        base_paths = [] +        if not version: +            for p in java_linux_include_dirs_glob: +                base_paths.extend(glob.glob(p)) +        else: +            for p in java_linux_version_include_dirs_glob: +                base_paths.extend(glob.glob(p % version)) + +        for p in base_paths: +            paths.extend([p, os.path.join(p, 'linux')]) + +    # print("PATHS:%s"%paths) +    return paths  # Local Variables:  # tab-width:4  | 
