Skip to content

Commit 6fc1303

Browse files
committed
Added separate module for checking user SDK, NDK, API etc.
Also fixed some issues found during testing
1 parent 8c1d5c8 commit 6fc1303

File tree

5 files changed

+143
-91
lines changed

5 files changed

+143
-91
lines changed

pythonforandroid/build.py

Lines changed: 10 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
from pythonforandroid.logger import (info, warning, info_notify, info_main, shprint)
1616
from pythonforandroid.archs import ArchARM, ArchARMv7_a, ArchAarch_64, Archx86, Archx86_64
1717
from pythonforandroid.recipe import CythonRecipe, Recipe
18-
19-
DEFAULT_ANDROID_API = 15
20-
21-
DEFAULT_NDK_API = 21
22-
18+
from pythonforandroid.recommendations import (
19+
check_ndk_version, check_target_api, check_ndk_api,
20+
RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API)
2321

2422
class Context(object):
2523
'''A build context. If anything will be built, an instance this class
@@ -140,19 +138,6 @@ def ndk_api(self):
140138
def ndk_api(self, value):
141139
self._ndk_api = value
142140

143-
@property
144-
def ndk_ver(self):
145-
'''The version of the NDK being used for compilation.'''
146-
if self._ndk_ver is None:
147-
raise ValueError('Tried to access ndk_ver but it has not '
148-
'been set - this should not happen, something '
149-
'went wrong!')
150-
return self._ndk_ver
151-
152-
@ndk_ver.setter
153-
def ndk_ver(self, value):
154-
self._ndk_ver = value
155-
156141
@property
157142
def sdk_dir(self):
158143
'''The path to the Android SDK.'''
@@ -183,7 +168,6 @@ def prepare_build_environment(self,
183168
user_sdk_dir,
184169
user_ndk_dir,
185170
user_android_api,
186-
user_ndk_ver,
187171
user_ndk_api):
188172
'''Checks that build dependencies exist and sets internal variables
189173
for the Android SDK etc.
@@ -237,17 +221,12 @@ def prepare_build_environment(self,
237221
info('Found Android API target in $ANDROIDAPI: {}'.format(android_api))
238222
else:
239223
info('Android API target was not set manually, using '
240-
'the default of {}'.format(DEFAULT_ANDROID_API))
241-
android_api = DEFAULT_ANDROID_API
224+
'the default of {}'.format(RECOMMENDED_TARGET_API))
225+
android_api = RECOMMENDED_TARGET_API
242226
android_api = int(android_api)
243227
self.android_api = android_api
244228

245-
if self.android_api >= 21 and self.archs[0].arch == 'armeabi':
246-
raise BuildInterruptingException(
247-
'Asked to build for armeabi architecture with API '
248-
'{}, but API 21 or greater does not support armeabi'.format(
249-
self.android_api),
250-
instructions='You probably want to build with --arch=armeabi-v7a instead')
229+
check_target_api(android_api, self.archs[0].arch)
251230

