JavaCommon.py (scons-4.2.0) | : | JavaCommon.py (SCons-4.3.0) | ||
---|---|---|---|---|
"""SCons.Tool.JavaCommon | # MIT License | |||
Stuff for processing Java. | ||||
""" | ||||
# | # | |||
# __COPYRIGHT__ | # Copyright The SCons Foundation | |||
# | # | |||
# Permission is hereby granted, free of charge, to any person obtaining | # Permission is hereby granted, free of charge, to any person obtaining | |||
# a copy of this software and associated documentation files (the | # a copy of this software and associated documentation files (the | |||
# "Software"), to deal in the Software without restriction, including | # "Software"), to deal in the Software without restriction, including | |||
# without limitation the rights to use, copy, modify, merge, publish, | # without limitation the rights to use, copy, modify, merge, publish, | |||
# distribute, sublicense, and/or sell copies of the Software, and to | # distribute, sublicense, and/or sell copies of the Software, and to | |||
# permit persons to whom the Software is furnished to do so, subject to | # permit persons to whom the Software is furnished to do so, subject to | |||
# the following conditions: | # the following conditions: | |||
# | # | |||
# The above copyright notice and this permission notice shall be included | # The above copyright notice and this permission notice shall be included | |||
# in all copies or substantial portions of the Software. | # in all copies or substantial portions of the Software. | |||
# | # | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
# | ||||
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" | """Common routines for processing Java. """ | |||
import os | import os | |||
import os.path | ||||
import re | import re | |||
import glob | import glob | |||
from pathlib import Path | ||||
from typing import List | ||||
java_parsing = 1 | java_parsing = True | |||
default_java_version = '1.4' | default_java_version = '1.4' | |||
# a switch for which jdk versions to use the Scope state for smarter | # a switch for which jdk versions to use the Scope state for smarter | |||
# anonymous inner class parsing. | # anonymous inner class parsing. | |||
scopeStateVersions = ('1.8',) | scopeStateVersions = ('1.8',) | |||
# Glob patterns for use in finding where the JDK is. | # 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. | # These are pairs, (*dir_glob, *version_dir_glob) depending on whether | |||
# For now only used for Windows. | # a JDK version was requested or not. | |||
java_win32_dir_glob = 'C:/Program Files*/Java/jdk*/bin' | # For now only used for Windows, which doesn't install JDK in a | |||
# path that would be in env['ENV']['PATH']. The specific tool will | ||||
# add the discovered path to this. Since Oracle changed the rules, | ||||
# there are many possible vendors, we can't guess them all, but take a shot. | ||||
java_win32_dir_glob = 'C:/Program Files*/*/*jdk*/bin' | ||||
# On windows, since Java 9, there is a dash between 'jdk' and the version | # 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. | # string that wasn't there before. this glob should catch either way. | |||
java_win32_version_dir_glob = 'C:/Program Files*/Java/jdk*%s*/bin' | java_win32_version_dir_glob = 'C:/Program Files*/*/*jdk*%s*/bin' | |||
# Glob patterns for use in finding where the JDK headers are. | # Glob patterns for use in finding where the JDK headers are. | |||
# These are pairs, *dir_glob used in the general case, | # These are pairs, *dir_glob used in the general case, | |||
# *version_dir_glob if matching only a specific version. | # *version_dir_glob if matching only a specific version. | |||
java_macos_include_dir_glob = '/System/Library/Frameworks/JavaVM.framework/Heade rs/' | java_macos_include_dir_glob = '/System/Library/Frameworks/JavaVM.framework/Heade rs/' | |||
java_macos_version_include_dir_glob = '/System/Library/Frameworks/JavaVM.framewo rk/Versions/%s*/Headers/' | java_macos_version_include_dir_glob = '/System/Library/Frameworks/JavaVM.framewo rk/Versions/%s*/Headers/' | |||
java_linux_include_dirs_glob = [ | java_linux_include_dirs_glob = [ | |||
'/usr/lib/jvm/default-java/include', | '/usr/lib/jvm/default-java/include', | |||
'/usr/lib/jvm/java-*/include' | '/usr/lib/jvm/java-*/include', | |||
'/opt/oracle-jdk-bin-*/include', | ||||
'/opt/openjdk-bin-*/include', | ||||
'/usr/lib/openjdk-*/include', | ||||
] | ] | |||
# Need to match path like below (from Centos 7) | # 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/ | # /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 = [ | java_linux_version_include_dirs_glob = [ | |||
'/usr/lib/jvm/java-*-sun-%s*/include', | '/usr/lib/jvm/java-*-sun-%s*/include', | |||
'/usr/lib/jvm/java-%s*-openjdk*/include', | '/usr/lib/jvm/java-%s*-openjdk*/include', | |||
'/usr/java/jdk%s*/include' | '/usr/java/jdk%s*/include', | |||
] | ] | |||
if java_parsing: | if java_parsing: | |||
# Parse Java files for class names. | # Parse Java files for class names. | |||
# | # | |||
# This is a really cool parser from Charles Crain | # This is a really cool parser from Charles Crain | |||
# that finds appropriate class names in Java source. | # that finds appropriate class names in Java source. | |||
# A regular expression that will find, in a java file: | # A regular expression that will find, in a java file: | |||
# newlines; | # newlines; | |||
skipping to change at line 100 | skipping to change at line 103 | |||
# Lambda function symbols: -> | # Lambda function symbols: -> | |||
_reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"{\};.()]|' + | _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"{\};.()]|' + | |||
r'\d*\.\d*|[A-Za-z_][\w$.]*|<[A-Za-z_]\w+>|' + | r'\d*\.\d*|[A-Za-z_][\w$.]*|<[A-Za-z_]\w+>|' + | |||
r'/\*|\*/|\[\]|->)') | r'/\*|\*/|\[\]|->)') | |||
class OuterState: | class OuterState: | |||
"""The initial state for parsing a Java file for classes, | """The initial state for parsing a Java file for classes, | |||
interfaces, and anonymous inner classes.""" | interfaces, and anonymous inner classes.""" | |||
def __init__(self, version=default_java_version): | def __init__(self, version=default_java_version): | |||
if version not in ( | ||||
if version not in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', | '1.1', | |||
'1.8', '5', '6', '9.0', '10.0', '11.0', '12.0'): | '1.2', | |||
'1.3', | ||||
'1.4', | ||||
'1.5', | ||||
'1.6', | ||||
'1.7', | ||||
'1.8', | ||||
'5', | ||||
'6', | ||||
'9.0', | ||||
'10.0', | ||||
'11.0', | ||||
'12.0', | ||||
'13.0', | ||||
'14.0', | ||||
'15.0', | ||||
'16.0', | ||||
'17.0', | ||||
): | ||||
msg = "Java version %s not supported" % version | msg = "Java version %s not supported" % version | |||
raise NotImplementedError(msg) | raise NotImplementedError(msg) | |||
self.version = version | self.version = version | |||
self.listClasses = [] | self.listClasses = [] | |||
self.listOutputs = [] | self.listOutputs = [] | |||
self.stackBrackets = [] | self.stackBrackets = [] | |||
self.brackets = 0 | self.brackets = 0 | |||
self.nextAnon = 1 | self.nextAnon = 1 | |||
self.localClasses = [] | self.localClasses = [] | |||
skipping to change at line 209 | skipping to change at line 230 | |||
# case we don't want to treat the following token as | # case we don't want to treat the following token as | |||
# an inner class name... | # an inner class name... | |||
return self.__getSkipState() | return self.__getSkipState() | |||
return self | return self | |||
def addAnonClass(self): | def addAnonClass(self): | |||
"""Add an anonymous inner class""" | """Add an anonymous inner class""" | |||
if self.version in ('1.1', '1.2', '1.3', '1.4'): | if self.version in ('1.1', '1.2', '1.3', '1.4'): | |||
clazz = self.listClasses[0] | clazz = self.listClasses[0] | |||
self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) | self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) | |||
elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', ' | # TODO: shouldn't need to repeat versions here and in OuterState | |||
10.0', '11.0', '12.0'): | elif self.version in ( | |||
'1.5', | ||||
'1.6', | ||||
'1.7', | ||||
'1.8', | ||||
'5', | ||||
'6', | ||||
'9.0', | ||||
'10.0', | ||||
'11.0', | ||||
'12.0', | ||||
'13.0', | ||||
'14.0', | ||||
'15.0', | ||||
'16.0', | ||||
'17.0', | ||||
): | ||||
self.stackAnonClassBrackets.append(self.brackets) | self.stackAnonClassBrackets.append(self.brackets) | |||
className = [] | className = [] | |||
className.extend(self.listClasses) | className.extend(self.listClasses) | |||
self._getAnonStack()[-1] = self._getAnonStack()[-1] + 1 | self._getAnonStack()[-1] = self._getAnonStack()[-1] + 1 | |||
for anon in self._getAnonStack(): | for anon in self._getAnonStack(): | |||
className.append(str(anon)) | className.append(str(anon)) | |||
self.listOutputs.append('$'.join(className)) | self.listOutputs.append('$'.join(className)) | |||
self.nextAnon = self.nextAnon + 1 | self.nextAnon = self.nextAnon + 1 | |||
self._getAnonStack().append(0) | self._getAnonStack().append(0) | |||
skipping to change at line 436 | skipping to change at line 474 | |||
def parse_java_file(fn): | def parse_java_file(fn): | |||
""" "Parse" a .java file. | """ "Parse" a .java file. | |||
This actually just splits the file name, so the assumption here | This actually just splits the file name, so the assumption here | |||
is that the file name matches the public class name, and that | is that the file name matches the public class name, and that | |||
the path to the file is the same as the package name. | the path to the file is the same as the package name. | |||
""" | """ | |||
return os.path.split(fn) | return os.path.split(fn) | |||
def get_java_install_dirs(platform, version=None): | def get_java_install_dirs(platform, version=None) -> List[str]: | |||
""" | """ Find possible java jdk installation directories. | |||
Find the java jdk installation directories. | ||||
This list is intended to supply as "default paths" for use when looking | Returns a list for use as `default_paths` when looking up actual | |||
up actual java binaries. | java binaries with :meth:`SCons.Tool.find_program_path`. | |||
The paths are sorted by version, latest first. | ||||
Args: | ||||
platform: selector for search algorithm. | ||||
version: if not None, restrict the search to this version. | ||||
:param platform: selector for search algorithm. | Returns: | |||
:param version: If specified, only look for java sdk's of this version | list of default paths for jdk. | |||
:return: list of default paths for java. | ||||
""" | """ | |||
paths = [] | ||||
if platform == 'win32': | if platform == 'win32': | |||
paths = [] | ||||
if version: | if version: | |||
paths = glob.glob(java_win32_version_dir_glob % version) | paths = glob.glob(java_win32_version_dir_glob % version) | |||
else: | else: | |||
paths = glob.glob(java_win32_dir_glob) | paths = glob.glob(java_win32_dir_glob) | |||
else: | ||||
# other platforms, do nothing for now | ||||
pass | ||||
return sorted(paths) | def win32getvnum(java): | |||
""" Generates a sort key for win32 jdk versions. | ||||
def get_java_include_paths(env, javac, version): | We'll have gotten a path like ...something/*jdk*/bin because | |||
""" | that is the pattern we glob for. To generate the sort key, | |||
Find java include paths for JNI building. | extracts the next-to-last component, then trims it further if | |||
it had a complex name, like 'java-1.8.0-openjdk-1.8.0.312-1', | ||||
to try and put it on a common footing with the more common style, | ||||
which looks like 'jdk-11.0.2'. | ||||
This is certainly fragile, and if someone has a 9.0 it won't | ||||
sort right since this will still be alphabetic, BUT 9.0 was | ||||
not an LTS release and is 30 mos out of support as this note | ||||
is written so just assume it will be okay. | ||||
""" | ||||
d = Path(java).parts[-2] | ||||
if not d.startswith('jdk'): | ||||
d = 'jdk' + d.rsplit('jdk', 1)[-1] | ||||
return d | ||||
return sorted(paths, key=win32getvnum, reverse=True) | ||||
# other platforms, do nothing for now: we expect the standard | ||||
# paths to be enough to find a jdk (e.g. use alternatives system) | ||||
return [] | ||||
def get_java_include_paths(env, javac, version) -> List[str]: | ||||
"""Find java include paths for JNI building. | ||||
Cannot be called in isolation - `javac` refers to an already detected | ||||
compiler. Normally would would call :func:`get_java_install_dirs` first | ||||
and then do lookups on the paths it returns before calling us. | ||||
Args: | ||||
env: construction environment, used to extract platform. | ||||
javac: path to detected javac. | ||||
version: if not None, restrict the search to this version. | ||||
:param env: construction environment, used to extract platform. | Returns: | |||
:param javac: path to detected javac. | list of include directory paths. | |||
:return: list of paths. | ||||
""" | """ | |||
paths = [] | ||||
if not javac: | if not javac: | |||
# there are no paths if we've not detected javac. | return [] | |||
pass | ||||
elif env['PLATFORM'] == 'win32': | # on Windows, we have a path to the actual javac, so look locally | |||
# on Windows, we have the right path to javac, so look locally | if env['PLATFORM'] == 'win32': | |||
javac_bin_dir = os.path.dirname(javac) | javac_bin_dir = os.path.dirname(javac) | |||
java_inc_dir = os.path.normpath(os.path.join(javac_bin_dir, '..', 'inclu de')) | java_inc_dir = os.path.normpath(os.path.join(javac_bin_dir, '..', 'inclu de')) | |||
paths = [java_inc_dir, os.path.join(java_inc_dir, 'win32')] | paths = [java_inc_dir, os.path.join(java_inc_dir, 'win32')] | |||
# for the others, we probably found something which isn't in the JDK dir, | ||||
# so use the predefined patterns to glob for an include directory. | ||||
elif env['PLATFORM'] == 'darwin': | elif env['PLATFORM'] == 'darwin': | |||
if not version: | if not version: | |||
paths = [java_macos_include_dir_glob] | paths = [java_macos_include_dir_glob] | |||
else: | else: | |||
paths = sorted(glob.glob(java_macos_version_include_dir_glob % versi on)) | paths = sorted(glob.glob(java_macos_version_include_dir_glob % versi on)) | |||
else: | else: | |||
base_paths = [] | base_paths = [] | |||
if not version: | if not version: | |||
for p in java_linux_include_dirs_glob: | for p in java_linux_include_dirs_glob: | |||
base_paths.extend(glob.glob(p)) | base_paths.extend(glob.glob(p)) | |||
else: | else: | |||
for p in java_linux_version_include_dirs_glob: | for p in java_linux_version_include_dirs_glob: | |||
base_paths.extend(glob.glob(p % version)) | base_paths.extend(glob.glob(p % version)) | |||
paths = [] | ||||
for p in base_paths: | for p in base_paths: | |||
paths.extend([p, os.path.join(p, 'linux')]) | paths.extend([p, os.path.join(p, 'linux')]) | |||
# print("PATHS:%s"%paths) | ||||
return paths | return paths | |||
# Local Variables: | # Local Variables: | |||
# tab-width:4 | # tab-width:4 | |||
# indent-tabs-mode:nil | # indent-tabs-mode:nil | |||
# End: | # End: | |||
# vim: set expandtab tabstop=4 shiftwidth=4: | # vim: set expandtab tabstop=4 shiftwidth=4: | |||
End of changes. 27 change blocks. | ||||
48 lines changed or deleted | 118 lines changed or added |