traverse: w/o deptype (#42345)

Add the empty deptype `spack.deptypes.NONE`.

Test the case `traverse_nodes(deptype=spack.deptypes.NONE)` to not
traverse dependencies, only de-duplicate.

Use the construct in environment views that otherwise would branch on
whether deps are enabled or not.
This commit is contained in:
Harmen Stoppels 2024-01-29 16:31:50 +01:00 committed by GitHub
parent 62ed5ee318
commit 890ec8d71c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 14 deletions

View file

@ -36,6 +36,9 @@
#: Default dependency type if none is specified
DEFAULT: DepFlag = BUILD | LINK
#: A flag with no dependency types set
NONE: DepFlag = 0
#: An iterator of all flag components
ALL_FLAGS: Tuple[DepFlag, DepFlag, DepFlag, DepFlag] = (BUILD, LINK, RUN, TEST)

View file

@ -21,7 +21,6 @@
import llnl.util.filesystem as fs
import llnl.util.tty as tty
import llnl.util.tty.color as clr
from llnl.util.lang import dedupe
from llnl.util.link_tree import ConflictingSpecsError
from llnl.util.symlink import symlink
@ -663,27 +662,23 @@ def __contains__(self, spec):
return True
def specs_for_view(self, concretized_root_specs):
def specs_for_view(self, concrete_roots: List[Spec]) -> List[Spec]:
"""
From the list of concretized user specs in the environment, flatten
the dags, and filter selected, installed specs, remove duplicates on dag hash.
"""
# With deps, requires traversal
if self.link == "all" or self.link == "run":
deptype = ("run") if self.link == "run" else ("link", "run")
specs = list(
traverse.traverse_nodes(
concretized_root_specs, deptype=deptype, key=traverse.by_dag_hash
)
)
if self.link == "all":
deptype = dt.LINK | dt.RUN
elif self.link == "run":
deptype = dt.RUN
else:
specs = list(dedupe(concretized_root_specs, key=traverse.by_dag_hash))
deptype = dt.NONE
specs = traverse.traverse_nodes(concrete_roots, deptype=deptype, key=traverse.by_dag_hash)
# Filter selected, installed specs
with spack.store.STORE.db.read_transaction():
specs = [s for s in specs if s in self and s.installed]
return specs
return [s for s in specs if s in self and s.installed]
def regenerate(self, concretized_root_specs):
specs = self.specs_for_view(concretized_root_specs)

View file

@ -395,3 +395,16 @@ def test_traverse_edges_topo(abstract_specs_toposort):
out_edge_indices = [i for (i, (parent, child)) in enumerate(edges) if node == parent]
if in_edge_indices and out_edge_indices:
assert max(in_edge_indices) < min(out_edge_indices)
def test_traverse_nodes_no_deps(abstract_specs_dtuse):
"""Traversing nodes without deps should be the same as deduplicating the input specs. This may
not look useful, but can be used to avoid a branch on the call site in which it's otherwise
easy to forget to deduplicate input specs."""
inputs = [
abstract_specs_dtuse["dtuse"],
abstract_specs_dtuse["dtlink5"],
abstract_specs_dtuse["dtuse"], # <- duplicate
]
outputs = [x for x in traverse.traverse_nodes(inputs, deptype=dt.NONE)]
assert outputs == [abstract_specs_dtuse["dtuse"], abstract_specs_dtuse["dtlink5"]]