|
|
@ -21,6 +21,15 @@ from itertools import chain
|
|
|
|
from britney2.utils import (ifilter_only, iter_except)
|
|
|
|
from britney2.utils import (ifilter_only, iter_except)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OrderNode(object):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__slots__ = ['before', 'after']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
self.after = set()
|
|
|
|
|
|
|
|
self.before = set()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def compute_scc(graph):
|
|
|
|
def compute_scc(graph):
|
|
|
|
"""Iterative algorithm for strongly-connected components
|
|
|
|
"""Iterative algorithm for strongly-connected components
|
|
|
|
|
|
|
|
|
|
|
@ -35,7 +44,7 @@ def compute_scc(graph):
|
|
|
|
node_stack = []
|
|
|
|
node_stack = []
|
|
|
|
|
|
|
|
|
|
|
|
def _cannot_be_a_scc(graph_node):
|
|
|
|
def _cannot_be_a_scc(graph_node):
|
|
|
|
if not graph[graph_node]['before'] or not graph[graph_node]['after']:
|
|
|
|
if not graph[graph_node].before or not graph[graph_node].after:
|
|
|
|
# Short-cut obviously isolated component
|
|
|
|
# Short-cut obviously isolated component
|
|
|
|
result.append((graph_node,))
|
|
|
|
result.append((graph_node,))
|
|
|
|
# Set the item number so high that no other item might
|
|
|
|
# Set the item number so high that no other item might
|
|
|
@ -63,7 +72,7 @@ def compute_scc(graph):
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
succ_num = len(low)
|
|
|
|
succ_num = len(low)
|
|
|
|
low[succ] = succ_num
|
|
|
|
low[succ] = succ_num
|
|
|
|
work_stack.append((succ, len(node_stack), succ_num, graph[succ]['before']))
|
|
|
|
work_stack.append((succ, len(node_stack), succ_num, graph[succ].before))
|
|
|
|
node_stack.append(succ)
|
|
|
|
node_stack.append(succ)
|
|
|
|
# "Recurse" into the child node first
|
|
|
|
# "Recurse" into the child node first
|
|
|
|
return True
|
|
|
|
return True
|
|
|
@ -81,7 +90,7 @@ def compute_scc(graph):
|
|
|
|
low[n] = root_num
|
|
|
|
low[n] = root_num
|
|
|
|
# DFS work-stack needed to avoid call recursion. It (more or less)
|
|
|
|
# DFS work-stack needed to avoid call recursion. It (more or less)
|
|
|
|
# replaces the variables on the call stack in Tarjan's algorithm
|
|
|
|
# replaces the variables on the call stack in Tarjan's algorithm
|
|
|
|
work_stack = [(n, len(node_stack), root_num, graph[n]['before'])]
|
|
|
|
work_stack = [(n, len(node_stack), root_num, graph[n].before)]
|
|
|
|
node_stack.append(n)
|
|
|
|
node_stack.append(n)
|
|
|
|
while work_stack:
|
|
|
|
while work_stack:
|
|
|
|
node, stack_idx, orig_node_num, successors = work_stack[-1]
|
|
|
|
node, stack_idx, orig_node_num, successors = work_stack[-1]
|
|
|
@ -132,17 +141,18 @@ def apply_order(key, relation, ptable, order, negative_relation, debug_solver, l
|
|
|
|
if other == key:
|
|
|
|
if other == key:
|
|
|
|
# "Self-relation" => ignore
|
|
|
|
# "Self-relation" => ignore
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
order_key = order[key]
|
|
|
|
if negative_relation:
|
|
|
|
if negative_relation:
|
|
|
|
order_key_fwd = 'before'
|
|
|
|
order_key.before.add(other)
|
|
|
|
order_key_back = 'after'
|
|
|
|
order[other].after.add(key)
|
|
|
|
|
|
|
|
debug_check = order_key.before
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
order_key_fwd = 'after'
|
|
|
|
order_key.after.add(other)
|
|
|
|
order_key_back = 'before'
|
|
|
|
order[other].before.add(key)
|
|
|
|
if debug_solver and other not in order[key][order_key_fwd]: # pragma: no cover
|
|
|
|
debug_check = order_key.after
|
|
|
|
|
|
|
|
if debug_solver and other not in debug_check: # pragma: no cover
|
|
|
|
cause = 'Conflict' if negative_relation else 'Conflict'
|
|
|
|
cause = 'Conflict' if negative_relation else 'Conflict'
|
|
|
|
logger.debug("%s induced order: %s before %s", cause, key, other)
|
|
|
|
logger.debug("%s induced order: %s before %s", cause, key, other)
|
|
|
|
order[key][order_key_fwd].add(other)
|
|
|
|
|
|
|
|
order[other][order_key_back].add(key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InstallabilitySolver(object):
|
|
|
|
class InstallabilitySolver(object):
|
|
|
@ -207,16 +217,16 @@ class InstallabilitySolver(object):
|
|
|
|
other_rms.add(other)
|
|
|
|
other_rms.add(other)
|
|
|
|
|
|
|
|
|
|
|
|
for other in (other_adds - other_rms):
|
|
|
|
for other in (other_adds - other_rms):
|
|
|
|
if debug_solver and other != key and other not in order[key]['after']: # pragma: no cover
|
|
|
|
if debug_solver and other != key and other not in order[key].after: # pragma: no cover
|
|
|
|
self.logger.debug("Dependency induced order (add): %s before %s", key, other)
|
|
|
|
self.logger.debug("Dependency induced order (add): %s before %s", key, other)
|
|
|
|
order[key]['after'].add(other)
|
|
|
|
order[key].after.add(other)
|
|
|
|
order[other]['before'].add(key)
|
|
|
|
order[other].before.add(key)
|
|
|
|
|
|
|
|
|
|
|
|
for other in (other_rms - other_adds):
|
|
|
|
for other in (other_rms - other_adds):
|
|
|
|
if debug_solver and other != key and other not in order[key]['before']: # pragma: no cover
|
|
|
|
if debug_solver and other != key and other not in order[key].before: # pragma: no cover
|
|
|
|
self.logger.debug("Dependency induced order (remove): %s before %s", key, other)
|
|
|
|
self.logger.debug("Dependency induced order (remove): %s before %s", key, other)
|
|
|
|
order[key]['before'].add(other)
|
|
|
|
order[key].before.add(other)
|
|
|
|
order[other]['after'].add(key)
|
|
|
|
order[other].after.add(key)
|
|
|
|
|
|
|
|
|
|
|
|
def _compute_group_order(self, groups, key2item):
|
|
|
|
def _compute_group_order(self, groups, key2item):
|
|
|
|
universe = self._universe
|
|
|
|
universe = self._universe
|
|
|
@ -231,7 +241,7 @@ class InstallabilitySolver(object):
|
|
|
|
for (item, adds, rms) in groups:
|
|
|
|
for (item, adds, rms) in groups:
|
|
|
|
key = str(item)
|
|
|
|
key = str(item)
|
|
|
|
key2item[key] = item
|
|
|
|
key2item[key] = item
|
|
|
|
order[key] = {'before': set(), 'after': set()}
|
|
|
|
order[key] = OrderNode()
|
|
|
|
going_in.update(adds)
|
|
|
|
going_in.update(adds)
|
|
|
|
going_out.update(rms)
|
|
|
|
going_out.update(rms)
|
|
|
|
for x in chain(adds, rms):
|
|
|
|
for x in chain(adds, rms):
|
|
|
@ -271,13 +281,13 @@ class InstallabilitySolver(object):
|
|
|
|
if len(com) < 2:
|
|
|
|
if len(com) < 2:
|
|
|
|
# Trivial case
|
|
|
|
# Trivial case
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
so_before = order[scc_id]['before']
|
|
|
|
so_before = order[scc_id].before
|
|
|
|
so_after = order[scc_id]['after']
|
|
|
|
so_after = order[scc_id].after
|
|
|
|
for n in com:
|
|
|
|
for n in com:
|
|
|
|
if n == scc_id:
|
|
|
|
if n == scc_id:
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
so_before.update(order[n]['before'])
|
|
|
|
so_before.update(order[n].before)
|
|
|
|
so_after.update(order[n]['after'])
|
|
|
|
so_after.update(order[n].after)
|
|
|
|
merged[n] = scc_id
|
|
|
|
merged[n] = scc_id
|
|
|
|
del order[n]
|
|
|
|
del order[n]
|
|
|
|
if debug_solver: # pragma: no cover
|
|
|
|
if debug_solver: # pragma: no cover
|
|
|
@ -285,22 +295,22 @@ class InstallabilitySolver(object):
|
|
|
|
|
|
|
|
|
|
|
|
for com in comps:
|
|
|
|
for com in comps:
|
|
|
|
node = com[0]
|
|
|
|
node = com[0]
|
|
|
|
nbefore = set(merged[b] for b in order[node]['before'])
|
|
|
|
nbefore = set(merged[b] for b in order[node].before)
|
|
|
|
nafter = set(merged[b] for b in order[node]['after'])
|
|
|
|
nafter = set(merged[b] for b in order[node].after)
|
|
|
|
|
|
|
|
|
|
|
|
# Drop self-relations (usually caused by the merging)
|
|
|
|
# Drop self-relations (usually caused by the merging)
|
|
|
|
nbefore.discard(node)
|
|
|
|
nbefore.discard(node)
|
|
|
|
nafter.discard(node)
|
|
|
|
nafter.discard(node)
|
|
|
|
order[node]['before'] = nbefore
|
|
|
|
order[node].before = nbefore
|
|
|
|
order[node]['after'] = nafter
|
|
|
|
order[node].after = nafter
|
|
|
|
|
|
|
|
|
|
|
|
for com in comps:
|
|
|
|
for com in comps:
|
|
|
|
scc_id = com[0]
|
|
|
|
scc_id = com[0]
|
|
|
|
|
|
|
|
|
|
|
|
for other_scc_id in order[scc_id]['before']:
|
|
|
|
for other_scc_id in order[scc_id].before:
|
|
|
|
order[other_scc_id]['after'].add(scc_id)
|
|
|
|
order[other_scc_id].after.add(scc_id)
|
|
|
|
for other_scc_id in order[scc_id]['after']:
|
|
|
|
for other_scc_id in order[scc_id].after:
|
|
|
|
order[other_scc_id]['before'].add(scc_id)
|
|
|
|
order[other_scc_id].before.add(scc_id)
|
|
|
|
|
|
|
|
|
|
|
|
return scc
|
|
|
|
return scc
|
|
|
|
|
|
|
|
|
|
|
@ -338,14 +348,14 @@ class InstallabilitySolver(object):
|
|
|
|
|
|
|
|
|
|
|
|
initial_round = []
|
|
|
|
initial_round = []
|
|
|
|
for com in sorted(order):
|
|
|
|
for com in sorted(order):
|
|
|
|
if debug_solver and order[com]['before']: # pragma: no cover
|
|
|
|
if debug_solver and order[com].before: # pragma: no cover
|
|
|
|
self.logger.debug("N: %s <= %s", com, str(sorted(order[com]['before'])))
|
|
|
|
self.logger.debug("N: %s <= %s", com, str(sorted(order[com].before)))
|
|
|
|
if not order[com]['after']:
|
|
|
|
if not order[com].after:
|
|
|
|
# This component can be scheduled immediately, add it
|
|
|
|
# This component can be scheduled immediately, add it
|
|
|
|
# to the queue
|
|
|
|
# to the queue
|
|
|
|
initial_round.append(com)
|
|
|
|
initial_round.append(com)
|
|
|
|
elif debug_solver: # pragma: no cover
|
|
|
|
elif debug_solver: # pragma: no cover
|
|
|
|
self.logger.debug("N: %s >= %s", com, str(sorted(order[com]['after'])))
|
|
|
|
self.logger.debug("N: %s >= %s", com, str(sorted(order[com].after)))
|
|
|
|
|
|
|
|
|
|
|
|
queue.extend(sorted(initial_round, key=len))
|
|
|
|
queue.extend(sorted(initial_round, key=len))
|
|
|
|
del initial_round
|
|
|
|
del initial_round
|
|
|
@ -355,18 +365,18 @@ class InstallabilitySolver(object):
|
|
|
|
self.logger.debug("-- LINEARIZED ORDER --")
|
|
|
|
self.logger.debug("-- LINEARIZED ORDER --")
|
|
|
|
|
|
|
|
|
|
|
|
for cur in iter_except(queue.popleft, IndexError):
|
|
|
|
for cur in iter_except(queue.popleft, IndexError):
|
|
|
|
if order[cur]['after'] <= emitted and cur not in emitted:
|
|
|
|
if order[cur].after <= emitted and cur not in emitted:
|
|
|
|
# This item is ready to be emitted right now
|
|
|
|
# This item is ready to be emitted right now
|
|
|
|
if debug_solver: # pragma: no cover
|
|
|
|
if debug_solver: # pragma: no cover
|
|
|
|
self.logger.debug("%s -- %s", cur, sorted(scc_items[cur]))
|
|
|
|
self.logger.debug("%s -- %s", cur, sorted(scc_items[cur]))
|
|
|
|
emitted.add(cur)
|
|
|
|
emitted.add(cur)
|
|
|
|
result.append([key2item[x] for x in scc_items[cur]])
|
|
|
|
result.append([key2item[x] for x in scc_items[cur]])
|
|
|
|
if order[cur]['before']:
|
|
|
|
if order[cur].before:
|
|
|
|
# There are components that come after this one.
|
|
|
|
# There are components that come after this one.
|
|
|
|
# Add it to queue:
|
|
|
|
# Add it to queue:
|
|
|
|
# - if it is ready, it will be emitted.
|
|
|
|
# - if it is ready, it will be emitted.
|
|
|
|
# - else, it will be dropped and re-added later.
|
|
|
|
# - else, it will be dropped and re-added later.
|
|
|
|
queue.extend(sorted(order[cur]['before'] - emitted, key=len))
|
|
|
|
queue.extend(sorted(order[cur].before - emitted, key=len))
|
|
|
|
|
|
|
|
|
|
|
|
if debug_solver: # pragma: no cover
|
|
|
|
if debug_solver: # pragma: no cover
|
|
|
|
self.logger.debug("-- END LINEARIZED ORDER --")
|
|
|
|
self.logger.debug("-- END LINEARIZED ORDER --")
|
|
|
|