From 890ec8d71c40a39a7018d4d7acd28d374697ce86 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Mon, 29 Jan 2024 16:31:50 +0100 Subject: [PATCH] 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. --- lib/spack/spack/deptypes.py | 3 +++ lib/spack/spack/environment/environment.py | 23 +++++++++------------- lib/spack/spack/test/traverse.py | 13 ++++++++++++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/lib/spack/spack/deptypes.py b/lib/spack/spack/deptypes.py index af196bd4ad0..df965a87f12 100644 --- a/lib/spack/spack/deptypes.py +++ b/lib/spack/spack/deptypes.py @@ -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) diff --git a/lib/spack/spack/environment/environment.py b/lib/spack/spack/environment/environment.py index 2a6cc6e7744..634c1925d30 100644 --- a/lib/spack/spack/environment/environment.py +++ b/lib/spack/spack/environment/environment.py @@ -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) diff --git a/lib/spack/spack/test/traverse.py b/lib/spack/spack/test/traverse.py index 8e440f93fb5..0876c65cc22 100644 --- a/lib/spack/spack/test/traverse.py +++ b/lib/spack/spack/test/traverse.py @@ -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"]]