|
25 | 25 | import imp
|
26 | 26 | import contextlib
|
27 | 27 | import logging
|
| 28 | +from copy import deepcopy |
28 | 29 | from functools import wraps
|
29 | 30 | from datetime import datetime
|
30 | 31 | from distutils.spawn import find_executable
|
@@ -414,38 +415,75 @@ class ArchAndroid(Arch):
|
414 | 415 |
|
415 | 416 | class Graph(object):
|
416 | 417 | # Taken from the old python-for-android/depsort
|
| 418 | + # Modified to include alternative dependencies |
417 | 419 | def __init__(self):
|
418 | 420 | # `graph`: dict that maps each package to a set of its dependencies.
|
419 |
| - self.graph = {} |
| 421 | + self.graphs = [{}] |
| 422 | + # self.graph = {} |
| 423 | + |
| 424 | + def remove_redundant_graphs(self): |
| 425 | + '''Removes possible graphs if they are equivalent to others.''' |
| 426 | + graphs = self.graphs |
| 427 | + initial_num_graphs = len(graphs) |
| 428 | + # Walk the list backwards so that popping elements doesn't mess up indexing |
| 429 | + for i in range(len(graphs) - 1): |
| 430 | + graph = graphs[initial_num_graphs - 1 - i] |
| 431 | + for j in range(1, len(graphs)): |
| 432 | + comparison_graph = graphs[initial_num_graphs - 1 - j] |
| 433 | + if set(comparison_graph.keys()) == set(graph.keys()): |
| 434 | + graphs.pop([initial_num_graphs - 1 - i]) |
| 435 | + break |
420 | 436 |
|
421 | 437 | def add(self, dependent, dependency):
|
422 | 438 | """Add a dependency relationship to the graph"""
|
423 |
| - self.graph.setdefault(dependent, set()) |
424 |
| - self.graph.setdefault(dependency, set()) |
| 439 | + if isinstance(dependency, (tuple, list)): |
| 440 | + for graph in self.graphs: |
| 441 | + self._add(graph, dependent, dependency[0]) |
| 442 | + for dep in dependency[1:]: |
| 443 | + new_graph = deepcopy(graph) |
| 444 | + self._add(new_graph, dependent, dep) |
| 445 | + self.graphs.append(new_graph) |
| 446 | + else: |
| 447 | + for graph in self.graphs: |
| 448 | + self._add(graph, dependent, dependency) |
| 449 | + self.remove_redundant_graphs() |
| 450 | + |
| 451 | + def _add(self, graph, dependent, dependency): |
| 452 | + '''Add a dependency relationship to a specific graph, where dependency |
| 453 | + must be a single dependency, not a list or tuple. |
| 454 | + ''' |
| 455 | + graph.setdefault(dependent, set()) |
| 456 | + graph.setdefault(dependency, set()) |
425 | 457 | if dependent != dependency:
|
426 |
| - self.graph[dependent].add(dependency) |
| 458 | + graph[dependent].add(dependency) |
427 | 459 |
|
428 | 460 | def conflicts(self, conflict):
|
429 |
| - if conflict in self.graph: |
430 |
| - return True |
431 |
| - return False |
| 461 | + graphs = self.graphs |
| 462 | + for i in range(len(graphs)): |
| 463 | + graph = graphs[len(graphs) - 1 - i] |
| 464 | + if conflict in graph: |
| 465 | + print('conflict in graph', conflict, graph) |
| 466 | + graphs.pop([len(graphs) - 1 - i]) |
| 467 | + return len(graphs) == 0 |
432 | 468 |
|
433 | 469 | def add_optional(self, dependent, dependency):
|
434 | 470 | """Add an optional (ordering only) dependency relationship to the graph
|
435 | 471 |
|
436 | 472 | Only call this after all mandatory requirements are added
|
437 | 473 | """
|
438 |
| - if dependent in self.graph and dependency in self.graph: |
439 |
| - self.add(dependent, dependency) |
| 474 | + for graph in self.graphs: |
| 475 | + if dependent in graph and dependency in graph: |
| 476 | + self._add(graph, dependent, dependency) |
440 | 477 |
|
441 |
| - def find_order(self): |
| 478 | + def find_order(self, index=0): |
442 | 479 | """Do a topological sort on a dependency graph
|
443 | 480 |
|
444 | 481 | :Parameters:
|
445 | 482 | :Returns:
|
446 | 483 | iterator, sorted items form first to last
|
447 | 484 | """
|
448 |
| - graph = dict((k, set(v)) for k, v in self.graph.items()) |
| 485 | + graph = self.graphs[index] |
| 486 | + graph = dict((k, set(v)) for k, v in graph.items()) |
449 | 487 | while graph:
|
450 | 488 | # Find all items without a parent
|
451 | 489 | leftmost = [l for l, s in graph.items() if not s]
|
@@ -1913,7 +1951,14 @@ def build_recipes(names, ctx):
|
1913 | 1951 | warning('Due to this conflict the build cannot continue, exiting.')
|
1914 | 1952 | exit(1)
|
1915 | 1953 | recipe_loaded.append(name)
|
1916 |
| - build_order = list(graph.find_order()) |
| 1954 | + if len(graph.graphs) > 1: |
| 1955 | + info('Found multiple valid recipe sets:') |
| 1956 | + for graph in graph.graphs: |
| 1957 | + info(' {}'.format(graph.keys())) |
| 1958 | + info_notify('Using the first of these: {}'.format(graph.graphs[0].keys())) |
| 1959 | + else: |
| 1960 | + info('Found a single valid recipe set (this is good)') |
| 1961 | + build_order = list(graph.find_order(0)) |
1917 | 1962 | info_notify("Recipe build order is {}".format(build_order))
|
1918 | 1963 | if python_modules:
|
1919 | 1964 | info_notify(('The requirements ({}) were not found as recipes, they will be '
|
|
0 commit comments