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 dependency type if none is specified
DEFAULT: DepFlag = BUILD | LINK DEFAULT: DepFlag = BUILD | LINK
#: A flag with no dependency types set
NONE: DepFlag = 0
#: An iterator of all flag components #: An iterator of all flag components
ALL_FLAGS: Tuple[DepFlag, DepFlag, DepFlag, DepFlag] = (BUILD, LINK, RUN, TEST) 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.filesystem as fs
import llnl.util.tty as tty import llnl.util.tty as tty
import llnl.util.tty.color as clr import llnl.util.tty.color as clr
from llnl.util.lang import dedupe
from llnl.util.link_tree import ConflictingSpecsError from llnl.util.link_tree import ConflictingSpecsError
from llnl.util.symlink import symlink from llnl.util.symlink import symlink
@ -663,27 +662,23 @@ def __contains__(self, spec):
return True 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 From the list of concretized user specs in the environment, flatten
the dags, and filter selected, installed specs, remove duplicates on dag hash. the dags, and filter selected, installed specs, remove duplicates on dag hash.
""" """
# With deps, requires traversal if self.link == "all":
if self.link == "all" or self.link == "run": deptype = dt.LINK | dt.RUN
deptype = ("run") if self.link == "run" else ("link", "run") elif self.link == "run":
specs = list( deptype = dt.RUN
traverse.traverse_nodes(
concretized_root_specs, deptype=deptype, key=traverse.by_dag_hash
)
)
else: 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 # Filter selected, installed specs
with spack.store.STORE.db.read_transaction(): with spack.store.STORE.db.read_transaction():
specs = [s for s in specs if s in self and s.installed] return [s for s in specs if s in self and s.installed]
return specs
def regenerate(self, concretized_root_specs): def regenerate(self, concretized_root_specs):
specs = self.specs_for_view(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] out_edge_indices = [i for (i, (parent, child)) in enumerate(edges) if node == parent]
if in_edge_indices and out_edge_indices: if in_edge_indices and out_edge_indices:
assert max(in_edge_indices) < min(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"]]