Skip to content

Commit 9fa38bd

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 942ad52 + de3d353 commit 9fa38bd

File tree

11 files changed

+171
-72
lines changed

11 files changed

+171
-72
lines changed

doc/source/services.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,9 @@ do pass it, the service can make use of this argument.
7979
Services support a range of options and interactions not yet
8080
documented here but all accessible via calling other methods of the
8181
``service`` reference.
82+
83+
.. note:: The app root directory for Python imports will be in the app
84+
root folder even if the service file is in a subfolder. To import from
85+
your service folder you must use e.g. ``import service.module``
86+
instead of ``import module``, if the service file is in the
87+
``service/`` folder.

pythonforandroid/bootstraps/sdl2/build/src/org/kivy/android/PythonUtil.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,30 @@ protected static String[] getLibraries() {
1616
"SDL2_ttf",
1717
"python2.7",
1818
"python3.5m",
19+
"python3.6m",
1920
"main"
2021
};
2122
}
2223

2324
public static void loadLibraries(File filesDir) {
2425

2526
String filesDirPath = filesDir.getAbsolutePath();
26-
boolean skippedPython = false;
27+
boolean foundPython = false;
2728

2829
for (String lib : getLibraries()) {
2930
try {
3031
System.loadLibrary(lib);
32+
if (lib.startsWith("python")) {
33+
foundPython = true;
34+
}
3135
} catch(UnsatisfiedLinkError e) {
32-
if (lib.startsWith("python") && !skippedPython) {
33-
skippedPython = true;
34-
continue;
36+
// If this is the last possible libpython
37+
// load, and it has failed, give a more
38+
// general error
39+
if (lib.startsWith("python3.6") && !foundPython) {
40+
throw new java.lang.RuntimeException("Could not load any libpythonXXX.so");
3541
}
36-
throw e;
42+
continue;
3743
}
3844
}
3945

@@ -52,5 +58,5 @@ public static void loadLibraries(File filesDir) {
5258
}
5359

5460
Log.v(TAG, "Loaded everything!");
55-
}
61+
}
5662
}

pythonforandroid/build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ def build_recipes(build_order, python_modules, ctx):
530530
bs = ctx.bootstrap
531531
info_notify("Recipe build order is {}".format(build_order))
532532
if python_modules:
533+
python_modules = sorted(set(python_modules))
533534
info_notify(
534535
('The requirements ({}) were not found as recipes, they will be '
535536
'installed with pip.').format(', '.join(python_modules)))

pythonforandroid/recipe.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,6 @@ def install_python_package(self, arch, name=None, env=None, is_dir=True):
818818

819819
with current_directory(self.get_build_dir(arch.arch)):
820820
hostpython = sh.Command(self.hostpython_location)
821-
# hostpython = sh.Command('python3.5')
822821

823822

824823
if self.ctx.python_recipe.from_crystax:
@@ -986,15 +985,13 @@ def build_cython_components(self, arch):
986985
site_packages_dirs = command(
987986
'-c', 'import site; print("\\n".join(site.getsitepackages()))')
988987
site_packages_dirs = site_packages_dirs.stdout.decode('utf-8').split('\n')
989-
# env['PYTHONPATH'] = '/usr/lib/python3.5/site-packages/:/usr/lib/python3.5'
990988
if 'PYTHONPATH' in env:
991989
env['PYTHONPATH'] = env + ':{}'.format(':'.join(site_packages_dirs))
992990
else:
993991
env['PYTHONPATH'] = ':'.join(site_packages_dirs)
994992

