Skip to content

Commit 01c0607

Browse files
authored
Made p4a use per-arch dist build dirs (#1986)
* Made p4a use per-arch dist build dirs * Fixed up check on existing folder when overwriting distributions * Improved Distribution.get_distribution api * Fixed test_archs.py * Fixed test_bootstrap.py * Fixed test_distribution.py * Fixed recipes tests * Fixed formatting * Made sure whole build process accesses dist_dir correctly * Fixed apk output name formatting by moving version to end of string * Further test fixes following dist_dir changes * Removed unnecessary variable
1 parent 9f6d6fc commit 01c0607

File tree

9 files changed

+140
-60
lines changed

9 files changed

+140
-60
lines changed

pythonforandroid/bootstrap.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
import shlex
99
import shutil
1010

11-
from pythonforandroid.logger import (warning, shprint, info, logger,
12-
debug)
13-
from pythonforandroid.util import (current_directory, ensure_dir,
14-
temp_directory)
11+
from pythonforandroid.logger import (shprint, info, logger, debug)
12+
from pythonforandroid.util import (
13+
current_directory, ensure_dir, temp_directory, BuildInterruptingException)
1514
from pythonforandroid.recipe import Recipe
1615

1716

@@ -75,7 +74,6 @@ class Bootstrap(object):
7574
bootstrap_dir = None
7675

7776
build_dir = None
78-
dist_dir = None
7977
dist_name = None
8078
distribution = None
8179

@@ -97,9 +95,9 @@ class Bootstrap(object):
9795
def dist_dir(self):
9896
'''The dist dir at which to place the finished distribution.'''
9997
if self.distribution is None:
100-
warning('Tried to access {}.dist_dir, but {}.distribution '
101-
'is None'.format(self, self))
102-
exit(1)
98+
raise BuildInterruptingException(
99+
'Internal error: tried to access {}.dist_dir, but {}.distribution '
100+
'is None'.format(self, self))
103101
return self.distribution.dist_dir
104102

105103
@property
@@ -158,7 +156,7 @@ def prepare_build_dir(self):
158156
with open('project.properties', 'w') as fileh:
159157
fileh.write('target=android-{}'.format(self.ctx.android_api))
160158

161-
def prepare_dist_dir(self, name):
159+
def prepare_dist_dir(self):
162160
ensure_dir(self.dist_dir)
163161

164162
def run_distribute(self):

pythonforandroid/build.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,13 @@ class Context(object):
9292
# in which bootstraps are copied for building
9393
# and recipes are built
9494
build_dir = None
95+
96+
distribution = None
97+
"""The Distribution object representing the current build target location."""
98+
9599
# the Android project folder where everything ends up
96100
dist_dir = None
101+
97102
# where Android libs are cached after build
98103
# but before being placed in dists
99104
libs_dir = None
@@ -106,7 +111,6 @@ class Context(object):
106111

107112
ndk_platform = None # the ndk platform directory
108113

109-
dist_name = None # should be deprecated in favour of self.dist.dist_name
110114
bootstrap = None
111115
bootstrap_build_dir = None
112116

@@ -485,9 +489,8 @@ def prepare_bootstrap(self, bs):
485489
self.bootstrap.prepare_build_dir()
486490
self.bootstrap_build_dir = self.bootstrap.build_dir
487491

488-
def prepare_dist(self, name):
489-
self.dist_name = name
490-
self.bootstrap.prepare_dist_dir(self.dist_name)
492+
def prepare_dist(self):
493+
self.bootstrap.prepare_dist_dir()
491494

492495
def get_site_packages_dir(self, arch=None):
493496
'''Returns the location of site-packages in the python-install build

pythonforandroid/distribution.py

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Distribution(object):
2424
ndk_api = None
2525

2626
archs = []
27-
'''The arch targets that the dist is built for.'''
27+
'''The names of the arch targets that the dist is built for.'''
2828

2929
recipes = []
3030

@@ -42,12 +42,19 @@ def __repr__(self):
4242
return str(self)
4343

4444
@classmethod
45-
def get_distribution(cls, ctx, name=None, recipes=[],
46-
ndk_api=None,
47-
force_build=False,
48-
extra_dist_dirs=[],
49-
require_perfect_match=False,
50-
allow_replace_dist=True):
45+
def get_distribution(
46+
cls,
47+
ctx,
48+
*,
49+
arch_name, # required keyword argument: there is no sensible default
50+
name=None,
51+
recipes=[],
52+
ndk_api=None,
53+
force_build=False,
54+
extra_dist_dirs=[],
55+
require_perfect_match=False,
56+
allow_replace_dist=True
57+
):
5158
'''Takes information about the distribution, and decides what kind of
5259
distribution it will be.
5360
@@ -60,6 +67,12 @@ def get_distribution(cls, ctx, name=None, recipes=[],
6067
name : str
6168
The name of the distribution. If a dist with this name already '
6269
exists, it will be used.
70+
ndk_api : int
71+
The NDK API to compile against, included in the dist because it cannot
72+
be changed later during APK packaging.
73+
arch_name : str
74+
The target architecture name to compile against, included in the dist because
75+
it cannot be changed later during APK packaging.
6376
recipes : list
6477
The recipes that the distribution must contain.
6578
force_download: bool
@@ -77,17 +90,24 @@ def get_distribution(cls, ctx, name=None, recipes=[],
7790
a new one with the current requirements.
7891
'''
7992

80-
existing_dists = Distribution.get_distributions(ctx)
93+
possible_dists = Distribution.get_distributions(ctx)
8194

82-
possible_dists = existing_dists
95+
# Will hold dists that would be built in the same folder as an existing dist
96+
folder_match_dist = None
8397

84-
name_match_dist = None
85-
86-
# 0) Check if a dist with that name already exists
98+
# 0) Check if a dist with that name and architecture already exists
8799
if name is not None and name:
88-
possible_dists = [d for d in possible_dists if d.name == name]
100+
possible_dists = [
101+
d for d in possible_dists if
102+
(d.name == name) and (arch_name in d.archs)]
103+
89104
if possible_dists:
90-
name_match_dist = possible_dists[0]
105+
# There should only be one folder with a given dist name *and* arch.
106+
# We could check that here, but for compatibility let's let it slide
107+
# and just record the details of one of them. We only use this data to
108+
# possibly fail the build later, so it doesn't really matter if there
109+
# was more than one clash.
110+
folder_match_dist = possible_dists[0]
91111

92112
# 1) Check if any existing dists meet the requirements
93113
_possible_dists = []
@@ -110,35 +130,37 @@ def get_distribution(cls, ctx, name=None, recipes=[],
110130
else:
111131
info('No existing dists meet the given requirements!')
112132

113-
# If any dist has perfect recipes and ndk API, return it
133+
# If any dist has perfect recipes, arch and NDK API, return it
114134
for dist in possible_dists:
115135
if force_build:
116136
continue
117137
if ndk_api is not None and dist.ndk_api != ndk_api:
118138
continue
139+
if arch_name is not None and arch_name not in dist.archs:
140+
continue
119141
if (set(dist.recipes) == set(recipes) or
120142
(set(recipes).issubset(set(dist.recipes)) and
121143
not require_perfect_match)):
122144
info_notify('{} has compatible recipes, using this one'
123145
.format(dist.name))
124146
return dist
125147

126-
assert len(possible_dists) < 2
127-
128148
# If there was a name match but we didn't already choose it,
129149
# then the existing dist is incompatible with the requested
130150
# configuration and the build cannot continue
131-
if name_match_dist is not None and not allow_replace_dist:
151+
if folder_match_dist is not None and not allow_replace_dist:
132152
raise BuildInterruptingException(
133153
'Asked for dist with name {name} with recipes ({req_recipes}) and '
134154
'NDK API {req_ndk_api}, but a dist '
135155
'with this name already exists and has either incompatible recipes '
136156
'({dist_recipes}) or NDK API {dist_ndk_api}'.format(
137157
name=name,
138158
req_ndk_api=ndk_api,
139-
dist_ndk_api=name_match_dist.ndk_api,
159+
dist_ndk_api=folder_match_dist.ndk_api,
140160
req_recipes=', '.join(recipes),
141-
dist_recipes=', '.join(name_match_dist.recipes)))
161+
dist_recipes=', '.join(folder_match_dist.recipes)))
162+
163+
assert len(possible_dists) < 2
142164

143165
# If we got this far, we need to build a new dist
144166
dist = Distribution(ctx)
@@ -152,9 +174,16 @@ def get_distribution(cls, ctx, name=None, recipes=[],
152174
name = filen.format(i)
153175

154176
dist.name = name
155-
dist.dist_dir = join(ctx.dist_dir, dist.name)
177+
dist.dist_dir = join(
178+
ctx.dist_dir,
179+
generate_dist_folder_name(
180+
name,
181+
[arch_name] if arch_name is not None else None,
182+
)
183+
)
156184
dist.recipes = recipes
157185
dist.ndk_api = ctx.ndk_api
186+
dist.archs = [arch_name]
158187

159188
return dist
160189

@@ -182,7 +211,7 @@ def get_distributions(cls, ctx, extra_dist_dirs=[]):
182211
with open(join(folder, 'dist_info.json')) as fileh:
183212
dist_info = json.load(fileh)
184213
dist = cls(ctx)
185-
dist.name = folder.split('/')[-1]
214+
dist.name = dist_info['dist_name']
186215
dist.dist_dir = folder
187216
dist.needs_build = False
188217
dist.recipes = dist_info['recipes']
@@ -210,7 +239,7 @@ def save_info(self, dirn):
210239
with current_directory(dirn):
211240
info('Saving distribution info')
212241
with open('dist_info.json', 'w') as fileh:
213-
json.dump({'dist_name': self.ctx.dist_name,
242+
json.dump({'dist_name': self.name,
214243
'bootstrap': self.ctx.bootstrap.name,
215244
'archs': [arch.arch for arch in self.ctx.archs],
216245
'ndk_api': self.ctx.ndk_api,
@@ -236,3 +265,23 @@ def pretty_log_dists(dists, log_func=info):
236265

237266
for line in infos:
238267
log_func('\t' + line)
268+
269+
270+
def generate_dist_folder_name(base_dist_name, arch_names=None):
271+
"""Generate the distribution folder name to use, based on a
272+
combination of the input arguments.
273+
274+
Parameters
275+
----------
276+
base_dist_name : str
277+
The core distribution identifier string
278+
arch_names : list of str
279+
The architecture compile targets
280+
"""
281+
if arch_names is None:
282+
arch_names = ["no_arch_specified"]
283+
284+
return '{}__{}'.format(
285+
base_dist_name,
286+
'_'.join(arch_names)
287+
)

pythonforandroid/python.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,11 @@ def create_python_bundle(self, dirn, arch):
341341
python_lib_name = 'libpython' + self.major_minor_version_string
342342
if self.major_minor_version_string[0] == '3':
343343
python_lib_name += 'm'
344-
shprint(sh.cp, join(python_build_dir, python_lib_name + '.so'),
345-
join(self.ctx.dist_dir, self.ctx.dist_name, 'libs', arch.arch))
344+
shprint(
345+
sh.cp,
346+
join(python_build_dir, python_lib_name + '.so'),
347+
join(self.ctx.bootstrap.dist_dir, 'libs', arch.arch)
348+
)
346349

347350
info('Renaming .so files to reflect cross-compile')
348351
self.reduce_object_file_names(join(dirn, 'site-packages'))

pythonforandroid/toolchain.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ def check_python_dependencies():
101101
toolchain_dir = dirname(__file__)
102102
sys.path.insert(0, join(toolchain_dir, "tools", "external"))
103103

104+
APK_SUFFIX = '.apk'
105+
104106

105107
def add_boolean_option(parser, names, no_names=None,
106108
default=True, dest=None, description=None):
@@ -163,6 +165,7 @@ def dist_from_args(ctx, args):
163165
ctx,
164166
name=args.dist_name,
165167
recipes=split_argument_list(args.requirements),
168+
arch_name=args.arch,
166169
ndk_api=args.ndk_api,
167170
force_build=args.force_build,
168171
require_perfect_match=args.require_perfect_match,
@@ -195,10 +198,10 @@ def build_dist_from_args(ctx, dist, args):
195198
info('Dist will also contain modules ({}) installed from pip'.format(
196199
', '.join(ctx.python_modules)))
197200

198-
ctx.dist_name = bs.distribution.name
201+
ctx.distribution = dist
199202
ctx.prepare_bootstrap(bs)
200203
if dist.needs_build:
201-
ctx.prepare_dist(ctx.dist_name)
204+
ctx.prepare_dist()
202205

203206
build_recipes(build_order, python_modules, ctx,
204207
getattr(args, "private", None),
@@ -211,7 +214,7 @@ def build_dist_from_args(ctx, dist, args):
211214

212215
info_main('# Your distribution was created successfully, exiting.')
213216
info('Dist can be found at (for now) {}'
214-
.format(join(ctx.dist_dir, ctx.dist_name)))
217+
.format(join(ctx.dist_dir, ctx.distribution.dist_dir)))
215218

216219

217220
def split_argument_list(l):
@@ -304,7 +307,7 @@ def __init__(self):
304307
'(default: {})'.format(default_storage_dir)))
305308

306309
generic_parser.add_argument(
307-
'--arch', help='The archs to build for, separated by commas.',
310+
'--arch', help='The arch to build for.',
308311
default='armeabi-v7a')
309312

310313
# Options for specifying the Distribution
@@ -912,6 +915,7 @@ def export_dist(self, args):
912915
def _dist(self):
913916
ctx = self.ctx
914917
dist = dist_from_args(ctx, self.args)
918+
ctx.distribution = dist
915919
return dist
916920

917921
@require_prebuilt_dist
@@ -1062,9 +1066,9 @@ def apk(self, args):
10621066
info_main('# Found APK file: {}'.format(apk_file))
10631067
if apk_add_version:
10641068
info('# Add version number to APK')
1065-
apk_name, apk_suffix = basename(apk_file).split("-", 1)
1069+
apk_name = basename(apk_file)[:-len(APK_SUFFIX)]
10661070
apk_file_dest = "{}-{}-{}".format(
1067-
apk_name, build_args.version, apk_suffix)
1071+
apk_name, build_args.version, APK_SUFFIX)
10681072
info('# APK renamed to {}'.format(apk_file_dest))
10691073
shprint(sh.cp, apk_file, apk_file_dest)
10701074
else:

tests/recipes/recipe_ctx.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ class RecipeCtx:
2626
contain the target recipe to test as well as a python recipe."""
2727
recipe_build_order = []
2828
"""A recipe_build_order which should take into account the recipe we want
29-
to test as well as the possible dependant recipes"""
29+
to test as well as the possible dependent recipes"""
30+
31+
TEST_ARCH = 'arm64-v8a'
3032

3133
def setUp(self):
3234
self.ctx = Context()
@@ -37,7 +39,7 @@ def setUp(self):
3739
self.ctx.setup_dirs(os.getcwd())
3840
self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx)
3941
self.ctx.bootstrap.distribution = Distribution.get_distribution(
40-
self.ctx, name="sdl2", recipes=self.recipes
42+
self.ctx, name="sdl2", recipes=self.recipes, arch_name=self.TEST_ARCH,
4143
)
4244
self.ctx.recipe_build_order = self.recipe_build_order
4345
self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)

tests/test_archs.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class ArchSetUpBaseClass(object):
5050
ctx = None
5151
expected_compiler = ""
5252

53+
TEST_ARCH = 'armeabi-v7a'
54+
5355
def setUp(self):
5456
self.ctx = Context()
5557
self.ctx.ndk_api = 21
@@ -59,7 +61,10 @@ def setUp(self):
5961
self.ctx.setup_dirs(os.getcwd())
6062
self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx)
6163
self.ctx.bootstrap.distribution = Distribution.get_distribution(
62-
self.ctx, name="sdl2", recipes=["python3", "kivy"]
64+
self.ctx,
65+
name="sdl2",
66+
recipes=["python3", "kivy"],
67+
arch_name=self.TEST_ARCH,
6368
)
6469
self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)
6570
# Here we define the expected compiler, which, as per ndk >= r19,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy