Compare commits
2 Commits
fix/fewer-
...
solver-pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7a2045c79 | ||
|
|
2632106f1e |
@@ -828,7 +828,7 @@ def setup_package(pkg, dirty, context: Context = Context.BUILD):
|
||||
return env_base
|
||||
|
||||
|
||||
class EnvironmentVisitor(traverse.AbstractVisitor):
|
||||
class EnvironmentVisitor:
|
||||
def __init__(self, *roots: spack.spec.Spec, context: Context):
|
||||
# For the roots (well, marked specs) we follow different edges
|
||||
# than for their deps, depending on the context.
|
||||
@@ -846,7 +846,7 @@ def __init__(self, *roots: spack.spec.Spec, context: Context):
|
||||
self.root_depflag = dt.RUN | dt.LINK
|
||||
|
||||
def neighbors(self, item):
|
||||
spec = item[0].spec
|
||||
spec = item.edge.spec
|
||||
if spec.dag_hash() in self.root_hashes:
|
||||
depflag = self.root_depflag
|
||||
else:
|
||||
|
||||
@@ -41,7 +41,7 @@ def setup_parser(subparser):
|
||||
)
|
||||
|
||||
|
||||
class AreDepsInstalledVisitor(traverse.AbstractVisitor):
|
||||
class AreDepsInstalledVisitor:
|
||||
def __init__(self, context: Context = Context.BUILD):
|
||||
if context == Context.BUILD:
|
||||
# TODO: run deps shouldn't be required for build env.
|
||||
@@ -53,27 +53,27 @@ def __init__(self, context: Context = Context.BUILD):
|
||||
|
||||
self.has_uninstalled_deps = False
|
||||
|
||||
def accept(self, item: traverse.EdgeAndDepth) -> bool:
|
||||
def accept(self, item):
|
||||
# The root may be installed or uninstalled.
|
||||
if item[1] == 0:
|
||||
if item.depth == 0:
|
||||
return True
|
||||
|
||||
# Early exit after we've seen an uninstalled dep.
|
||||
if self.has_uninstalled_deps:
|
||||
return False
|
||||
|
||||
spec = item[0].spec
|
||||
spec = item.edge.spec
|
||||
if not spec.external and not spec.installed:
|
||||
self.has_uninstalled_deps = True
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def neighbors(self, item: traverse.EdgeAndDepth):
|
||||
def neighbors(self, item):
|
||||
# Direct deps: follow build & test edges.
|
||||
# Transitive deps: follow link / run.
|
||||
depflag = self.direct_deps if item[1] == 0 else dt.LINK | dt.RUN
|
||||
return item[0].spec.edges_to_dependencies(depflag=depflag)
|
||||
depflag = self.direct_deps if item.depth == 0 else dt.LINK | dt.RUN
|
||||
return item.edge.spec.edges_to_dependencies(depflag=depflag)
|
||||
|
||||
|
||||
def emulate_env_utility(cmd_name, context: Context, args):
|
||||
|
||||
@@ -59,7 +59,7 @@ def __init__(
|
||||
self.buildcache_flag = ""
|
||||
|
||||
|
||||
class DepfileSpecVisitor(traverse.AbstractVisitor):
|
||||
class DepfileSpecVisitor:
|
||||
"""This visitor produces an adjacency list of a (reduced) DAG, which
|
||||
is used to generate depfile targets with their prerequisites. Currently
|
||||
it only drops build deps when using buildcache only mode.
|
||||
@@ -75,17 +75,17 @@ def __init__(self, pkg_buildcache: UseBuildCache, deps_buildcache: UseBuildCache
|
||||
self.depflag_root = _deptypes(pkg_buildcache)
|
||||
self.depflag_deps = _deptypes(deps_buildcache)
|
||||
|
||||
def neighbors(self, node: traverse.EdgeAndDepth) -> List[spack.spec.DependencySpec]:
|
||||
def neighbors(self, node):
|
||||
"""Produce a list of spec to follow from node"""
|
||||
depflag = self.depflag_root if node[1] == 0 else self.depflag_deps
|
||||
return traverse.sort_edges(node[0].spec.edges_to_dependencies(depflag=depflag))
|
||||
depflag = self.depflag_root if node.depth == 0 else self.depflag_deps
|
||||
return traverse.sort_edges(node.edge.spec.edges_to_dependencies(depflag=depflag))
|
||||
|
||||
def accept(self, node: traverse.EdgeAndDepth) -> bool:
|
||||
def accept(self, node):
|
||||
self.adjacency_list.append(
|
||||
DepfileNode(
|
||||
target=node[0].spec,
|
||||
target=node.edge.spec,
|
||||
prereqs=[edge.spec for edge in self.neighbors(node)],
|
||||
buildcache=self.pkg_buildcache if node[1] == 0 else self.deps_buildcache,
|
||||
buildcache=self.pkg_buildcache if node.depth == 0 else self.deps_buildcache,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
},
|
||||
]
|
||||
},
|
||||
"prefer_older": {"type": "boolean"},
|
||||
"enable_node_namespace": {"type": "boolean"},
|
||||
"targets": {
|
||||
"type": "object",
|
||||
|
||||
@@ -1025,7 +1025,7 @@ def __iter__(self):
|
||||
class SpackSolverSetup:
|
||||
"""Class to set up and run a Spack concretization solve."""
|
||||
|
||||
def __init__(self, tests: bool = False):
|
||||
def __init__(self, tests: bool = False, prefer_older=False):
|
||||
# these are all initialized in setup()
|
||||
self.gen: "ProblemInstanceBuilder" = ProblemInstanceBuilder()
|
||||
self.possible_virtuals: Set[str] = set()
|
||||
@@ -1058,6 +1058,8 @@ def __init__(self, tests: bool = False):
|
||||
# whether to add installed/binary hashes to the solve
|
||||
self.tests = tests
|
||||
|
||||
self.prefer_older = prefer_older
|
||||
|
||||
# If False allows for input specs that are not solved
|
||||
self.concretize_everything = True
|
||||
|
||||
@@ -1091,7 +1093,9 @@ def key_fn(version):
|
||||
list(sorted(group, reverse=True, key=lambda x: vn.ver(x.version)))
|
||||
)
|
||||
|
||||
for weight, declared_version in enumerate(most_to_least_preferred):
|
||||
for weight, declared_version in enumerate(
|
||||
reversed(most_to_least_preferred) if self.prefer_older else most_to_least_preferred
|
||||
):
|
||||
self.gen.fact(
|
||||
fn.pkg_fact(
|
||||
pkg.name,
|
||||
@@ -3800,6 +3804,8 @@ def __init__(self):
|
||||
self.driver = PyclingoDriver()
|
||||
self.selector = ReusableSpecsSelector(configuration=spack.config.CONFIG)
|
||||
|
||||
self.prefer_older = spack.config.get("concretizer:prefer_older", False)
|
||||
|
||||
@staticmethod
|
||||
def _check_input_and_extract_concrete_specs(specs):
|
||||
reusable = []
|
||||
@@ -3838,7 +3844,7 @@ def solve(
|
||||
specs = [s.lookup_hash() for s in specs]
|
||||
reusable_specs = self._check_input_and_extract_concrete_specs(specs)
|
||||
reusable_specs.extend(self.selector.reusable_specs(specs))
|
||||
setup = SpackSolverSetup(tests=tests)
|
||||
setup = SpackSolverSetup(tests=tests, prefer_older=self.prefer_older)
|
||||
output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=setup_only)
|
||||
result, _, _ = self.driver.solve(
|
||||
setup, specs, reuse=reusable_specs, output=output, allow_deprecated=allow_deprecated
|
||||
@@ -3867,7 +3873,7 @@ def solve_in_rounds(
|
||||
specs = [s.lookup_hash() for s in specs]
|
||||
reusable_specs = self._check_input_and_extract_concrete_specs(specs)
|
||||
reusable_specs.extend(self.selector.reusable_specs(specs))
|
||||
setup = SpackSolverSetup(tests=tests)
|
||||
setup = SpackSolverSetup(tests=tests, prefer_older=self.prefer_older)
|
||||
|
||||
# Tell clingo that we don't have to solve all the inputs at once
|
||||
setup.concretize_everything = False
|
||||
|
||||
@@ -1454,9 +1454,7 @@ def _get_dependency(self, name):
|
||||
raise spack.error.SpecError(err_msg.format(name, len(deps)))
|
||||
return deps[0]
|
||||
|
||||
def edges_from_dependents(
|
||||
self, name=None, depflag: dt.DepFlag = dt.ALL
|
||||
) -> List[DependencySpec]:
|
||||
def edges_from_dependents(self, name=None, depflag: dt.DepFlag = dt.ALL):
|
||||
"""Return a list of edges connecting this node in the DAG
|
||||
to parents.
|
||||
|
||||
@@ -1466,9 +1464,7 @@ def edges_from_dependents(
|
||||
"""
|
||||
return [d for d in self._dependents.select(parent=name, depflag=depflag)]
|
||||
|
||||
def edges_to_dependencies(
|
||||
self, name=None, depflag: dt.DepFlag = dt.ALL
|
||||
) -> List[DependencySpec]:
|
||||
def edges_to_dependencies(self, name=None, depflag: dt.DepFlag = dt.ALL):
|
||||
"""Return a list of edges connecting this node in the DAG
|
||||
to children.
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from abc import ABC
|
||||
from collections import defaultdict
|
||||
from typing import Any, Callable, Iterable, List, Optional, Tuple, Union
|
||||
from collections import defaultdict, namedtuple
|
||||
from typing import Union
|
||||
|
||||
import spack.deptypes as dt
|
||||
import spack.spec
|
||||
@@ -13,82 +12,72 @@
|
||||
# Export only the high-level API.
|
||||
__all__ = ["traverse_edges", "traverse_nodes", "traverse_tree"]
|
||||
|
||||
EdgeAndDepth = Tuple["spack.spec.DependencySpec", int]
|
||||
Key = Callable[["spack.spec.Spec"], Any]
|
||||
#: Data class that stores a directed edge together with depth at
|
||||
#: which the target vertex was found. It is passed to ``accept``
|
||||
#: and ``neighbors`` of visitors, so they can decide whether to
|
||||
#: follow the edge or not.
|
||||
EdgeAndDepth = namedtuple("EdgeAndDepth", ["edge", "depth"])
|
||||
|
||||
|
||||
def sort_edges(edges: List["spack.spec.DependencySpec"]) -> List["spack.spec.DependencySpec"]:
|
||||
def sort_edges(edges):
|
||||
edges.sort(key=lambda edge: (edge.spec.name or "", edge.spec.abstract_hash or ""))
|
||||
return edges
|
||||
|
||||
|
||||
class AbstractVisitor(ABC):
|
||||
"""Abstract base class for visitors that traverse the DAG."""
|
||||
class BaseVisitor:
|
||||
"""A simple visitor that accepts all edges unconditionally and follows all
|
||||
edges to dependencies of a given ``deptype``."""
|
||||
|
||||
def accept(self, item: EdgeAndDepth) -> bool:
|
||||
def __init__(self, depflag: dt.DepFlag = dt.ALL):
|
||||
self.depflag = depflag
|
||||
|
||||
def accept(self, item):
|
||||
"""
|
||||
Arguments:
|
||||
item: the edge through which this node was reached at what depth.
|
||||
item (EdgeAndDepth): Provides the depth and the edge through which the
|
||||
node was discovered
|
||||
|
||||
Returns:
|
||||
Iff True, the node is yielded by iterators and dependencies are followed.
|
||||
bool: Returns ``True`` if the node is accepted. When ``False``, this
|
||||
indicates that the node won't be yielded by iterators and dependencies
|
||||
are not followed.
|
||||
"""
|
||||
return True
|
||||
|
||||
def neighbors(self, item: EdgeAndDepth) -> List["spack.spec.DependencySpec"]:
|
||||
raise NotImplementedError
|
||||
def neighbors(self, item):
|
||||
return sort_edges(item.edge.spec.edges_to_dependencies(depflag=self.depflag))
|
||||
|
||||
|
||||
class AbstractDFSVisitor(AbstractVisitor):
|
||||
"""Abstract base class for visitors that traverse the DAG in depth-first fashion."""
|
||||
|
||||
def pre(self, item: EdgeAndDepth) -> None:
|
||||
pass
|
||||
|
||||
def post(self, item: EdgeAndDepth) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class DefaultVisitor(AbstractVisitor):
|
||||
def __init__(self, depflag: dt.DepFlag = dt.ALL) -> None:
|
||||
self.depflag = depflag
|
||||
|
||||
def neighbors(self, item: EdgeAndDepth) -> List["spack.spec.DependencySpec"]:
|
||||
return sort_edges(item[0].spec.edges_to_dependencies(depflag=self.depflag))
|
||||
|
||||
|
||||
class ReverseVisitor(AbstractVisitor):
|
||||
class ReverseVisitor:
|
||||
"""A visitor that reverses the arrows in the DAG, following dependents."""
|
||||
|
||||
def __init__(self, visitor: AbstractVisitor, depflag: dt.DepFlag = dt.ALL) -> None:
|
||||
def __init__(self, visitor, depflag: dt.DepFlag = dt.ALL):
|
||||
self.visitor = visitor
|
||||
self.depflag = depflag
|
||||
|
||||
def accept(self, item: EdgeAndDepth) -> bool:
|
||||
def accept(self, item):
|
||||
return self.visitor.accept(item)
|
||||
|
||||
def neighbors(self, item: EdgeAndDepth) -> List["spack.spec.DependencySpec"]:
|
||||
def neighbors(self, item):
|
||||
"""Return dependents, note that we actually flip the edge direction to allow
|
||||
generic programming"""
|
||||
spec = item[0].spec
|
||||
spec = item.edge.spec
|
||||
return sort_edges(
|
||||
[edge.flip() for edge in spec.edges_from_dependents(depflag=self.depflag)]
|
||||
)
|
||||
|
||||
|
||||
class CoverNodesVisitor(AbstractVisitor):
|
||||
class CoverNodesVisitor:
|
||||
"""A visitor that traverses each node once."""
|
||||
|
||||
def __init__(
|
||||
self, visitor: AbstractVisitor, key: Key = id, visited: Optional[set] = None
|
||||
) -> None:
|
||||
def __init__(self, visitor, key=id, visited=None):
|
||||
self.visitor = visitor
|
||||
self.key = key
|
||||
self.visited = set() if visited is None else visited
|
||||
|
||||
def accept(self, item: EdgeAndDepth) -> bool:
|
||||
def accept(self, item):
|
||||
# Covering nodes means: visit nodes once and only once.
|
||||
key = self.key(item[0].spec)
|
||||
key = self.key(item.edge.spec)
|
||||
|
||||
if key in self.visited:
|
||||
return False
|
||||
@@ -97,26 +86,24 @@ def accept(self, item: EdgeAndDepth) -> bool:
|
||||
self.visited.add(key)
|
||||
return accept
|
||||
|
||||
def neighbors(self, item: EdgeAndDepth) -> List["spack.spec.DependencySpec"]:
|
||||
def neighbors(self, item):
|
||||
return self.visitor.neighbors(item)
|
||||
|
||||
|
||||
class CoverEdgesVisitor(AbstractVisitor):
|
||||
class CoverEdgesVisitor:
|
||||
"""A visitor that traverses all edges once."""
|
||||
|
||||
def __init__(
|
||||
self, visitor: AbstractVisitor, key: Key = id, visited: Optional[set] = None
|
||||
) -> None:
|
||||
def __init__(self, visitor, key=id, visited=None):
|
||||
self.visitor = visitor
|
||||
self.visited = set() if visited is None else visited
|
||||
self.key = key
|
||||
|
||||
def accept(self, item: EdgeAndDepth) -> bool:
|
||||
def accept(self, item):
|
||||
return self.visitor.accept(item)
|
||||
|
||||
def neighbors(self, item: EdgeAndDepth) -> List["spack.spec.DependencySpec"]:
|
||||
def neighbors(self, item):
|
||||
# Covering edges means: drop dependencies of visited nodes.
|
||||
key = self.key(item[0].spec)
|
||||
key = self.key(item.edge.spec)
|
||||
|
||||
if key in self.visited:
|
||||
return []
|
||||
@@ -125,7 +112,7 @@ def neighbors(self, item: EdgeAndDepth) -> List["spack.spec.DependencySpec"]:
|
||||
return self.visitor.neighbors(item)
|
||||
|
||||
|
||||
class TopoVisitor(AbstractDFSVisitor):
|
||||
class TopoVisitor:
|
||||
"""Visitor that can be used in :py:func:`depth-first traversal
|
||||
<spack.traverse.traverse_depth_first_with_visitor>` to generate
|
||||
a topologically ordered list of specs.
|
||||
@@ -145,39 +132,42 @@ class TopoVisitor(AbstractDFSVisitor):
|
||||
edges, with the property that for each vertex all in-edges precede all out-edges.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, visitor: AbstractVisitor, key: Key = id, root: bool = True, all_edges: bool = False
|
||||
):
|
||||
def __init__(self, visitor, key=id, root=True, all_edges=False):
|
||||
"""
|
||||
Arguments:
|
||||
visitor: visitor that implements accept(), pre(), post() and neighbors()
|
||||
key: uniqueness key for nodes
|
||||
root: Whether to include the root node.
|
||||
all_edges: when ``False`` (default): Each node is reached once, and
|
||||
``map(lambda edge: edge.spec, visitor.edges)`` is topologically ordered. When
|
||||
``True``, every edge is listed, ordered such that for each node all in-edges
|
||||
precede all out-edges.
|
||||
root (bool): Whether to include the root node.
|
||||
all_edges (bool): when ``False`` (default): Each node is reached once,
|
||||
and ``map(lambda edge: edge.spec, visitor.edges)`` is topologically
|
||||
ordered. When ``True``, every edge is listed, ordered such that for
|
||||
each node all in-edges precede all out-edges.
|
||||
"""
|
||||
self.visited: set = set()
|
||||
self.visited = set()
|
||||
self.visitor = visitor
|
||||
self.key = key
|
||||
self.root = root
|
||||
self.reverse_order: List[spack.spec.DependencySpec] = []
|
||||
self.reverse_order = []
|
||||
self.all_edges = all_edges
|
||||
|
||||
def accept(self, item: EdgeAndDepth) -> bool:
|
||||
if self.key(item[0].spec) not in self.visited:
|
||||
def accept(self, item):
|
||||
if self.key(item.edge.spec) not in self.visited:
|
||||
return True
|
||||
if self.all_edges and (self.root or item[1] > 0):
|
||||
self.reverse_order.append(item[0])
|
||||
if self.all_edges and (self.root or item.depth > 0):
|
||||
self.reverse_order.append(item.edge)
|
||||
return False
|
||||
|
||||
def post(self, item: EdgeAndDepth) -> None:
|
||||
self.visited.add(self.key(item[0].spec))
|
||||
if self.root or item[1] > 0:
|
||||
self.reverse_order.append(item[0])
|
||||
def pre(self, item):
|
||||
# You could add a temporary marker for cycle detection
|
||||
# that's cleared in `post`, but we assume no cycles.
|
||||
pass
|
||||
|
||||
def neighbors(self, item: EdgeAndDepth) -> List["spack.spec.DependencySpec"]:
|
||||
def post(self, item):
|
||||
self.visited.add(self.key(item.edge.spec))
|
||||
if self.root or item.depth > 0:
|
||||
self.reverse_order.append(item.edge)
|
||||
|
||||
def neighbors(self, item):
|
||||
return self.visitor.neighbors(item)
|
||||
|
||||
@property
|
||||
@@ -214,7 +204,7 @@ def get_visitor_from_args(
|
||||
"""
|
||||
if not isinstance(depflag, dt.DepFlag):
|
||||
depflag = dt.canonicalize(depflag)
|
||||
visitor = visitor or DefaultVisitor(depflag)
|
||||
visitor = visitor or BaseVisitor(depflag)
|
||||
if cover == "nodes":
|
||||
visitor = CoverNodesVisitor(visitor, key, visited)
|
||||
elif cover == "edges":
|
||||
@@ -227,40 +217,38 @@ def get_visitor_from_args(
|
||||
def with_artificial_edges(specs):
|
||||
"""Initialize a list of edges from an imaginary root node to the root specs."""
|
||||
return [
|
||||
(spack.spec.DependencySpec(parent=None, spec=s, depflag=0, virtuals=()), 0) for s in specs
|
||||
EdgeAndDepth(
|
||||
edge=spack.spec.DependencySpec(parent=None, spec=s, depflag=0, virtuals=()), depth=0
|
||||
)
|
||||
for s in specs
|
||||
]
|
||||
|
||||
|
||||
def traverse_depth_first_edges_generator(
|
||||
edges: List[EdgeAndDepth],
|
||||
visitor,
|
||||
post_order: bool = False,
|
||||
root: bool = True,
|
||||
depth: bool = False,
|
||||
):
|
||||
def traverse_depth_first_edges_generator(edges, visitor, post_order=False, root=True, depth=False):
|
||||
"""Generator that takes explores a DAG in depth-first fashion starting from
|
||||
a list of edges. Note that typically DFS would take a vertex not a list of edges,
|
||||
but the API is like this so we don't have to create an artificial root node when
|
||||
traversing from multiple roots in a DAG.
|
||||
|
||||
Arguments:
|
||||
edges: List of EdgeAndDepth instances
|
||||
edges (list): List of EdgeAndDepth instances
|
||||
visitor: class instance implementing accept() and neigbors()
|
||||
post_order: Whether to yield nodes when backtracking
|
||||
root: whether to yield at depth 0
|
||||
depth: when ``True`` yield a tuple of depth and edge, otherwise only the edge.
|
||||
post_order (bool): Whether to yield nodes when backtracking
|
||||
root (bool): whether to yield at depth 0
|
||||
depth (bool): when ``True`` yield a tuple of depth and edge, otherwise only the
|
||||
edge.
|
||||
"""
|
||||
for edge in edges:
|
||||
if not visitor.accept(edge):
|
||||
continue
|
||||
|
||||
yield_me = root or edge[1] > 0
|
||||
yield_me = root or edge.depth > 0
|
||||
|
||||
# Pre
|
||||
if yield_me and not post_order:
|
||||
yield (edge[1], edge[0]) if depth else edge[0]
|
||||
yield (edge.depth, edge.edge) if depth else edge.edge
|
||||
|
||||
neighbors = [(n, edge[1] + 1) for n in visitor.neighbors(edge)]
|
||||
neighbors = [EdgeAndDepth(edge=n, depth=edge.depth + 1) for n in visitor.neighbors(edge)]
|
||||
|
||||
# This extra branch is just for efficiency.
|
||||
if len(neighbors) > 0:
|
||||
@@ -271,12 +259,10 @@ def traverse_depth_first_edges_generator(
|
||||
|
||||
# Post
|
||||
if yield_me and post_order:
|
||||
yield (edge[1], edge[0]) if depth else edge[0]
|
||||
yield (edge.depth, edge.edge) if depth else edge.edge
|
||||
|
||||
|
||||
def traverse_breadth_first_edges_generator(
|
||||
queue: List[EdgeAndDepth], visitor, root: bool = True, depth: bool = False
|
||||
):
|
||||
def traverse_breadth_first_edges_generator(queue, visitor, root=True, depth=False):
|
||||
while len(queue) > 0:
|
||||
edge = queue.pop(0)
|
||||
|
||||
@@ -284,18 +270,18 @@ def traverse_breadth_first_edges_generator(
|
||||
if not visitor.accept(edge):
|
||||
continue
|
||||
|
||||
if root or edge[1] > 0:
|
||||
yield (edge[1], edge[0]) if depth else edge[0]
|
||||
if root or edge.depth > 0:
|
||||
yield (edge.depth, edge.edge) if depth else edge.edge
|
||||
|
||||
for e in visitor.neighbors(edge):
|
||||
queue.append((e, edge[1] + 1))
|
||||
queue.append(EdgeAndDepth(e, edge.depth + 1))
|
||||
|
||||
|
||||
def traverse_breadth_first_with_visitor(specs: List[EdgeAndDepth], visitor: AbstractVisitor):
|
||||
def traverse_breadth_first_with_visitor(specs, visitor):
|
||||
"""Performs breadth first traversal for a list of specs (not a generator).
|
||||
|
||||
Arguments:
|
||||
specs: List of Spec instances.
|
||||
specs (list): List of Spec instances.
|
||||
visitor: object that implements accept and neighbors interface, see
|
||||
for example BaseVisitor.
|
||||
"""
|
||||
@@ -308,21 +294,26 @@ def traverse_breadth_first_with_visitor(specs: List[EdgeAndDepth], visitor: Abst
|
||||
continue
|
||||
|
||||
for e in visitor.neighbors(edge):
|
||||
queue.append((e, edge[1] + 1))
|
||||
queue.append(EdgeAndDepth(e, edge.depth + 1))
|
||||
|
||||
|
||||
def traverse_depth_first_with_visitor(edges: List[EdgeAndDepth], visitor: AbstractDFSVisitor):
|
||||
def traverse_depth_first_with_visitor(edges, visitor):
|
||||
"""Traverse a DAG in depth-first fashion using a visitor, starting from
|
||||
a list of edges. Note that typically DFS would take a vertex not a list of edges,
|
||||
but the API is like this so we don't have to create an artificial root node when
|
||||
traversing from multiple roots in a DAG."""
|
||||
traversing from multiple roots in a DAG.
|
||||
|
||||
Arguments:
|
||||
edges (list): List of EdgeAndDepth instances
|
||||
visitor: class instance implementing accept(), pre(), post() and neighbors()
|
||||
"""
|
||||
for edge in edges:
|
||||
if not visitor.accept(edge):
|
||||
continue
|
||||
|
||||
visitor.pre(edge)
|
||||
|
||||
neighbors = [(e, edge[1] + 1) for e in visitor.neighbors(edge)]
|
||||
neighbors = [EdgeAndDepth(edge=e, depth=edge.depth + 1) for e in visitor.neighbors(edge)]
|
||||
|
||||
traverse_depth_first_with_visitor(neighbors, visitor)
|
||||
|
||||
@@ -332,15 +323,12 @@ def traverse_depth_first_with_visitor(edges: List[EdgeAndDepth], visitor: Abstra
|
||||
# Helper functions for generating a tree using breadth-first traversal
|
||||
|
||||
|
||||
def breadth_first_to_tree_edges(
|
||||
roots: Iterable["spack.spec.Spec"],
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.ALL,
|
||||
key: Key = id,
|
||||
):
|
||||
"""This produces an adjacency list (with edges) and a map of parents. There may be nodes that
|
||||
are reached through multiple edges. To print as a tree, one should use the parents dict to
|
||||
verify if the path leading to the node is through the correct parent. If not, the branch should
|
||||
be truncated."""
|
||||
def breadth_first_to_tree_edges(roots, deptype="all", key=id):
|
||||
"""This produces an adjacency list (with edges) and a map of parents.
|
||||
There may be nodes that are reached through multiple edges. To print as
|
||||
a tree, one should use the parents dict to verify if the path leading to
|
||||
the node is through the correct parent. If not, the branch should be
|
||||
truncated."""
|
||||
edges = defaultdict(list)
|
||||
parents = dict()
|
||||
|
||||
@@ -354,11 +342,7 @@ def breadth_first_to_tree_edges(
|
||||
return edges, parents
|
||||
|
||||
|
||||
def breadth_first_to_tree_nodes(
|
||||
roots: Iterable["spack.spec.Spec"],
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.ALL,
|
||||
key: Key = id,
|
||||
):
|
||||
def breadth_first_to_tree_nodes(roots, deptype="all", key=id):
|
||||
"""This produces a list of edges that forms a tree; every node has no more
|
||||
that one incoming edge."""
|
||||
edges = defaultdict(list)
|
||||
@@ -371,8 +355,8 @@ def breadth_first_to_tree_nodes(
|
||||
|
||||
|
||||
def traverse_breadth_first_tree_edges(parent_id, edges, parents, key=id, depth=0):
|
||||
"""Do a depth-first search on edges generated by breadth-first traversal, which can be used to
|
||||
produce a tree."""
|
||||
"""Do a depth-first search on edges generated by bread-first traversal,
|
||||
which can be used to produce a tree."""
|
||||
for edge in edges[parent_id]:
|
||||
yield (depth, edge)
|
||||
|
||||
@@ -382,23 +366,26 @@ def traverse_breadth_first_tree_edges(parent_id, edges, parents, key=id, depth=0
|
||||
if parents[child_id] != parent_id:
|
||||
continue
|
||||
|
||||
yield from traverse_breadth_first_tree_edges(child_id, edges, parents, key, depth + 1)
|
||||
# yield from ... in Python 3.
|
||||
for item in traverse_breadth_first_tree_edges(child_id, edges, parents, key, depth + 1):
|
||||
yield item
|
||||
|
||||
|
||||
def traverse_breadth_first_tree_nodes(parent_id, edges, key=id, depth=0):
|
||||
for edge in edges[parent_id]:
|
||||
yield (depth, edge)
|
||||
yield from traverse_breadth_first_tree_nodes(key(edge.spec), edges, key, depth + 1)
|
||||
for item in traverse_breadth_first_tree_nodes(key(edge.spec), edges, key, depth + 1):
|
||||
yield item
|
||||
|
||||
|
||||
# Topologic order
|
||||
def traverse_edges_topo(
|
||||
specs: Iterable["spack.spec.Spec"],
|
||||
direction: str = "children",
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.ALL,
|
||||
key: Key = id,
|
||||
root: bool = True,
|
||||
all_edges: bool = False,
|
||||
specs,
|
||||
direction="children",
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = "all",
|
||||
key=id,
|
||||
root=True,
|
||||
all_edges=False,
|
||||
):
|
||||
"""
|
||||
Returns a list of edges in topological order, in the sense that all in-edges of a
|
||||
@@ -407,49 +394,50 @@ def traverse_edges_topo(
|
||||
directed from dependency to dependent.
|
||||
|
||||
Arguments:
|
||||
specs: List of root specs (considered to be depth 0)
|
||||
direction: ``children`` (edges are directed from dependent to dependency)
|
||||
specs (list): List of root specs (considered to be depth 0)
|
||||
direction (str): ``children`` (edges are directed from dependent to dependency)
|
||||
or ``parents`` (edges are flipped / directed from dependency to dependent)
|
||||
deptype: allowed dependency types
|
||||
key: function that takes a spec and outputs a key for uniqueness test.
|
||||
root: Yield the root nodes themselves
|
||||
all_edges: When ``False`` only one in-edge per node is returned, when ``True`` all
|
||||
reachable edges are returned.
|
||||
root (bool): Yield the root nodes themselves
|
||||
all_edges (bool): When ``False`` only one in-edge per node is returned, when
|
||||
``True`` all reachable edges are returned.
|
||||
"""
|
||||
if not isinstance(deptype, dt.DepFlag):
|
||||
deptype = dt.canonicalize(deptype)
|
||||
default = DefaultVisitor(deptype)
|
||||
with_dir = ReverseVisitor(default, deptype) if direction == "parents" else default
|
||||
topo = TopoVisitor(with_dir, key=key, root=root, all_edges=all_edges)
|
||||
traverse_depth_first_with_visitor(with_artificial_edges(specs), topo)
|
||||
return topo.edges
|
||||
visitor: Union[BaseVisitor, ReverseVisitor, TopoVisitor] = BaseVisitor(deptype)
|
||||
if direction == "parents":
|
||||
visitor = ReverseVisitor(visitor, deptype)
|
||||
visitor = TopoVisitor(visitor, key=key, root=root, all_edges=all_edges)
|
||||
traverse_depth_first_with_visitor(with_artificial_edges(specs), visitor)
|
||||
return visitor.edges
|
||||
|
||||
|
||||
# High-level API: traverse_edges, traverse_nodes, traverse_tree.
|
||||
|
||||
|
||||
def traverse_edges(
|
||||
specs: Iterable["spack.spec.Spec"],
|
||||
root: bool = True,
|
||||
order: str = "pre",
|
||||
cover: str = "nodes",
|
||||
direction: str = "children",
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.ALL,
|
||||
depth: bool = False,
|
||||
key: Key = id,
|
||||
visited: Optional[set] = None,
|
||||
specs,
|
||||
root=True,
|
||||
order="pre",
|
||||
cover="nodes",
|
||||
direction="children",
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = "all",
|
||||
depth=False,
|
||||
key=id,
|
||||
visited=None,
|
||||
):
|
||||
"""
|
||||
Generator that yields edges from the DAG, starting from a list of root specs.
|
||||
|
||||
Arguments:
|
||||
|
||||
specs: List of root specs (considered to be depth 0)
|
||||
root: Yield the root nodes themselves
|
||||
order: What order of traversal to use in the DAG. For depth-first
|
||||
specs (list): List of root specs (considered to be depth 0)
|
||||
root (bool): Yield the root nodes themselves
|
||||
order (str): What order of traversal to use in the DAG. For depth-first
|
||||
search this can be ``pre`` or ``post``. For BFS this should be ``breadth``.
|
||||
For topological order use ``topo``
|
||||
cover: Determines how extensively to cover the dag. Possible values:
|
||||
cover (str): Determines how extensively to cover the dag. Possible values:
|
||||
``nodes`` -- Visit each unique node in the dag only once.
|
||||
``edges`` -- If a node has been visited once but is reached along a
|
||||
new path, it's accepted, but not recurisvely followed. This traverses
|
||||
@@ -457,15 +445,15 @@ def traverse_edges(
|
||||
``paths`` -- Explore every unique path reachable from the root.
|
||||
This descends into visited subtrees and will accept nodes multiple
|
||||
times if they're reachable by multiple paths.
|
||||
direction: ``children`` or ``parents``. If ``children``, does a traversal
|
||||
direction (str): ``children`` or ``parents``. If ``children``, does a traversal
|
||||
of this spec's children. If ``parents``, traverses upwards in the DAG
|
||||
towards the root.
|
||||
deptype: allowed dependency types
|
||||
depth: When ``False``, yield just edges. When ``True`` yield
|
||||
depth (bool): When ``False``, yield just edges. When ``True`` yield
|
||||
the tuple (depth, edge), where depth corresponds to the depth
|
||||
at which edge.spec was discovered.
|
||||
key: function that takes a spec and outputs a key for uniqueness test.
|
||||
visited: a set of nodes not to follow
|
||||
visited (set or None): a set of nodes not to follow
|
||||
|
||||
Returns:
|
||||
A generator that yields ``DependencySpec`` if depth is ``False``
|
||||
@@ -494,29 +482,29 @@ def traverse_edges(
|
||||
elif order == "breadth":
|
||||
return traverse_breadth_first_edges_generator(root_edges, visitor, root, depth)
|
||||
|
||||
raise ValueError(f"Unknown order {order}")
|
||||
raise ValueError("Unknown order {}".format(order))
|
||||
|
||||
|
||||
def traverse_nodes(
|
||||
specs: Iterable["spack.spec.Spec"],
|
||||
root: bool = True,
|
||||
order: str = "pre",
|
||||
cover: str = "nodes",
|
||||
direction: str = "children",
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.ALL,
|
||||
depth: bool = False,
|
||||
key: Key = id,
|
||||
visited: Optional[set] = None,
|
||||
specs,
|
||||
root=True,
|
||||
order="pre",
|
||||
cover="nodes",
|
||||
direction="children",
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = "all",
|
||||
depth=False,
|
||||
key=id,
|
||||
visited=None,
|
||||
):
|
||||
"""
|
||||
Generator that yields specs from the DAG, starting from a list of root specs.
|
||||
|
||||
Arguments:
|
||||
specs: List of root specs (considered to be depth 0)
|
||||
root: Yield the root nodes themselves
|
||||
order: What order of traversal to use in the DAG. For depth-first
|
||||
specs (list): List of root specs (considered to be depth 0)
|
||||
root (bool): Yield the root nodes themselves
|
||||
order (str): What order of traversal to use in the DAG. For depth-first
|
||||
search this can be ``pre`` or ``post``. For BFS this should be ``breadth``.
|
||||
cover: Determines how extensively to cover the dag. Possible values:
|
||||
cover (str): Determines how extensively to cover the dag. Possible values:
|
||||
``nodes`` -- Visit each unique node in the dag only once.
|
||||
``edges`` -- If a node has been visited once but is reached along a
|
||||
new path, it's accepted, but not recurisvely followed. This traverses
|
||||
@@ -524,15 +512,15 @@ def traverse_nodes(
|
||||
``paths`` -- Explore every unique path reachable from the root.
|
||||
This descends into visited subtrees and will accept nodes multiple
|
||||
times if they're reachable by multiple paths.
|
||||
direction: ``children`` or ``parents``. If ``children``, does a traversal
|
||||
direction (str): ``children`` or ``parents``. If ``children``, does a traversal
|
||||
of this spec's children. If ``parents``, traverses upwards in the DAG
|
||||
towards the root.
|
||||
deptype: allowed dependency types
|
||||
depth: When ``False``, yield just edges. When ``True`` yield
|
||||
depth (bool): When ``False``, yield just edges. When ``True`` yield
|
||||
the tuple ``(depth, edge)``, where depth corresponds to the depth
|
||||
at which ``edge.spec`` was discovered.
|
||||
key: function that takes a spec and outputs a key for uniqueness test.
|
||||
visited: a set of nodes not to follow
|
||||
visited (set or None): a set of nodes not to follow
|
||||
|
||||
Yields:
|
||||
By default :class:`~spack.spec.Spec`, or a tuple ``(depth, Spec)`` if depth is
|
||||
@@ -543,11 +531,7 @@ def traverse_nodes(
|
||||
|
||||
|
||||
def traverse_tree(
|
||||
specs: Iterable["spack.spec.Spec"],
|
||||
cover: str = "nodes",
|
||||
deptype: Union[dt.DepFlag, dt.DepTypes] = dt.ALL,
|
||||
key: Key = id,
|
||||
depth_first: bool = True,
|
||||
specs, cover="nodes", deptype: Union[dt.DepFlag, dt.DepTypes] = "all", key=id, depth_first=True
|
||||
):
|
||||
"""
|
||||
Generator that yields ``(depth, DependencySpec)`` tuples in the depth-first
|
||||
@@ -555,8 +539,8 @@ def traverse_tree(
|
||||
|
||||
Arguments:
|
||||
|
||||
specs: List of root specs (considered to be depth 0)
|
||||
cover: Determines how extensively to cover the dag. Possible values:
|
||||
specs (list): List of root specs (considered to be depth 0)
|
||||
cover (str): Determines how extensively to cover the dag. Possible values:
|
||||
``nodes`` -- Visit each unique node in the dag only once.
|
||||
``edges`` -- If a node has been visited once but is reached along a
|
||||
new path, it's accepted, but not recurisvely followed. This traverses
|
||||
@@ -566,7 +550,7 @@ def traverse_tree(
|
||||
times if they're reachable by multiple paths.
|
||||
deptype: allowed dependency types
|
||||
key: function that takes a spec and outputs a key for uniqueness test.
|
||||
depth_first: Explore the tree in depth-first or breadth-first order.
|
||||
depth_first (bool): Explore the tree in depth-first or breadth-first order.
|
||||
When setting ``depth_first=True`` and ``cover=nodes``, each spec only
|
||||
occurs once at the shallowest level, which is useful when rendering
|
||||
the tree in a terminal.
|
||||
|
||||
@@ -37,16 +37,11 @@ class Gasnet(Package, CudaPackage, ROCmPackage):
|
||||
version("main", branch="stable")
|
||||
version("master", branch="master")
|
||||
|
||||
version("2024.5.0", sha256="f945e80f71d340664766b66290496d230e021df5e5cd88f404d101258446daa9")
|
||||
version("2023.9.0", sha256="2d9f15a794e10683579ce494cd458b0dd97e2d3327c4d17e1fea79bd95576ce6")
|
||||
version("2023.3.0", sha256="e1fa783d38a503cf2efa7662be591ca5c2bb98d19ac72a9bc6da457329a9a14f")
|
||||
version("2022.9.2", sha256="2352d52f395a9aa14cc57d82957d9f1ebd928d0a0021fd26c5f1382a06cd6f1d")
|
||||
version("2022.9.0", sha256="6873ff4ad8ebee49da4378f2d78095a6ccc31333d6ae4cd739b9f772af11f936")
|
||||
version(
|
||||
"2022.3.0",
|
||||
deprecated=True,
|
||||
sha256="91b59aa84c0680c807e00d3d1d8fa7c33c1aed50b86d1616f93e499620a9ba09",
|
||||
)
|
||||
version("2022.3.0", sha256="91b59aa84c0680c807e00d3d1d8fa7c33c1aed50b86d1616f93e499620a9ba09")
|
||||
version(
|
||||
"2021.9.0",
|
||||
deprecated=True,
|
||||
@@ -80,12 +75,12 @@ class Gasnet(Package, CudaPackage, ROCmPackage):
|
||||
"conduits",
|
||||
values=any_combination_of("smp", "mpi", "ibv", "udp", "ofi", "ucx").with_default("smp"),
|
||||
description="The hardware-dependent network backends to enable.\n"
|
||||
+ "(smp) = SMP conduit for single-node operation\n"
|
||||
+ "(ibv) = Native InfiniBand verbs conduit\n"
|
||||
+ "(ofi) = OFI conduit over libfabric, for HPE Cray Slingshot and Intel Omni-Path\n"
|
||||
+ "(udp) = Portable UDP conduit, for Ethernet networks\n"
|
||||
+ "(mpi) = Low-performance/portable MPI conduit\n"
|
||||
+ "(ucx) = EXPERIMENTAL UCX conduit for Mellanox IB/RoCE ConnectX-5+\n"
|
||||
+ "(smp) = SMP conduit for single-node operation ;\n"
|
||||
+ "(ibv) = Native InfiniBand verbs conduit ;\n"
|
||||
+ "(ofi) = OFI conduit over libfabric, for HPE Cray Slingshot and Intel Omni-Path ;\n"
|
||||
+ "(udp) = Portable UDP conduit, for Ethernet networks ;\n"
|
||||
+ "(mpi) = Low-performance/portable MPI conduit ;\n"
|
||||
+ "(ucx) = EXPERIMENTAL UCX conduit for Mellanox IB/RoCE ConnectX-5+ ;\n"
|
||||
+ "For detailed recommendations, consult https://gasnet.lbl.gov",
|
||||
)
|
||||
|
||||
@@ -123,15 +118,12 @@ class Gasnet(Package, CudaPackage, ROCmPackage):
|
||||
)
|
||||
|
||||
depends_on("mpi", when="conduits=mpi")
|
||||
depends_on("libfabric", when="conduits=ofi")
|
||||
|
||||
depends_on("autoconf@2.69", type="build", when="@master:")
|
||||
depends_on("automake@1.16:", type="build", when="@master:")
|
||||
|
||||
conflicts("^hip@:4.4.0", when="+rocm")
|
||||
|
||||
conflicts("^hip@6:", when="@:2024.4+rocm") # Bug 4686
|
||||
|
||||
depends_on("oneapi-level-zero@1.8.0:", when="+level_zero")
|
||||
|
||||
def install(self, spec, prefix):
|
||||
@@ -165,11 +157,9 @@ def install(self, spec, prefix):
|
||||
|
||||
if "+cuda" in spec:
|
||||
options.append("--enable-kind-cuda-uva")
|
||||
options.append("--with-cuda-home=" + spec["cuda"].prefix)
|
||||
|
||||
if "+rocm" in spec:
|
||||
options.append("--enable-kind-hip")
|
||||
options.append("--with-hip-home=" + spec["hip"].prefix)
|
||||
|
||||
if "+level_zero" in spec:
|
||||
options.append("--enable-kind-ze")
|
||||
|
||||
@@ -20,15 +20,12 @@ class Gmsh(CMakePackage):
|
||||
url = "https://gmsh.info/src/gmsh-4.4.1-source.tgz"
|
||||
git = "https://gitlab.onelab.info/gmsh/gmsh.git"
|
||||
|
||||
maintainers("fspiga")
|
||||
|
||||
license("GPL-2.0-or-later")
|
||||
|
||||
version("master", branch="master")
|
||||
version("4.13.0", sha256="0208110adb1792d1c59dcbcbea5d5ecb1272dfef63f69ceedb91923c40e1a652")
|
||||
version("4.12.2", sha256="13e09d9ca8102e5c40171d6ee150c668742b98c3a6ca57f837f7b64e1e2af48f")
|
||||
version("4.12.0", sha256="2a6007872ba85abd9901914826f6986a2437ab7104f564ccefa1b7a3de742c17")
|
||||
version("4.11.1", sha256="c5fe1b7cbd403888a814929f2fd0f5d69e27600222a18c786db5b76e8005b365")
|
||||
version("4.10.3", sha256="a87d59ccea596d493d375b0d6bc380079a5e5a4baebf0d3383018b0cd6bd8e33")
|
||||
version("4.8.4", sha256="760dbdc072eaa3c82d066c5ba3b06eacdd3304eb2a97373fe4ada9509f0b6ace")
|
||||
version("4.7.1", sha256="c984c295116c757ed165d77149bd5fdd1068cbd7835e9bcd077358b503891c6a")
|
||||
|
||||
@@ -16,15 +16,12 @@ class PyFortls(PythonPackage):
|
||||
|
||||
license("MIT")
|
||||
|
||||
version("3.1.0", sha256="e38f9f6af548f78151d54bdbb9884166f8d717f8e147ab1e2dbf06b985df2c6d")
|
||||
version("2.13.0", sha256="23c5013e8dd8e1d65bf07be610d0827bc48aa7331a7a7ce13612d4c646d0db31")
|
||||
|
||||
depends_on("py-setuptools@45:", type="build")
|
||||
depends_on("py-setuptools@61:", when="@3:", type="build")
|
||||
depends_on("py-packaging", type=("build", "run"))
|
||||
depends_on("py-setuptools-scm@6.2:+toml", type="build")
|
||||
depends_on("py-setuptools-scm@7:+toml", when="@3:", type="build")
|
||||
depends_on("py-setuptools-scm-git-archive", when="@:2", type="build")
|
||||
depends_on("py-setuptools-scm-git-archive", type="build")
|
||||
|
||||
depends_on("py-json5", type=("build", "run"))
|
||||
depends_on("py-importlib-metadata", type=("build", "run"), when="^python@:3.7")
|
||||
|
||||
Reference in New Issue
Block a user