252231
if exists(join(sdk_dir, 'tools', 'bin', 'avdmanager')):
253232
avdmanager = sh.Command(join(sdk_dir, 'tools', 'bin', 'avdmanager'))
@@ -306,47 +285,7 @@ def prepare_build_environment(self,
306285
raise BuildInterruptingException('Android NDK dir was not specified')
307286
self.ndk_dir = realpath(ndk_dir)
308287

309-
# Find the NDK version, and check it against what the NDK dir
310-
# seems to report
311-
ndk_ver = None
312-
if user_ndk_ver:
313-
ndk_ver = user_ndk_ver
314-
if ndk_dir is not None:
315-
info('Got NDK version from from user argument: {}'.format(ndk_ver))
316-
if ndk_ver is None:
317-
ndk_ver = environ.get('ANDROIDNDKVER', None)
318-
if ndk_ver is not None:
319-
info('Got NDK version from $ANDROIDNDKVER: {}'.format(ndk_ver))
320-
321-
self.ndk = 'google'
322-
323-
try:
324-
with open(join(ndk_dir, 'RELEASE.TXT')) as fileh:
325-
reported_ndk_ver = fileh.read().split(' ')[0].strip()
326-
except IOError:
327-
pass
328-
else:
329-
if reported_ndk_ver.startswith('crystax-ndk-'):
330-
reported_ndk_ver = reported_ndk_ver[12:]
331-
self.ndk = 'crystax'
332-
if ndk_ver is None:
333-
ndk_ver = reported_ndk_ver
334-
info(('Got Android NDK version from the NDK dir: {}').format(ndk_ver))
335-
else:
336-
if ndk_ver != reported_ndk_ver:
337-
warning('NDK version was set as {}, but checking '
338-
'the NDK dir claims it is {}.'.format(
339-
ndk_ver, reported_ndk_ver))
340-
warning('The build will try to continue, but it may '
341-
'fail and you should check '
342-
'that your setting is correct.')
343-
warning('If the NDK dir result is correct, you don\'t '
344-
'need to manually set the NDK ver.')
345-
if ndk_ver is None:
346-
warning('Android NDK version could not be found. This probably'
347-
'won\'t cause any problems, but if necessary you can'
348-
'set it with `--ndk-version=...`.')
349-
self.ndk_ver = ndk_ver
288+
check_ndk_version(ndk_dir)
350289

351290
ndk_api = None
352291
if user_ndk_api:
@@ -356,21 +295,14 @@ def prepare_build_environment(self,
356295
ndk_api = environ.get('NDKAPI', None)
357296
info('Found Android API target in $NDKAPI')
358297
else:
359-
ndk_api = min(self.android_api, DEFAULT_NDK_API)
298+
ndk_api = min(self.android_api, RECOMMENDED_NDK_API)
360299
warning('NDK API target was not set manually, using '
361300
'the default of {} = min(android-api={}, default ndk-api={})'.format(
362-
ndk_api, self.android_api, DEFAULT_NDK_API))
301+
ndk_api, self.android_api, RECOMMENDED_NDK_API))
363302
ndk_api = int(ndk_api)
364303
self.ndk_api = ndk_api
365304

366-
if self.ndk_api > self.android_api:
367-
raise BuildInterruptingException(
368-
'Target NDK API is {}, higher than the target Android API {}.'.format(
369-
self.ndk_api, self.android_api),
370-
instructions=('The NDK API is a minimum supported API number and must be lower '
371-
'than the target Android API'))
372-
373-
info('Using {} NDK {}'.format(self.ndk.capitalize(), self.ndk_ver))
305+
check_ndk_api(ndk_api, self.android_api)
374306

375307
virtualenv = None
376308
if virtualenv is None:
@@ -483,7 +415,6 @@ def __init__(self):
483415
self._ndk_dir = None
484416
self._android_api = None
485417
self._ndk_api = None
486-
self._ndk_ver = None
487418
self.ndk = None
488419

489420
self.toolchain_prefix = None

