3
3
from itertools import product
4
4
from sys import exit
5
5
6
- from pythonforandroid .logger import (info , info_notify , warning , error )
6
+ from pythonforandroid .logger import (info , warning , error )
7
7
from pythonforandroid .recipe import Recipe
8
8
from pythonforandroid .bootstrap import Bootstrap
9
9
10
10
11
- # class Graph(object):
12
- # # Taken from the old python-for-android/depsort
13
- # # Modified to include alternative dependencies
14
- # def __init__(self):
15
- # # `graph`: dict that maps each package to a set of its dependencies.
16
- # self.graphs = [{}]
17
- # # self.graph = {}
18
-
19
- # def remove_redundant_graphs(self):
20
- # '''Removes possible graphs if they are equivalent to others.'''
21
- # graphs = self.graphs
22
- # # Walk the list backwards so that popping elements doesn't
23
- # # mess up indexing.
24
-
25
- # # n.b. no need to test graph 0 as it will have been tested against
26
- # # all others by the time we get to it
27
- # for i in range(len(graphs) - 1, 0, -1):
28
- # graph = graphs[i]
29
-
30
- # # test graph i against all graphs 0 to i-1
31
- # for j in range(0, i):
32
- # comparison_graph = graphs[j]
33
-
34
- # if set(comparison_graph.keys()) == set(graph.keys()):
35
- # # graph[i] == graph[j]
36
- # # so remove graph[i] and continue on to testing graph[i-1]
37
- # graphs.pop(i)
38
- # break
39
-
40
- # def add(self, dependent, dependency):
41
- # """Add a dependency relationship to the graph"""
42
- # if isinstance(dependency, (tuple, list)):
43
- # for graph in self.graphs[:]:
44
- # for dep in dependency[1:]:
45
- # new_graph = deepcopy(graph)
46
- # self._add(new_graph, dependent, dep)
47
- # self.graphs.append(new_graph)
48
- # self._add(graph, dependent, dependency[0])
49
- # else:
50
- # for graph in self.graphs:
51
- # self._add(graph, dependent, dependency)
52
- # self.remove_redundant_graphs()
53
-
54
- # def _add(self, graph, dependent, dependency):
55
- # '''Add a dependency relationship to a specific graph, where dependency
56
- # must be a single dependency, not a list or tuple.
57
- # '''
58
- # graph.setdefault(dependent, set())
59
- # graph.setdefault(dependency, set())
60
- # if dependent != dependency:
61
- # graph[dependent].add(dependency)
62
-
63
- # def conflicts(self, conflict):
64
- # graphs = self.graphs
65
- # initial_num = len(graphs)
66
- # for i in range(len(graphs)):
67
- # graph = graphs[initial_num - 1 - i]
68
- # if conflict in graph:
69
- # graphs.pop(initial_num - 1 - i)
70
- # return len(graphs) == 0
71
-
72
- # def remove_remaining_conflicts(self, ctx):
73
- # # It's unpleasant to have to pass ctx as an argument...
74
- # '''Checks all possible graphs for conflicts that have arisen during
75
- # the additon of alternative repice branches, as these are not checked
76
- # for conflicts at the time.'''
77
- # new_graphs = []
78
- # for i, graph in enumerate(self.graphs):
79
- # for name in graph.keys():
80
- # recipe = Recipe.get_recipe(name, ctx)
81
- # if any([c in graph for c in recipe.conflicts]):
82
- # break
83
- # else:
84
- # new_graphs.append(graph)
85
- # self.graphs = new_graphs
86
-
87
- # def add_optional(self, dependent, dependency):
88
- # """Add an optional (ordering only) dependency relationship to the graph
89
-
90
- # Only call this after all mandatory requirements are added
91
- # """
92
- # for graph in self.graphs:
93
- # if dependent in graph and dependency in graph:
94
- # self._add(graph, dependent, dependency)
95
-
96
- # def find_order(self, index=0):
97
- # """Do a topological sort on a dependency graph
98
-
99
- # :Parameters:
100
- # :Returns:
101
- # iterator, sorted items form first to last
102
- # """
103
- # graph = self.graphs[index]
104
- # graph = dict((k, set(v)) for k, v in graph.items())
105
- # while graph:
106
- # # Find all items without a parent
107
- # leftmost = [l for l, s in graph.items() if not s]
108
- # if not leftmost:
109
- # raise ValueError('Dependency cycle detected! %s' % graph)
110
- # # If there is more than one, sort them for predictable order
111
- # leftmost.sort()
112
- # for result in leftmost:
113
- # # Yield and remove them from the graph
114
- # yield result
115
- # graph.pop(result)
116
- # for bset in graph.values():
117
- # bset.discard(result)
118
-
119
-
120
11
class RecipeOrder (dict ):
121
12
122
13
def __init__ (self , ctx ):
@@ -129,11 +20,12 @@ def conflicts(self, name):
129
20
conflicts = recipe .conflicts
130
21
except IOError :
131
22
conflicts = []
132
-
23
+
133
24
if any ([c in self for c in conflicts ]):
134
25
return True
135
26
return False
136
27
28
+
137
29
def recursively_collect_orders (name , ctx , orders = []):
138
30
'''For each possible recipe ordering, try to add the new recipe name
139
31
to that order. Recursively do the same thing with all the
@@ -146,7 +38,8 @@ def recursively_collect_orders(name, ctx, orders=[]):
146
38
dependencies = []
147
39
else :
148
40
# make all dependencies into lists so that product will work
149
- dependencies = [([dependency ] if not isinstance (dependency , (list , tuple ))
41
+ dependencies = [([dependency ] if not isinstance (
42
+ dependency , (list , tuple ))
150
43
else dependency ) for dependency in recipe .depends ]
151
44
if recipe .conflicts is None :
152
45
conflicts = []
@@ -200,7 +93,7 @@ def find_order(graph):
200
93
graph .pop (result )
201
94
for bset in graph .values ():
202
95
bset .discard (result )
203
-
96
+
204
97
205
98
def get_recipe_order_and_bootstrap (ctx , names , bs = None ):
206
99
recipes_to_load = set (names )
@@ -226,7 +119,8 @@ def get_recipe_order_and_bootstrap(ctx, names, bs=None):
226
119
try :
227
120
order = find_order (possible_order )
228
121
except ValueError : # a circular dependency was found
229
- info ('Circular dependency found in graph {}, skipping it.' .format (possible_order ))
122
+ info ('Circular dependency found in graph {}, skipping it.' .format (
123
+ possible_order ))
230
124
continue
231
125
except :
232
126
warning ('Failed to import recipe named {}; the recipe exists '
@@ -241,7 +135,8 @@ def get_recipe_order_and_bootstrap(ctx, names, bs=None):
241
135
242
136
if not orders :
243
137
error ('Didn\' t find any valid dependency graphs.' )
244
- error ('This means that some of your requirements pull in conflicting dependencies.' )
138
+ error ('This means that some of your requirements pull in '
139
+ 'conflicting dependencies.' )
245
140
error ('Exiting.' )
246
141
exit (1 )
247
142
# It would be better to check against possible orders other
@@ -258,138 +153,18 @@ def get_recipe_order_and_bootstrap(ctx, names, bs=None):
258
153
259
154
if bs is None :
260
155
bs = Bootstrap .get_bootstrap_from_recipes (chosen_order , ctx )
261
- recipes , python_modules , bs = get_recipe_order_and_bootstrap (ctx , chosen_order , bs = bs )
156
+ recipes , python_modules , bs = get_recipe_order_and_bootstrap (
157
+ ctx , chosen_order , bs = bs )
262
158
else :
263
159
# check if each requirement has a recipe
264
160
recipes = []
265
161
python_modules = []
266
162
for name in chosen_order :
267
163
try :
268
- recipe = Recipe .get_recipe (name , ctx )
164
+ Recipe .get_recipe (name , ctx )
269
165
except IOError :
270
166
python_modules .append (name )
271
167
else :
272
168
recipes .append (name )
273
169
274
170
return recipes , python_modules , bs
275
-
276
-
277
- # def get_recipe_order_and_bootstrap(ctx, names, bs=None):
278
- # '''Takes a list of recipe names and (optionally) a bootstrap. Then
279
- # works out the dependency graph (including bootstrap recipes if
280
- # necessary). Finally, if no bootstrap was initially selected,
281
- # chooses one that supports all the recipes.
282
- # '''
283
- # graph = Graph()
284
- # recipes_to_load = set(names)
285
- # if bs is not None and bs.recipe_depends:
286
- # info_notify('Bootstrap requires recipes {}'.format(bs.recipe_depends))
287
- # recipes_to_load = recipes_to_load.union(set(bs.recipe_depends))
288
- # recipes_to_load = list(recipes_to_load)
289
- # recipe_loaded = []
290
- # python_modules = []
291
- # print('recipes_to_load', recipes_to_load)
292
- # while recipes_to_load:
293
- # name = recipes_to_load.pop(0)
294
- # if name in recipe_loaded or isinstance(name, (list, tuple)):
295
- # continue
296
- # try:
297
- # recipe = Recipe.get_recipe(name, ctx)
298
- # except IOError:
299
- # info('No recipe named {}; will attempt to install with pip'
300
- # .format(name))
301
- # python_modules.append(name)
302
- # continue
303
- # except (KeyboardInterrupt, SystemExit):
304
- # raise
305
- # except:
306
- # warning('Failed to import recipe named {}; the recipe exists '
307
- # 'but appears broken.'.format(name))
308
- # warning('Exception was:')
309
- # raise
310
- # graph.add(name, name)
311
- # info('Loaded recipe {} (depends on {}{})'.format(
312
- # name, recipe.depends,
313
- # ', conflicts {}'.format(recipe.conflicts) if recipe.conflicts
314
- # else ''))
315
- # for depend in recipe.depends:
316
- # graph.add(name, depend)
317
- # recipes_to_load += recipe.depends
318
- # for conflict in recipe.conflicts:
319
- # if graph.conflicts(conflict):
320
- # warning(
321
- # ('{} conflicts with {}, but both have been '
322
- # 'included or pulled into the requirements.'
323
- # .format(recipe.name, conflict)))
324
- # warning(
325
- # 'Due to this conflict the build cannot continue, exiting.')
326
- # exit(1)
327
- # python_modules += recipe.python_depends
328
- # recipe_loaded.append(name)
329
- # graph.remove_remaining_conflicts(ctx)
330
- # if len(graph.graphs) > 1:
331
- # info('Found multiple valid recipe sets:')
332
- # for g in graph.graphs:
333
- # info(' {}'.format(g.keys()))
334
- # info_notify('Using the first of these: {}'
335
- # .format(graph.graphs[0].keys()))
336
- # elif len(graph.graphs) == 0:
337
- # warning('Didn\'t find any valid dependency graphs, exiting.')
338
- # exit(1)
339
- # else:
340
- # info('Found a single valid recipe set (this is good)')
341
-
342
- # build_order = list(graph.find_order(0))
343
- # if bs is None: # It would be better to check against possible
344
- # # orders other than the first one, but in practice
345
- # # there will rarely be clashes, and the user can
346
- # # specify more parameters if necessary to resolve
347
- # # them.
348
- # bs = Bootstrap.get_bootstrap_from_recipes(build_order, ctx)
349
- # if bs is None:
350
- # info('Could not find a bootstrap compatible with the '
351
- # 'required recipes.')
352
- # info('If you think such a combination should exist, try '
353
- # 'specifying the bootstrap manually with --bootstrap.')
354
- # exit(1)
355
- # info('{} bootstrap appears compatible with the required recipes.'
356
- # .format(bs.name))
357
- # info('Checking this...')
358
- # recipes_to_load = bs.recipe_depends
359
- # # This code repeats the code from earlier! Should move to a function:
360
- # while recipes_to_load:
361
- # name = recipes_to_load.pop(0)
362
- # if name in recipe_loaded or isinstance(name, (list, tuple)):
363
- # continue
364
- # try:
365
- # recipe = Recipe.get_recipe(name, ctx)
366
- # except ImportError:
367
- # info('No recipe named {}; will attempt to install with pip'
368
- # .format(name))
369
- # python_modules.append(name)
370
- # continue
371
- # graph.add(name, name)
372
- # info('Loaded recipe {} (depends on {}{})'.format(
373
- # name, recipe.depends,
374
- # ', conflicts {}'.format(recipe.conflicts) if recipe.conflicts
375
- # else ''))
376
- # for depend in recipe.depends:
377
- # graph.add(name, depend)
378
- # recipes_to_load += recipe.depends
379
- # for conflict in recipe.conflicts:
380
- # if graph.conflicts(conflict):
381
- # warning(
382
- # ('{} conflicts with {}, but both have been '
383
- # 'included or pulled into the requirements.'
384
- # .format(recipe.name, conflict)))
385
- # warning('Due to this conflict the build cannot continue, '
386
- # 'exiting.')
387
- # exit(1)
388
- # recipe_loaded.append(name)
389
- # graph.remove_remaining_conflicts(ctx)
390
- # build_order = list(graph.find_order(0))
391
- # build_order, python_modules, bs = get_recipe_order_and_bootstrap(
392
- # ctx, build_order + python_modules, bs)
393
- # return build_order, python_modules, bs
394
-
395
- # # Do a final check that the new bs doesn't pull in any conflicts
0 commit comments