995993
with current_directory(self.get_build_dir(arch.arch)):
996994
hostpython = sh.Command(self.ctx.hostpython)
997-
# hostpython = sh.Command('python3.5')
998995
shprint(hostpython, '-c', 'import sys; print(sys.path)', _env=env)
999996
print('cwd is', realpath(curdir))
1000997
info('Trying first build of {} to get cython files: this is '
@@ -1042,6 +1039,8 @@ def cythonize_file(self, env, build_dir, filename):
10421039
cyenv['PYTHONPATH'] = cyenv['CYTHONPATH']
10431040
elif 'PYTHONPATH' in cyenv:
10441041
del cyenv['PYTHONPATH']
1042+
if 'PYTHONNOUSERSITE' in cyenv:
1043+
cyenv.pop('PYTHONNOUSERSITE')
10451044
cython = 'cython' if self.ctx.python_recipe.from_crystax else self.ctx.cython
10461045
cython_command = sh.Command(cython)
10471046
shprint(cython_command, filename, *self.cython_args, _env=cyenv)
@@ -1089,12 +1088,14 @@ def get_recipe_env(self, arch, with_flags_in_cc=True):
10891088
self.ctx.python_recipe.version, 'include',
10901089
'python')) + env['CFLAGS']
10911090

1092-
# Temporarily hardcode the -lpython3.5 as this does not
1091+
# Temporarily hardcode the -lpython3.x as this does not
10931092
# get applied automatically in some environments. This
10941093
# will need generalising, along with the other hardcoded
10951094
# py3.5 references, to support other python3 or crystax
10961095
# python versions.
1097-
env['LDFLAGS'] = env['LDFLAGS'] + ' -lpython3.5m'
1096+
python3_version = self.ctx.python_recipe.version
1097+
python3_version = '.'.join(python3_version.split('.')[:2])
1098+
env['LDFLAGS'] = env['LDFLAGS'] + ' -lpython{}m'.format(python3_version)
10981099

10991100
return env
11001101

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from pythonforandroid.recipe import PythonRecipe
2+
3+
4+
class DateutilRecipe(PythonRecipe):
5+
name = 'dateutil'
6+
version = '2.6.0'
7+
url = 'https://pypi.python.org/packages/51/fc/39a3fbde6864942e8bb24c93663734b74e281b984d1b8c4f95d64b0c21f6/python-dateutil-2.6.0.tar.gz'
8+
9+
depends = ['python2', "setuptools"]
10+
call_hostpython_via_targetpython = False
11+
install_in_hostpython = True
12+
13+
14+
recipe = DateutilRecipe()

pythonforandroid/recipes/icu/__init__.py

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ICURecipe(NDKRecipe):
1111
version = '57.1'
1212
url = 'http://download.icu-project.org/files/icu4c/57.1/icu4c-57_1-src.tgz'
1313

14-
depends = [('python2', 'python3crystax')] # installs in python
14+
depends = [('python2', 'python3crystax'), 'hostpython2'] # installs in python
1515
generated_libraries = [
1616
'libicui18n.so', 'libicuuc.so', 'libicudata.so', 'libicule.so']
1717

@@ -68,50 +68,48 @@ def make_build_dest(dest):
6868
shprint(sh.make, "install", _env=host_env)
6969

7070
build_android, exists = make_build_dest("build_icu_android")
71-
if exists:
72-
return
73-
74-
configure = sh.Command(join(build_root, "source", "configure"))
75-
76-
include = (
77-
" -I{ndk}/sources/cxx-stl/gnu-libstdc++/{version}/include/"
78-
" -I{ndk}/sources/cxx-stl/gnu-libstdc++/{version}/libs/"
79-
"{arch}/include")
80-
include = include.format(ndk=self.ctx.ndk_dir,
81-
version=env["TOOLCHAIN_VERSION"],
82-
arch=arch.arch)
83-
env["CPPFLAGS"] = env["CXXFLAGS"] + " "
84-
env["CPPFLAGS"] += host_env["CPPFLAGS"]
85-
env["CPPFLAGS"] += include
71+
if not exists:
8672