pythonforandroid/recommendations.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""Simple functions for checking dependency versions."""
2+
3+
from distutils.version import LooseVersion
4+
from os.path import join
5+
from pythonforandroid.logger import info, warning
6+
from pythonforandroid.util import BuildInterruptingException
7+
8+
# We only check the NDK major version
9+
MIN_NDK_VERSION = 17
10+
MAX_NDK_VERSION = 17
11+
12+
RECOMMENDED_NDK_VERSION = '17c'
13+
OLD_NDK_MESSAGE = 'Older NDKs may not be compatible with all p4a features.'
14+
NEW_NDK_MESSAGE = 'Newer NDKs may not be fully supported by p4a.'
15+
16+
17+
def get_recommended_ndk():
18+
pass
19+
20+
def check_ndk_version(ndk_dir):
21+
# Check the NDK version against what is currently recommended
22+
version = read_ndk_version(ndk_dir)
23+
24+
if version is None:
25+
return # it doesn't matter
26+
27+
major_version = version.version[0]
28+
29+
info('Found NDK revision {}'.format(version))
30+
31+
if major_version < MIN_NDK_VERSION:
32+
warning('Minimum recommended NDK version is {}'.format(
33+
RECOMMENDED_NDK_VERSION))
34+
warning(OLD_NDK_MESSAGE)
35+
elif major_version > MAX_NDK_VERSION:
36+
warning('Maximum recommended NDK version is {}'.format(
37+
RECOMMENDED_NDK_VERSION))
38+
warning(NEW_NDK_MESSAGE)
39+
40+
def read_ndk_version(ndk_dir):
41+
"""Read the NDK version from the NDK dir, if possible"""
42+
try:
43+
with open(join(ndk_dir, 'source.properties')) as fileh:
44+
ndk_data = fileh.read()
45+
except IOError:
46+
info('Could not determine NDK version, no source.properties '
47+
'in the NDK dir')
48+
return
49+
50+
for line in ndk_data.split('\n'):
51+
if line.startswith('Pkg.Revision'):
52+
break
53+
else:
54+
info('Could not parse $NDK_DIR/source.properties, not checking '
55+
'NDK version')
56+
57+
# Line should have the form "Pkg.Revision = ..."
58+
ndk_version = LooseVersion(line.split('=')[-1].strip())
59+
60+
return ndk_version
61+
62+
63+
MIN_TARGET_API = 26
64+
RECOMMENDED_TARGET_API = 27 # highest version tested to work fine with SDL2
65+
# should be a good default for other bootstraps too
66+
ARMEABI_MAX_TARGET_API = 21
67+
OLD_API_MESSAGE = (
68+
'Target APIs lower than 26 are no longer supported on Google Play, '
69+
'and are not recommended. Note that the Target API can be higher than '
70+
'your device Android version, and should usually be as high as possible.')
71+
72+
def check_target_api(api, arch):
73+
"""Warn if the user's target API is less than the current minimum
74+
recommendation
75+
"""
76+
77+
if api >= ARMEABI_MAX_TARGET_API and arch == 'armeabi':
78+
raise BuildInterruptingException(
79+
'Asked to build for armeabi architecture with API '
80+
'{}, but API {} or greater does not support armeabi'.format(
81+
self.android_api, ARMEABI_MAX_TARGET_API),
82+
instructions='You probably want to build with --arch=armeabi-v7a instead')
83+
84+
if api < MIN_TARGET_API:
85+
warning('Target API {} < {}'.format(api, MIN_TARGET_API))
86+
warning(OLD_API_MESSAGE)
87+
88+
89+
MIN_NDK_API = 21
90+
RECOMMENDED_NDK_API = 21
91+
OLD_NDK_API_MESSAGE = ('NDK API less than {} is not supported'.format(MIN_NDK_API))
92+
93+
def check_ndk_api(ndk_api, android_api):
94+
"""Warn if the user's NDK is too high or low."""
95+
if ndk_api > android_api:
96+
raise BuildInterruptingException(
97+
'Target NDK API is {}, higher than the target Android API {}.'.format(
98+
ndk_api, android_api),
99+
instructions=('The NDK API is a minimum supported API number and must be lower '
100+
'than the target Android API'))
101+
102+
if ndk_api < MIN_NDK_API:
103+
warning(OLD_NDK_API_MESSAGE)

