merge three branches into one
This commit is contained in:
parent
485291ef20
commit
07a8f6235b
@ -2673,7 +2673,7 @@ def name_and_dependency_types(s: str) -> Tuple[str, dt.DepFlag]:
|
|||||||
return name, depflag
|
return name, depflag
|
||||||
|
|
||||||
def spec_and_dependency_types(
|
def spec_and_dependency_types(
|
||||||
s: Union[Spec, Tuple[Spec, str]]
|
s: Union[Spec, Tuple[Spec, str]],
|
||||||
) -> Tuple[Spec, dt.DepFlag]:
|
) -> Tuple[Spec, dt.DepFlag]:
|
||||||
"""Given a non-string key in the literal, extracts the spec
|
"""Given a non-string key in the literal, extracts the spec
|
||||||
and its dependency types.
|
and its dependency types.
|
||||||
@ -3838,90 +3838,76 @@ def _cmp_iter(self):
|
|||||||
# need for the complexity here. It was not clear at the time of writing that how
|
# need for the complexity here. It was not clear at the time of writing that how
|
||||||
# much optimization was possible in `spack.traverse`.
|
# much optimization was possible in `spack.traverse`.
|
||||||
|
|
||||||
if not self._dependencies:
|
sorted_l1_edges = None
|
||||||
# Spec has no dependencies -- simplest case
|
edge_list = None
|
||||||
def nodes():
|
node_ids = None
|
||||||
yield self._cmp_node # just yield this node
|
|
||||||
|
|
||||||
def edges():
|
def nodes():
|
||||||
|
nonlocal sorted_l1_edges
|
||||||
|
nonlocal edge_list
|
||||||
|
nonlocal node_ids
|
||||||
|
|
||||||
|
yield self._cmp_node # always yield the root (this node)
|
||||||
|
if not self._dependencies: # done if there are no dependencies
|
||||||
return
|
return
|
||||||
yield
|
|
||||||
|
|
||||||
elif not any(
|
# first level: yield sorted direct dependencies (inlines first level of BFS)
|
||||||
dep.spec._dependencies for deplist in self._dependencies.values() for dep in deplist
|
deps_have_deps = False
|
||||||
):
|
sorted_l1_edges = self.edges_to_dependencies(depflag=dt.ALL)
|
||||||
# Spec has dependencies, but none of them have any dependencies. Avoid calling traverse
|
if len(sorted_l1_edges) > 1:
|
||||||
# need to track visited nodes or do any of the other fancy and expensive
|
sorted_l1_edges = spack.traverse.sort_edges(sorted_l1_edges)
|
||||||
# things traverse_edges does.
|
|
||||||
sorted_edges = None
|
|
||||||
|
|
||||||
def nodes():
|
for edge in sorted_l1_edges:
|
||||||
# We generate sorted_edges once, in nodes, lazily. If the comparison can
|
|
||||||
# end with just _cmp_node, we never have to sort.
|
|
||||||
nonlocal sorted_edges
|
|
||||||
|
|
||||||
# yield the root node comparator
|
|
||||||
yield self._cmp_node
|
|
||||||
|
|
||||||
# sort only if roots were equal and caller needs to look at deps
|
|
||||||
sorted_edges = spack.traverse.sort_edges(
|
|
||||||
self.edges_to_dependencies(depflag=dt.ALL)
|
|
||||||
)
|
|
||||||
|
|
||||||
# yield dependency node comparators in BFS order
|
|
||||||
for edge in sorted_edges:
|
|
||||||
yield edge.spec._cmp_node
|
yield edge.spec._cmp_node
|
||||||
|
if edge.spec._dependencies:
|
||||||
|
deps_have_deps = True
|
||||||
|
|
||||||
def edges():
|
if not deps_have_deps: # done if level 1 specs have no dependencies
|
||||||
# we've already sorted to yield dependency nodes in order, now yield
|
return
|
||||||
# edges for comparison in the same order, with BFS-consistent ids
|
|
||||||
for i, edge in enumerate(sorted_edges, start=1):
|
|
||||||
|
|
||||||
def yield_edge(i=i, edge=edge):
|
# second level: now it's general; we need full traverse() to track visited nodes
|
||||||
yield 0 # id of root
|
l1_specs = [edge.spec for edge in sorted_l1_edges]
|
||||||
yield i # id of dependency in BFS order
|
|
||||||
yield edge.depflag
|
|
||||||
yield edge.virtuals
|
|
||||||
|
|
||||||
yield yield_edge
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Spec has dependencies, and they have dependencies. Need to do a full traversal here.
|
|
||||||
|
|
||||||
# We need a way to compare edges consistently between two arbitrary specs.
|
|
||||||
# node_ids generates consistent node ids based on BFS traversal order.
|
# node_ids generates consistent node ids based on BFS traversal order.
|
||||||
node_ids = collections.defaultdict(lambda: len(node_ids))
|
node_ids = collections.defaultdict(lambda: len(node_ids))
|
||||||
|
node_ids[id(self)] # self is 0
|
||||||
|
for spec in l1_specs:
|
||||||
|
node_ids[id(spec)] # l1 starts at 1
|
||||||
|
|
||||||
# To avoid traversing twice, we put edges in this list as we yield the nodes.
|
|
||||||
# edges() yields the edge functions later, if necessary.
|
|
||||||
edge_list = []
|
edge_list = []
|
||||||
|
for edge in spack.traverse.traverse_edges(
|
||||||
def nodes():
|
l1_specs, order="breadth", cover="edges", root=False, visited=set([0])
|
||||||
# Breadth-first edge traversal, yielding nodes as they're encountered.
|
):
|
||||||
for edge in self.traverse_edges(order="breadth", cover="edges", deptype=dt.ALL):
|
|
||||||
# yield each node only once, and generate a consistent id for it the
|
# yield each node only once, and generate a consistent id for it the
|
||||||
# first time it's encountered.
|
# first time it's encountered.
|
||||||
if id(edge.spec) not in node_ids:
|
if id(edge.spec) not in node_ids:
|
||||||
yield edge.spec._cmp_node
|
yield edge.spec._cmp_node
|
||||||
node_ids[id(edge.spec)]
|
node_ids[id(edge.spec)]
|
||||||
|
|
||||||
# skip fake edge to root
|
if edge.parent is None: # skip fake edge to root
|
||||||
if edge.parent is None:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# create "edges" with the ids we generated above
|
edge_list.append(
|
||||||
def yield_edge(edge=edge):
|
(
|
||||||
yield node_ids[id(edge.parent)]
|
node_ids[id(edge.parent)],
|
||||||
yield node_ids[id(edge.spec)]
|
node_ids[id(edge.spec)],
|
||||||
yield edge.depflag
|
edge.depflag,
|
||||||
yield edge.virtuals
|
edge.virtuals,
|
||||||
|
)
|
||||||
edge_list.append(yield_edge)
|
)
|
||||||
|
|
||||||
def edges():
|
def edges():
|
||||||
# yield edges in the order they were encountered during traversal
|
# no edges in single-node graph
|
||||||
for yield_edge_func in edge_list:
|
if not self._dependencies:
|
||||||
yield yield_edge_func
|
return
|
||||||
|
|
||||||
|
# level 1 edges all start with zero
|
||||||
|
for i, edge in enumerate(sorted_l1_edges, start=1):
|
||||||
|
yield (0, i, edge.depflag, edge.virtuals)
|
||||||
|
|
||||||
|
# yield remaining edges in the order they were encountered during traversal
|
||||||
|
if edge_list:
|
||||||
|
yield from edge_list
|
||||||
|
|
||||||
yield nodes
|
yield nodes
|
||||||
yield edges
|
yield edges
|
||||||
|
@ -2140,6 +2140,9 @@ def test_spec_ordering(specs_in_expected_order):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_spec_canonical_comparison_form(spec, expected_tuplified):
|
def test_spec_canonical_comparison_form(spec, expected_tuplified):
|
||||||
|
print()
|
||||||
|
print()
|
||||||
|
print()
|
||||||
assert llnl.util.lang.tuplify(Spec(spec)._cmp_iter) == expected_tuplified
|
assert llnl.util.lang.tuplify(Spec(spec)._cmp_iter) == expected_tuplified
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user