Skip to content

Commit c073dae

Browse files
committed
Moved Bootstrap to bootstrap.py
1 parent 807a94e commit c073dae

File tree

4 files changed

+257
-250
lines changed

4 files changed

+257
-250
lines changed

pythonforandroid/archs.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@ def include_dirs(self):
3131
for d in self.ctx.include_dirs]
3232

3333
def get_env(self):
34-
include_dirs = [
35-
"-I{}/{}".format(
36-
self.ctx.include_dir,
37-
d.format(arch=self))
38-
for d in self.ctx.include_dirs]
39-
4034
env = {}
4135

4236
env["CFLAGS"] = " ".join([

pythonforandroid/bootstrap.py

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
from os.path import (join, dirname, isdir, splitext, basename)
2+
from os import listdir
3+
import sh
4+
import glob
5+
import json
6+
import importlib
7+
8+
from pythonforandroid.logger import (warning, shprint, info, logger,
9+
debug)
10+
from pythonforandroid.util import (current_directory, ensure_dir,
11+
temp_directory, which)
12+
from pythonforandroid.recipebases import Recipe
13+
14+
15+
class Bootstrap(object):
16+
'''An Android project template, containing recipe stuff for
17+
compilation and templated fields for APK info.
18+
'''
19+
name = ''
20+
jni_subdir = '/jni'
21+
ctx = None
22+
23+
bootstrap_dir = None
24+
25+
build_dir = None
26+
dist_dir = None
27+
dist_name = None
28+
distribution = None
29+
30+
recipe_depends = []
31+
32+
can_be_chosen_automatically = True
33+
'''Determines whether the bootstrap can be chosen as one that
34+
satisfies user requirements. If False, it will not be returned
35+
from Bootstrap.get_bootstrap_from_recipes.
36+
'''
37+
38+
# Other things a Bootstrap might need to track (maybe separately):
39+
# ndk_main.c
40+
# whitelist.txt
41+
# blacklist.txt
42+
43+
@property
44+
def dist_dir(self):
45+
'''The dist dir at which to place the finished distribution.'''
46+
if self.distribution is None:
47+
warning('Tried to access {}.dist_dir, but {}.distribution '
48+
'is None'.format(self, self))
49+
exit(1)
50+
return self.distribution.dist_dir
51+
52+
@property
53+
def jni_dir(self):
54+
return self.name + self.jni_subdir
55+
56+
def get_build_dir(self):
57+
return join(self.ctx.build_dir, 'bootstrap_builds', self.name)
58+
59+
def get_dist_dir(self, name):
60+
return join(self.ctx.dist_dir, name)
61+
62+
@property
63+
def name(self):
64+
modname = self.__class__.__module__
65+
return modname.split(".", 2)[-1]
66+
67+
def prepare_build_dir(self):
68+
'''Ensure that a build dir exists for the recipe. This same single
69+
dir will be used for building all different archs.'''
70+
self.build_dir = self.get_build_dir()
71+
shprint(sh.cp, '-r',
72+
join(self.bootstrap_dir, 'build'),
73+
# join(self.ctx.root_dir,
74+
# 'bootstrap_templates',
75+
# self.name),
76+
self.build_dir)
77+
with current_directory(self.build_dir):
78+
with open('project.properties', 'w') as fileh:
79+
fileh.write('target=android-{}'.format(self.ctx.android_api))
80+
81+
def prepare_dist_dir(self, name):
82+
# self.dist_dir = self.get_dist_dir(name)
83+
ensure_dir(self.dist_dir)
84+
85+
def run_distribute(self):
86+
# print('Default bootstrap being used doesn\'t know how '
87+
# 'to distribute...failing.')
88+
# exit(1)
89+
with current_directory(self.dist_dir):
90+
info('Saving distribution info')
91+
with open('dist_info.json', 'w') as fileh:
92+
json.dump({'dist_name': self.ctx.dist_name,
93+
'bootstrap': self.ctx.bootstrap.name,
94+
'archs': [arch.arch for arch in self.ctx.archs],
95+
'recipes': self.ctx.recipe_build_order},
96+
fileh)
97+
98+
@classmethod
99+
def list_bootstraps(cls):
100+
'''Find all the available bootstraps and return them.'''
101+
forbidden_dirs = ('__pycache__', )
102+
bootstraps_dir = join(dirname(__file__), 'bootstraps')
103+
for name in listdir(bootstraps_dir):
104+
if name in forbidden_dirs:
105+
continue
106+
filen = join(bootstraps_dir, name)
107+
if isdir(filen):
108+
yield name
109+
110+
@classmethod
111+
def get_bootstrap_from_recipes(cls, recipes, ctx):
112+
'''Returns a bootstrap whose recipe requirements do not conflict with
113+
the given recipes.'''
114+
info('Trying to find a bootstrap that matches the given recipes.')
115+
bootstraps = [cls.get_bootstrap(name, ctx)
116+
for name in cls.list_bootstraps()]
117+
acceptable_bootstraps = []
118+
for bs in bootstraps:
119+
ok = True
120+
if not bs.can_be_chosen_automatically:
121+
ok = False
122+
for recipe in bs.recipe_depends:
123+
recipe = Recipe.get_recipe(recipe, ctx)
124+
if any([conflict in recipes for conflict in recipe.conflicts]):
125+
ok = False
126+
break
127+
for recipe in recipes:
128+
recipe = Recipe.get_recipe(recipe, ctx)
129+
if any([conflict in bs.recipe_depends
130+
for conflict in recipe.conflicts]):
131+
ok = False
132+
break
133+
if ok:
134+
acceptable_bootstraps.append(bs)
135+
info('Found {} acceptable bootstraps: {}'.format(
136+
len(acceptable_bootstraps),
137+
[bs.name for bs in acceptable_bootstraps]))
138+
if acceptable_bootstraps:
139+
info('Using the first of these: {}'
140+
.format(acceptable_bootstraps[0].name))
141+
return acceptable_bootstraps[0]
142+
return None
143+
144+
@classmethod
145+
def get_bootstrap(cls, name, ctx):
146+
'''Returns an instance of a bootstrap with the given name.
147+
148+
This is the only way you should access a bootstrap class, as
149+
it sets the bootstrap directory correctly.
150+
'''
151+
# AND: This method will need to check user dirs, and access
152+
# bootstraps in a slightly different way
153+
if name is None:
154+
return None
155+
if not hasattr(cls, 'bootstraps'):
156+
cls.bootstraps = {}
157+
if name in cls.bootstraps:
158+
return cls.bootstraps[name]
159+
mod = importlib.import_module('pythonforandroid.bootstraps.{}'
160+
.format(name))
161+
if len(logger.handlers) > 1:
162+
logger.removeHandler(logger.handlers[1])
163+
bootstrap = mod.bootstrap
164+
bootstrap.bootstrap_dir = join(ctx.root_dir, 'bootstraps', name)
165+
bootstrap.ctx = ctx
166+
return bootstrap
167+
168+
def distribute_libs(self, arch, src_dirs, wildcard='*'):
169+
'''Copy existing arch libs from build dirs to current dist dir.'''
170+
info('Copying libs')
171+
tgt_dir = join('libs', arch.arch)
172+
ensure_dir(tgt_dir)
173+
for src_dir in src_dirs:
174+
for lib in glob.glob(join(src_dir, wildcard)):
175+
shprint(sh.cp, '-a', lib, tgt_dir)
176+
177+
def distribute_javaclasses(self, javaclass_dir):
178+
'''Copy existing javaclasses from build dir to current dist dir.'''
179+
info('Copying java files')
180+
for filename in glob.glob(javaclass_dir):
181+
shprint(sh.cp, '-a', filename, 'src')
182+
183+
def distribute_aars(self, arch):
184+
'''Process existing .aar bundles and copy to current dist dir.'''
185+
info('Unpacking aars')
186+
for aar in glob.glob(join(self.ctx.aars_dir, '*.aar')):
187+
self._unpack_aar(aar, arch)
188+
189+
def _unpack_aar(self, aar, arch):
190+
'''Unpack content of .aar bundle and copy to current dist dir.'''
191+
with temp_directory() as temp_dir:
192+
name = splitext(basename(aar))[0]
193+
jar_name = name + '.jar'
194+
info("unpack {} aar".format(name))
195+
debug(" from {}".format(aar))
196+
debug(" to {}".format(temp_dir))
197+
shprint(sh.unzip, '-o', aar, '-d', temp_dir)
198+
199+
jar_src = join(temp_dir, 'classes.jar')
200+
jar_tgt = join('libs', jar_name)
201+
debug("copy {} jar".format(name))
202+
debug(" from {}".format(jar_src))
203+
debug(" to {}".format(jar_tgt))
204+
ensure_dir('libs')
205+
shprint(sh.cp, '-a', jar_src, jar_tgt)
206+
207+
so_src_dir = join(temp_dir, 'jni', arch.arch)
208+
so_tgt_dir = join('libs', arch.arch)
209+
debug("copy {} .so".format(name))
210+
debug(" from {}".format(so_src_dir))
211+
debug(" to {}".format(so_tgt_dir))
212+
ensure_dir(so_tgt_dir)
213+
so_files = glob.glob(join(so_src_dir, '*.so'))
214+
for f in so_files:
215+
shprint(sh.cp, '-a', f, so_tgt_dir)
216+
217+
def strip_libraries(self, arch):
218+
info('Stripping libraries')
219+
env = arch.get_env()
220+
strip = which('arm-linux-androideabi-strip', env['PATH'])
221+
if strip is None:
222+
warning('Can\'t find strip in PATH...')
223+
return
224+
strip = sh.Command(strip)
225+
filens = shprint(sh.find, join(self.dist_dir, 'private'),
226+
join(self.dist_dir, 'libs'),
227+
'-iname', '*.so', _env=env).stdout.decode('utf-8')
228+
logger.info('Stripping libraries in private dir')
229+
for filen in filens.split('\n'):
230+
try:
231+
strip(filen, _env=env)
232+
except sh.ErrorReturnCode_1:
233+
logger.debug('Failed to strip ' + filen)

0 commit comments

Comments
 (0)