pythonforandroid/toolchain.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
"""
88

99
from __future__ import print_function
10+
from os import environ
1011
from pythonforandroid import __version__
11-
from pythonforandroid.build import DEFAULT_NDK_API, DEFAULT_ANDROID_API
12+
from pythonforandroid.recommendations import (
13+
RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API)
1214
from pythonforandroid.util import BuildInterruptingException, handle_build_exception
1315

1416

@@ -139,7 +141,6 @@ def wrapper_func(self, args):
139141
ctx.prepare_build_environment(user_sdk_dir=self.sdk_dir,
140142
user_ndk_dir=self.ndk_dir,
141143
user_android_api=self.android_api,
142-
user_ndk_ver=self.ndk_version,
143144
user_ndk_api=self.ndk_api)
144145
dist = self._dist
145146
if dist.needs_build:
@@ -258,16 +259,16 @@ def __init__(self):
258259
default=0,
259260
type=int,
260261
help=('The Android API level to build against defaults to {} if '
261-
'not specified.').format(DEFAULT_ANDROID_API))
262+
'not specified.').format(RECOMMENDED_TARGET_API))
262263
generic_parser.add_argument(
263-
'--ndk-version', '--ndk_version', dest='ndk_version', default='',
264-
help=('The version of the Android NDK. This is optional: '
265-
'we try to work it out automatically from the ndk_dir.'))
264+
'--ndk-version', '--ndk_version', dest='ndk_version', default=None,
265+
help=('DEPRECATED: the NDK version is now found automatically or '
266+
'not at all.'))
266267
generic_parser.add_argument(
267268
'--ndk-api', type=int, default=None,
268269
help=('The Android API level to compile against. This should be your '
269270
'*minimal supported* API, not normally the same as your --android-api. '
270-
'Defaults to min(ANDROID_API, {}) if not specified.').format(DEFAULT_NDK_API))
271+
'Defaults to min(ANDROID_API, {}) if not specified.').format(RECOMMENDED_NDK_API))
271272
generic_parser.add_argument(
272273
'--symlink-java-src', '--symlink_java_src',
273274
action='store_true',
@@ -360,6 +361,11 @@ def add_parser(subparsers, *args, **kwargs):
360361
kwargs.pop('aliases')
361362
return subparsers.add_parser(*args, **kwargs)
362363

364+
parser_recommendations = add_parser(
365+
subparsers,
366+
'recommendations',
367+
parents=[generic_parser],
368+
help='List recommended p4a dependencies')
363369
parser_recipes = add_parser(
364370
subparsers,
365371
'recipes',
@@ -533,13 +539,14 @@ def add_parser(subparsers, *args, **kwargs):
533539
requirements.append(requirement)
534540
args.requirements = u",".join(requirements)
535541

542+
self.warn_on_deprecated_args(args)
543+
536544
self.ctx = Context()
537545
self.storage_dir = args.storage_dir
538546
self.ctx.setup_dirs(self.storage_dir)
539547
self.sdk_dir = args.sdk_dir
540548
self.ndk_dir = args.ndk_dir
541549
self.android_api = args.android_api
542-
self.ndk_version = args.ndk_version
543550
self.ndk_api = args.ndk_api
544551
self.ctx.symlink_java_src = args.symlink_java_src
545552
self.ctx.java_build_tool = args.java_build_tool
@@ -552,6 +559,19 @@ def add_parser(subparsers, *args, **kwargs):
552559
# Each subparser corresponds to a method
553560
getattr(self, args.subparser_name.replace('-', '_'))(args)
554561

562+
def warn_on_deprecated_args(self, args):
563+
"""
564+
Print warning messages for any deprecated arguments that were passed.
565+
"""
566+
567+
# NDK version is now determined automatically
568+
if args.ndk_version is not None:
569+
warning('--ndk-version is deprecated and no longer necessary, '
570+
'the value you passed is ignored')
571+
if 'ANDROIDNDKVER' in environ:
572+
warning('$ANDROIDNDKVER is deprecated and no longer necessary, '
573+
'the value you set is ignored')
574+
555575
def hook(self, name):
556576
if not self.args.hook:
557577
return
@@ -959,7 +979,6 @@ def sdk_tools(self, args):
959979
ctx.prepare_build_environment(user_sdk_dir=self.sdk_dir,
960980
user_ndk_dir=self.ndk_dir,
961981
user_android_api=self.android_api,
962-
user_ndk_ver=self.ndk_version,
963982
user_ndk_api=self.ndk_api)
964983
android = sh.Command(join(ctx.sdk_dir, 'tools', args.tool))
965984
output = android(
@@ -987,7 +1006,6 @@ def _adb(self, commands):
9871006
ctx.prepare_build_environment(user_sdk_dir=self.sdk_dir,
9881007
user_ndk_dir=self.ndk_dir,
9891008
user_android_api=self.android_api,
990-
user_ndk_ver=self.ndk_version,
9911009
user_ndk_api=self.ndk_api)
9921010
if platform in ('win32', 'cygwin'):
9931011
adb = sh.Command(join(ctx.sdk_dir, 'platform-tools', 'adb.exe'))

testapps/on_device_unit_tests/buildozer.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ android.whitelist = unittest/*
153153
#android.add_activites = com.example.ExampleActivity
154154

155155
# (str) python-for-android branch to use, defaults to master
156-
#p4a.branch = master
156+
p4a.branch = master
157157

158158
# (str) OUYA Console category. Should be one of GAME or APP
159159
# If you leave this blank, OUYA support will not be enabled

testapps/on_device_unit_tests/test_app/tests/test_requirements.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ class PyjniusTestCase(PythonTestMixIn, TestCase):
4747

4848
def test_run_module(self):
4949
from jnius import autoclass
50-
autoclass('org.kivy.PythonActivity')
50+
autoclass('org.kivy.android.PythonActivity')

0 commit comments

Comments
 (0)