33from itertools import product
44from sys import exit
55
6- from pythonforandroid .logger import (info , info_notify , warning , error )
6+ from pythonforandroid .logger import (info , warning , error )
77from pythonforandroid .recipe import Recipe
88from pythonforandroid .bootstrap import Bootstrap
99
1010
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-
12011class RecipeOrder (dict ):
12112
12213 def __init__ (self , ctx ):
@@ -129,11 +20,12 @@ def conflicts(self, name):
12920 conflicts = recipe .conflicts
13021 except IOError :
13122 conflicts = []
132-
23+
13324 if any ([c in self for c in conflicts ]):
13425 return True
13526 return False
13627
28+
13729def recursively_collect_orders (name , ctx , orders = []):
13830 '''For each possible recipe ordering, try to add the new recipe name
13931 to that order. Recursively do the same thing with all the
@@ -146,7 +38,8 @@ def recursively_collect_orders(name, ctx, orders=[]):
14638 dependencies = []
14739 else :
14840 # 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 ))
15043 else dependency ) for dependency in recipe .depends ]
15144 if recipe .conflicts is None :
15245 conflicts = []
@@ -200,7 +93,7 @@ def find_order(graph):
20093 graph .pop (result )
20194 for bset in graph .values ():
20295 bset .discard (result )
203-
96+
20497
20598def get_recipe_order_and_bootstrap (ctx , names , bs = None ):
20699 recipes_to_load = set (names )
@@ -226,7 +119,8 @@ def get_recipe_order_and_bootstrap(ctx, names, bs=None):
226119 try :
227120 order = find_order (possible_order )
228121 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 ))
230124 continue
231125 except :
232126 warning ('Failed to import recipe named {}; the recipe exists '
@@ -241,7 +135,8 @@ def get_recipe_order_and_bootstrap(ctx, names, bs=None):
241135
242136 if not orders :
243137 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.' )
245140 error ('Exiting.' )
246141 exit (1 )
247142 # It would be better to check against possible orders other
@@ -258,138 +153,18 @@ def get_recipe_order_and_bootstrap(ctx, names, bs=None):
258153
259154 if bs is None :
260155 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 )
262158 else :
263159 # check if each requirement has a recipe
264160 recipes = []
265161 python_modules = []
266162 for name in chosen_order :
267163 try :
268- recipe = Recipe .get_recipe (name , ctx )
164+ Recipe .get_recipe (name , ctx )
269165 except IOError :
270166 python_modules .append (name )
271167 else :
272168 recipes .append (name )
273169
274170 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