87-
lib = "{ndk}/sources/cxx-stl/gnu-libstdc++/{version}/libs/{arch}"
88-
lib = lib.format(ndk=self.ctx.ndk_dir,
89-
version=env["TOOLCHAIN_VERSION"],
90-
arch=arch.arch)
91-
env["LDFLAGS"] += " -lgnustl_shared -L"+lib
92-
93-
env.pop("CFLAGS", None)
94-
env.pop("CXXFLAGS", None)
95-
96-
with current_directory(build_android):
97-
shprint(
98-
configure,
99-
"--with-cross-build="+build_linux,
100-
"--enable-extras=no",
101-
"--enable-strict=no",
102-
"--enable-static",
103-
"--enable-tests=no",
104-
"--enable-samples=no",
105-
"--host="+env["TOOLCHAIN_PREFIX"],
106-
"--prefix="+icu_build,
107-
_env=env)
108-
shprint(sh.make, "-j5", _env=env)
109-
shprint(sh.make, "install", _env=env)
73+
configure = sh.Command(join(build_root, "source", "configure"))
74+
75+
include = (
76+
" -I{ndk}/sources/cxx-stl/gnu-libstdc++/{version}/include/"
77+
" -I{ndk}/sources/cxx-stl/gnu-libstdc++/{version}/libs/"
78+
"{arch}/include")
79+
include = include.format(ndk=self.ctx.ndk_dir,
80+
version=env["TOOLCHAIN_VERSION"],
81+
arch=arch.arch)
82+
env["CPPFLAGS"] = env["CXXFLAGS"] + " "
83+
env["CPPFLAGS"] += host_env["CPPFLAGS"]
84+
env["CPPFLAGS"] += include
85+
86+
lib = "{ndk}/sources/cxx-stl/gnu-libstdc++/{version}/libs/{arch}"
87+
lib = lib.format(ndk=self.ctx.ndk_dir,
88+
version=env["TOOLCHAIN_VERSION"],
89+
arch=arch.arch)
90+
env["LDFLAGS"] += " -lgnustl_shared -L"+lib
91+
92+
env.pop("CFLAGS", None)
93+
env.pop("CXXFLAGS", None)
94+
95+
with current_directory(build_android):
96+
shprint(
97+
configure,
98+
"--with-cross-build="+build_linux,
99+
"--enable-extras=no",
100+
"--enable-strict=no",
101+
"--enable-static",
102+
"--enable-tests=no",
103+
"--enable-samples=no",
104+
"--host="+env["TOOLCHAIN_PREFIX"],
105+
"--prefix="+icu_build,
106+
_env=env)
107+
shprint(sh.make, "-j5", _env=env)
108+
shprint(sh.make, "install", _env=env)
110109

111110
self.copy_files(arch)
112111

113112
def copy_files(self, arch):
114-
ndk = self.ctx.ndk_dir
115113
env = self.get_recipe_env(arch)
116114

117115
lib = "{ndk}/sources/cxx-stl/gnu-libstdc++/{version}/libs/{arch}"

pythonforandroid/recipes/libzmq/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ def build_arch(self, arch):
4848
self.ctx.ndk_dir, self.ctx.toolchain_version, arch),
4949
join(bootstrap_obj_dir, 'libgnustl_shared.so'))
5050

51+
# Copy libgnustl_shared.so
52+
with current_directory(self.get_build_dir(arch.arch)):
53+
sh.cp(
54+
"{ctx.ndk_dir}/sources/cxx-stl/gnu-libstdc++/{ctx.toolchain_version}/libs/{arch.arch}/libgnustl_shared.so".format(ctx=self.ctx,arch=arch),
55+
self.ctx.get_libs_dir(arch.arch)
56+
)
57+
5158
def get_recipe_env(self, arch):
5259
# XXX should stl be configuration for the toolchain itself?
5360
env = super(LibZMQRecipe, self).get_recipe_env(arch)

pythonforandroid/recipes/protobuf_cpp/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from pythonforandroid.recipe import PythonRecipe
22
from pythonforandroid.logger import shprint
3-
from pythonforandroid.util import current_directory
3+
from pythonforandroid.util import current_directory, shutil
4+
from pythonforandroid.util import ensure_dir
45
from os.path import exists, join, dirname
56
import sh
67
from multiprocessing import cpu_count
@@ -37,6 +38,10 @@ def build_arch(self, arch):
3738
shprint(sh.make, 'libprotobuf.la', '-j'+str(cpu_count()), _env=env)
3839
shprint(sh.cp, '.libs/libprotobuf.a', join(self.ctx.get_libs_dir(arch.arch), 'libprotobuf.a'))
3940

