Skip to content

Commit b929995

Browse files
committed
Moved Distribution to its own file
1 parent c073dae commit b929995

File tree

2 files changed

+231
-224
lines changed

2 files changed

+231
-224
lines changed

pythonforandroid/distribution.py

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
from os.path import exists, join
2+
import glob
3+
import json
4+
5+
from pythonforandroid.logger import (info, info_notify, warning,
6+
Err_Style, Err_Fore)
7+
from pythonforandroid.util import current_directory
8+
9+
10+
class Distribution(object):
11+
'''State container for information about a distribution (i.e. an
12+
Android project).
13+
14+
This is separate from a Bootstrap because the Bootstrap is
15+
concerned with building and populating the dist directory, whereas
16+
the dist itself could also come from e.g. a binary download.
17+
'''
18+
ctx = None
19+
20+
name = None # A name identifying the dist. May not be None.
21+
needs_build = False # Whether the dist needs compiling
22+
url = None
23+
dist_dir = None # Where the dist dir ultimately is. Should not be None.
24+
25+
archs = []
26+
'''The arch targets that the dist is built for.'''
27+
28+
recipes = []
29+
30+
description = '' # A long description
31+
32+
def __init__(self, ctx):
33+
self.ctx = ctx
34+
35+
def __str__(self):
36+
return '<Distribution: name {} with recipes ({})>'.format(
37+
# self.name, ', '.join([recipe.name for recipe in self.recipes]))
38+
self.name, ', '.join(self.recipes))
39+
40+
def __repr__(self):
41+
return str(self)
42+
43+
@classmethod
44+
def get_distribution(cls, ctx, name=None, recipes=[], allow_download=True,
45+
force_build=False,
46+
allow_build=True, extra_dist_dirs=[],
47+
require_perfect_match=False):
48+
'''Takes information about the distribution, and decides what kind of
49+
distribution it will be.
50+
51+
If parameters conflict (e.g. a dist with that name already
52+
exists, but doesn't have the right set of recipes),
53+
an error is thrown.
54+
55+
Parameters
56+
----------
57+
name : str
58+
The name of the distribution. If a dist with this name already '
59+
exists, it will be used.
60+
recipes : list
61+
The recipes that the distribution must contain.
62+
allow_download : bool
63+
Whether binary dists may be downloaded.
64+
allow_build : bool
65+
Whether the distribution may be built from scratch if necessary.
66+
This is always False on e.g. Windows.
67+
force_download: bool
68+
If True, only downloaded dists are considered.
69+
force_build : bool
70+
If True, the dist is forced to be built locally.
71+
extra_dist_dirs : list
72+
Any extra directories in which to search for dists.
73+
require_perfect_match : bool
74+
If True, will only match distributions with precisely the
75+
correct set of recipes.
76+
'''
77+
78+
# AND: This whole function is a bit hacky, it needs checking
79+
# properly to make sure it follows logically correct
80+
# possibilities
81+
82+
existing_dists = Distribution.get_distributions(ctx)
83+
84+
needs_build = True # whether the dist needs building, will be returned
85+
86+
possible_dists = existing_dists
87+
88+
# 0) Check if a dist with that name already exists
89+
if name is not None and name:
90+
possible_dists = [d for d in possible_dists if d.name == name]
91+
92+
# 1) Check if any existing dists meet the requirements
93+
_possible_dists = []
94+
for dist in possible_dists:
95+
for recipe in recipes:
96+
if recipe not in dist.recipes:
97+
break
98+
else:
99+
_possible_dists.append(dist)
100+
possible_dists = _possible_dists
101+
102+
if possible_dists:
103+
info('Of the existing distributions, the following meet '
104+
'the given requirements:')
105+
pretty_log_dists(possible_dists)
106+
else:
107+
info('No existing dists meet the given requirements!')
108+
109+
# If any dist has perfect recipes, return it
110+
for dist in possible_dists:
111+
if force_build:
112+
continue
113+
if (set(dist.recipes) == set(recipes) or
114+
(set(recipes).issubset(set(dist.recipes)) and
115+
not require_perfect_match)):
116+
info_notify('{} has compatible recipes, using this one'
117+
.format(dist.name))
118+
return dist
119+
120+
assert len(possible_dists) < 2
121+
122+
if not name and possible_dists:
123+
info('Asked for dist with name {} with recipes ({}), but a dist '
124+
'with this name already exists and has incompatible recipes '
125+
'({})'.format(name, ', '.join(recipes),
126+
', '.join(possible_dists[0].recipes)))
127+
info('No compatible dist found, so exiting.')
128+
exit(1)
129+
130+
# # 2) Check if any downloadable dists meet the requirements
131+
132+
# online_dists = [('testsdl2', ['hostpython2', 'sdl2_image',
133+
# 'sdl2_mixer', 'sdl2_ttf',
134+
# 'python2', 'sdl2',
135+
# 'pyjniussdl2', 'kivysdl2'],
136+
# 'https://github.com/inclement/sdl2-example-dist/archive/master.zip'),
137+
# ]
138+
# _possible_dists = []
139+
# for dist_name, dist_recipes, dist_url in online_dists:
140+
# for recipe in recipes:
141+
# if recipe not in dist_recipes:
142+
# break
143+
# else:
144+
# dist = Distribution(ctx)
145+
# dist.name = dist_name
146+
# dist.url = dist_url
147+
# _possible_dists.append(dist)
148+
# # if _possible_dists
149+
150+
# If we got this far, we need to build a new dist
151+
dist = Distribution(ctx)
152+
dist.needs_build = True
153+
154+
if not name:
155+
filen = 'unnamed_dist_{}'
156+
i = 1
157+
while exists(join(ctx.dist_dir, filen.format(i))):
158+
i += 1
159+
name = filen.format(i)
160+
161+
dist.name = name
162+
dist.dist_dir = join(ctx.dist_dir, dist.name)
163+
dist.recipes = recipes
164+
165+
return dist
166+
167+
@classmethod
168+
def get_distributions(cls, ctx, extra_dist_dirs=[]):
169+
'''Returns all the distributions found locally.'''
170+
if extra_dist_dirs:
171+
warning('extra_dist_dirs argument to get_distributions '
172+
'is not yet implemented')
173+
exit(1)
174+
dist_dir = ctx.dist_dir
175+
folders = glob.glob(join(dist_dir, '*'))
176+
for dir in extra_dist_dirs:
177+
folders.extend(glob.glob(join(dir, '*')))
178+
179+
dists = []
180+
for folder in folders:
181+
if exists(join(folder, 'dist_info.json')):
182+
with open(join(folder, 'dist_info.json')) as fileh:
183+
dist_info = json.load(fileh)
184+
dist = cls(ctx)
185+
dist.name = folder.split('/')[-1]
186+
dist.dist_dir = folder
187+
dist.needs_build = False
188+
dist.recipes = dist_info['recipes']
189+
if 'archs' in dist_info:
190+
dist.archs = dist_info['archs']
191+
dists.append(dist)
192+
return dists
193+
194+
def save_info(self):
195+
'''
196+
Save information about the distribution in its dist_dir.
197+
'''
198+
with current_directory(self.dist_dir):
199+
info('Saving distribution info')
200+
with open('dist_info.json', 'w') as fileh:
201+
json.dump({'dist_name': self.name,
202+
'archs': [arch.arch for arch in self.ctx.archs],
203+
'recipes': self.ctx.recipe_build_order},
204+
fileh)
205+
206+
def load_info(self):
207+
'''Load information about the dist from the info file that p4a
208+
automatically creates.'''
209+
with current_directory(self.dist_dir):
210+
filen = 'dist_info.json'
211+
if not exists(filen):
212+
return None
213+
with open('dist_info.json', 'r') as fileh:
214+
dist_info = json.load(fileh)
215+
return dist_info
216+
217+
218+
def pretty_log_dists(dists, log_func=info):
219+
infos = []
220+
for dist in dists:
221+
infos.append('{Fore.GREEN}{Style.BRIGHT}{name}{Style.RESET_ALL}: '
222+
'includes recipes ({Fore.GREEN}{recipes}'
223+
'{Style.RESET_ALL}), built for archs ({Fore.BLUE}'
224+
'{archs}{Style.RESET_ALL})'.format(
225+
name=dist.name, recipes=', '.join(dist.recipes),
226+
archs=', '.join(dist.archs) if dist.archs else 'UNKNOWN',
227+
Fore=Err_Fore, Style=Err_Style))
228+
229+
for line in infos:
230+
log_func('\t' + line)

0 commit comments

Comments
 (0)