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