41+
# Copy stl library
42+
shutil.copyfile(self.ctx.ndk_dir + '/sources/cxx-stl/gnu-libstdc++/' + self.ctx.toolchain_version + '/libs/' + arch.arch + '/libgnustl_shared.so',
43+
join(self.ctx.get_libs_dir(arch.arch), 'libgnustl_shared.so'))
44+
4045
# Build python bindings and _message.so
4146
with current_directory(join(self.get_build_dir(arch.arch), 'python')):
4247
hostpython = sh.Command(self.hostpython_location)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from pythonforandroid.toolchain import PythonRecipe
2+
from pythonforandroid.toolchain import CythonRecipe
3+
from pythonforandroid.recipe import CompiledComponentsPythonRecipe
4+
from pythonforandroid.logger import info
5+
6+
import os.path
7+
8+
class PymunkRecipe(CompiledComponentsPythonRecipe):
9+
name = "pymunk"
10+
version = '5.2.0'
11+
url = 'https://pypi.python.org/packages/5e/bd/e67edcffdee3d0a1e3ebf0050bb9746a61d616f5502ceedddf0f7fd0a896/pymunk-5.2.0.zip'
12+
depends = [('python2', 'python3crystax'), 'cffi', 'setuptools']
13+
call_hostpython_via_targetpython = False
14+
15+
def get_recipe_env(self, arch):
16+
env = super(PymunkRecipe, self).get_recipe_env(arch)
17+
env['PYTHON_ROOT'] = self.ctx.get_python_install_dir()
18+
arch_noeabi = arch.arch.replace('eabi', '')
19+
env['LDFLAGS'] += " -shared -llog"
20+
env['LDFLAGS'] += " -landroid -lpython2.7"
21+
env['LDFLAGS'] += " --sysroot={ctx.ndk_dir}/platforms/android-{ctx.android_api}/arch-{arch_noeabi}".format(
22+
ctx=self.ctx, arch_noeabi=arch_noeabi)
23+
return env
24+
25+
recipe = PymunkRecipe()
Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11

22
from pythonforandroid.recipe import TargetPythonRecipe
33
from pythonforandroid.toolchain import shprint, current_directory, ArchARM
4-
from pythonforandroid.logger import info
5-
from pythonforandroid.util import ensure_dir
4+
from pythonforandroid.logger import info, error
5+
from pythonforandroid.util import ensure_dir, temp_directory
66
from os.path import exists, join
7-
from os import uname
87
import glob
98
import sh
109

10+
prebuilt_download_locations = {
11+
'3.6': ('https://github.com/inclement/crystax_python_builds/'
12+
'releases/download/0.1/crystax_python_3.6_armeabi_armeabi-v7a.tar.gz')}
13+
1114
class Python3Recipe(TargetPythonRecipe):
1215
version = '3.5'
1316
url = ''
@@ -24,19 +27,50 @@ def get_dir_name(self):
2427
return name
2528

2629
def build_arch(self, arch):
27-
info('Extracting CrystaX python3 from NDK package')
30+
# We don't have to actually build anything as CrystaX comes
31+
# with the necessary modules. They are included by modifying
32+
# the Android.mk in the jni folder.
33+
34+
# If the Python version to be used is not prebuilt with the CrystaX
35+
# NDK, we do have to download it.
36+
37+
crystax_python_dir = join(self.ctx.ndk_dir, 'sources', 'python')
38+
if not exists(join(crystax_python_dir, self.version)):
39+
info(('The NDK does not have a prebuilt Python {}, trying '
40+
'to obtain one.').format(self.version))
41+
42+
if self.version not in prebuilt_download_locations:
43+
error(('No prebuilt version for Python {} could be found, '
44+
'the built cannot continue.'))
45+
exit(1)
46+
47+
with temp_directory() as td:
48+
self.download_file(prebuilt_download_locations[self.version],
49+
join(td, 'downloaded_python'))
50+
shprint(sh.tar, 'xf', join(td, 'downloaded_python'),
51+
'--directory', crystax_python_dir)
52+
53+
if not exists(join(crystax_python_dir, self.version)):
54+
error(('Something went wrong, the directory at {} should '
55+
'have been created but does not exist.').format(
56+
join(crystax_python_dir, self.version)))
57+
58+
if not exists(join(
59+
crystax_python_dir, self.version, 'libs', arch.arch)):
60+
error(('The prebuilt Python for version {} does not contain '
61+
'binaries for your chosen architecture "{}".').format(
62+
self.version, arch.arch))
63+
exit(1)
64+
65+
# TODO: We should have an option to build a new Python. This
66+
# would also allow linking to openssl and sqlite from CrystaX.
2867

