pyjnius/setup.py

299 lines
8.8 KiB
Python

'''
Setup.py for creating a binary distribution.
'''
from __future__ import print_function
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup, Extension
try:
import subprocess32 as subprocess
except ImportError:
import subprocess
import os
from os import environ
from os.path import dirname, join, exists, realpath
import re
import sys
from platform import machine
from setup_sdist import SETUP_KWARGS
PY2 = sys.version_info < (3, 0, 0)
def getenv(key):
'''Get value from environment and decode it.'''
val = environ.get(key)
if val is not None:
if not PY2:
try:
return val.decode()
except AttributeError:
return val
return val
FILES = [
'jni.pxi',
'jnius_compat.pxi',
'jnius_conversion.pxi',
'jnius_export_class.pxi',
'jnius_export_func.pxi',
'jnius_jvm_android.pxi',
'jnius_jvm_desktop.pxi',
'jnius_jvm_dlopen.pxi',
'jnius_localref.pxi',
'jnius.pyx',
'jnius_utils.pxi',
]
LIBRARIES = []
LIBRARY_DIRS = []
LIB_LOCATION = None
EXTRA_LINK_ARGS = []
INCLUDE_DIRS = []
INSTALL_REQUIRES = ['six>=1.7.0']
SETUP_REQUIRES = []
# detect Python for android
PLATFORM = sys.platform
NDKPLATFORM = getenv('NDKPLATFORM')
if NDKPLATFORM is not None and getenv('LIBLINK'):
PLATFORM = 'android'
# detect cython
try:
from Cython.Distutils import build_ext
INSTALL_REQUIRES.append('cython')
except ImportError:
# pylint: disable=ungrouped-imports
try:
from setuptools.command.build_ext import build_ext
except ImportError:
from distutils.command.build_ext import build_ext
if PLATFORM != 'android':
SETUP_REQUIRES.append('cython')
INSTALL_REQUIRES.append('cython')
else:
# On Android we expect to see 'c' files lying about.
# and we go ahead with the 'desktop' file? Odd.
FILES = [fn[:-3] + 'c' for fn in FILES if fn.endswith('pyx')]
def find_javac(possible_homes):
'''Find javac in all possible locations.'''
name = "javac.exe" if sys.platform == "win32" else "javac"
for home in possible_homes:
for javac in [join(home, name), join(home, 'bin', name)]:
if exists(javac):
if sys.platform == "win32" and not PY2: # Avoid path space execution error
return '"%s"' % javac
return javac
return name # Fall back to "hope it's on the path"
def compile_native_invocation_handler(*possible_homes):
'''Find javac and compile NativeInvocationHandler.java.'''
javac = find_javac(possible_homes)
source_level = '1.7'
try:
subprocess.check_call([
javac, '-target', source_level, '-source', source_level,
join('jnius', 'src', 'org', 'jnius', 'NativeInvocationHandler.java')
])
except FileNotFoundError:
subprocess.check_call([
javac.replace('"', ''), '-target', source_level, '-source', source_level,
join('jnius', 'src', 'org', 'jnius', 'NativeInvocationHandler.java')
])
if PLATFORM == 'android':
# for android, we use SDL...
LIBRARIES = ['sdl', 'log']
LIBRARY_DIRS = ['libs/' + getenv('ARCH')]
elif PLATFORM == 'darwin':
FRAMEWORK = subprocess.Popen(
'/usr/libexec/java_home',
stdout=subprocess.PIPE, shell=True).communicate()[0]
if not PY2:
FRAMEWORK = FRAMEWORK.decode('utf-8')
FRAMEWORK = FRAMEWORK.strip()
if not FRAMEWORK:
raise Exception('You must install Java on your Mac OS X distro')
if '1.6' in FRAMEWORK:
LIB_LOCATION = '../Libraries/libjvm.dylib'
INCLUDE_DIRS = [join(
FRAMEWORK, (
'System/Library/Frameworks/'
'JavaVM.framework/Versions/Current/Headers'
)
)]
else:
LIB_LOCATION = 'jre/lib/jli/libjli.dylib'
# We want to favor Java installation declaring JAVA_HOME
if getenv('JAVA_HOME'):
FRAMEWORK = getenv('JAVA_HOME')
FULL_LIB_LOCATION = join(FRAMEWORK, LIB_LOCATION)
if not exists(FULL_LIB_LOCATION):
# In that case, the Java version is very likely >=9.
# So we need to modify the `libjvm.so` path.
LIB_LOCATION = 'lib/jli/libjli.dylib'
FULL_LIB_LOCATION = join(FRAMEWORK, LIB_LOCATION)
if not exists(FULL_LIB_LOCATION):
# adoptopenjdk12 doesn't have the jli subfolder either
LIB_LOCATION = 'lib/libjli.dylib'
FULL_LIB_LOCATION = join(FRAMEWORK, LIB_LOCATION)
INCLUDE_DIRS = [
'{0}/include'.format(FRAMEWORK),
'{0}/include/darwin'.format(FRAMEWORK)
]
print('JAVA_HOME: {0}\n'.format(FRAMEWORK))
compile_native_invocation_handler(FRAMEWORK)
else:
# Note: if on Windows, set ONLY JAVA_HOME
# not on android or osx, we need to search the JDK_HOME
JDK_HOME = getenv('JDK_HOME')
if not JDK_HOME:
if PLATFORM == 'win32':
TMP_JDK_HOME = getenv('JAVA_HOME')
print(TMP_JDK_HOME)
if not TMP_JDK_HOME:
raise Exception('Unable to find JAVA_HOME')
# Remove /bin if it's appended to JAVA_HOME
if TMP_JDK_HOME[-3:] == 'bin':
TMP_JDK_HOME = TMP_JDK_HOME[:-4]
# Check whether it's JDK
if exists(join(TMP_JDK_HOME, 'bin', 'javac.exe')):
JDK_HOME = TMP_JDK_HOME
else:
JDK_HOME = realpath(
subprocess.check_output(
['which', 'javac']
).decode('utf-8').strip()
).replace('bin/javac', '')
if not JDK_HOME or not exists(JDK_HOME):
raise Exception('Unable to determine JDK_HOME')
JRE_HOME = None
if exists(join(JDK_HOME, 'jre')):
JRE_HOME = join(JDK_HOME, 'jre')
if PLATFORM != 'win32' and not JRE_HOME:
JRE_HOME = realpath(
subprocess.check_output(
['which', 'java']
).decode('utf-8').strip()
).replace('bin/java', '')
# This dictionary converts values from platform.machine()
# to a "cpu" string. It is needed to set the correct lib path,
# found in the JRE_HOME, e.g.: <JRE_HOME>/lib/<cpu>/.
MACHINE2CPU = {
"i686": "i386",
"x86_64": "amd64",
"armv7l": "arm",
"sun4u": "sparcv9",
"sun4v": "sparcv9"
}
if machine() in MACHINE2CPU.keys():
CPU = MACHINE2CPU[machine()]
else:
print(
"WARNING: Not able to assign machine()"
" = %s to a cpu value!" % machine()
)
print(" Using cpu = 'i386' instead!")
CPU = 'i386'
if PLATFORM == 'win32':
INCL_DIR = join(JDK_HOME, 'include', 'win32')
LIBRARIES = ['jvm']
elif PLATFORM == 'sunos5':
INCL_DIR = join(JDK_HOME, 'include', 'solaris')
LIB_LOCATION = 'jre/lib/{}/server/libjvm.so'.format(CPU)
else:
INCL_DIR = join(JDK_HOME, 'include', 'linux')
LIB_LOCATION = 'jre/lib/{}/server/libjvm.so'.format(CPU)
if isinstance(JRE_HOME, bytes):
JAVA_HOME = dirname(JRE_HOME.decode())
else:
JAVA_HOME = dirname(JRE_HOME)
FULL_LIB_LOCATION = join(JAVA_HOME, LIB_LOCATION)
if not exists(FULL_LIB_LOCATION):
# In that case, the Java version is very likely >=9.
# So we need to modify the `libjvm.so` path.
LIB_LOCATION = 'lib/server/libjvm.so'
INCLUDE_DIRS = [
join(JDK_HOME, 'include'),
INCL_DIR
]
if PLATFORM == 'win32':
if isinstance(JRE_HOME, bytes):
JRE_HOME = JRE_HOME.decode('utf-8')
LIBRARY_DIRS = [
join(JDK_HOME, 'lib'),
join(JDK_HOME, 'bin', 'server')
]
print('JDK_HOME: {0}\n'.format(JDK_HOME))
print('JRE_HOME: {0}\n'.format(JRE_HOME))
compile_native_invocation_handler(JDK_HOME, JRE_HOME)
# generate the config.pxi
with open(join(dirname(__file__), 'jnius', 'config.pxi'), 'w') as fd:
fd.write('DEF JNIUS_PLATFORM = {0!r}\n\n'.format(PLATFORM))
if not PY2:
fd.write('DEF JNIUS_PYTHON3 = True\n\n')
else:
fd.write('DEF JNIUS_PYTHON3 = False\n\n')
if LIB_LOCATION is not None:
fd.write('DEF JNIUS_LIB_SUFFIX = {0!r}\n\n'.format(LIB_LOCATION))
# pop setup.py from included files in the installed package
SETUP_KWARGS['py_modules'].remove('setup')
# create the extension
setup(
cmdclass={'build_ext': build_ext},
install_requires=INSTALL_REQUIRES,
setup_requires=SETUP_REQUIRES,
ext_modules=[
Extension(
'jnius', [join('jnius', x) for x in FILES],
libraries=LIBRARIES,
library_dirs=LIBRARY_DIRS,
include_dirs=INCLUDE_DIRS,
extra_link_args=EXTRA_LINK_ARGS
)
],
**SETUP_KWARGS
)