2968
dirn = self.ctx.get_python_install_dir()
3069
ensure_dir(dirn)
3170

71+
# Instead of using a locally built hostpython, we use the
72+
# user's Python for now. They must have the right version
73+
# available. Using e.g. pyenv makes this easy.
3274
self.ctx.hostpython = 'python{}'.format(self.version)
33-
# ensure_dir(join(dirn, 'lib'))
34-
# ensure_dir(join(dirn, 'lib', 'python{}'.format(self.version),
35-
# 'site-packages'))
36-
37-
# ndk_dir = self.ctx.ndk_dir
38-
# sh.cp('-r', '/home/asandy/kivytest/crystax_stdlib', join(dirn, 'lib', 'python3.5'))
39-
# sh.cp('-r', '/home/asandy/android/crystax-ndk-10.3.0/sources/python/3.5/libs/armeabi/modules', join(dirn, 'lib', 'python3.5', 'lib-dynload'))
40-
# ensure_dir(join(dirn, 'lib', 'site-packages'))
4175

4276
recipe = Python3Recipe()

pythonforandroid/toolchain.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ def apk(self, args):
768768
if not apk_file:
769769
info_main('# APK filename not found in build output, trying to guess')
770770
suffix = args.build_mode
771-
if suffix == 'release':
771+
if suffix == 'release' and not args.keystore:
772772
suffix = suffix + '-unsigned'
773773
apks = glob.glob(join(dist.dist_dir, 'bin', '*-*-{}.apk'.format(suffix)))
774774
if len(apks) == 0:
@@ -871,18 +871,20 @@ def _adb(self, commands):
871871

872872

873873
def build_status(self, args):
874-
875874
print('{Style.BRIGHT}Bootstraps whose core components are probably '
876875
'already built:{Style.RESET_ALL}'.format(Style=Out_Style))
877-
for filen in os.listdir(join(self.ctx.build_dir, 'bootstrap_builds')):
878-
print(' {Fore.GREEN}{Style.BRIGHT}{filen}{Style.RESET_ALL}'
879-
.format(filen=filen, Fore=Out_Fore, Style=Out_Style))
876+
877+
bootstrap_dir = join(self.ctx.build_dir, 'bootstrap_builds')
878+
if exists(bootstrap_dir):
879+
for filen in os.listdir(bootstrap_dir):
880+
print(' {Fore.GREEN}{Style.BRIGHT}{filen}{Style.RESET_ALL}'
881+
.format(filen=filen, Fore=Out_Fore, Style=Out_Style))
880882

881883
print('{Style.BRIGHT}Recipes that are probably already built:'
882884
'{Style.RESET_ALL}'.format(Style=Out_Style))
883-
if exists(join(self.ctx.build_dir, 'other_builds')):
884-
for filen in sorted(
885-
os.listdir(join(self.ctx.build_dir, 'other_builds'))):
885+
other_builds_dir = join(self.ctx.build_dir, 'other_builds')
886+
if exists(other_builds_dir):
887+
for filen in sorted(os.listdir(other_builds_dir)):
886888
name = filen.split('-')[0]
887889
dependencies = filen.split('-')[1:]
888890
recipe_str = (' {Style.BRIGHT}{Fore.GREEN}{name}'

0 commit comments

Comments
 (0)