Refactor Package dependency metadata
- Previously, dependencies and dependency_types were stored as separate dicts on Package. - This means a package can only depend on another in one specific way, which is usually but not always true. - Prior code unioned dependency types statically across dependencies on the same package. - New code stores dependency relationships as their own object, with a spec constraint and a set of dependency types per relationship. - Dependency types are now more precise - There is now room to add more information to dependency relationships. - New Dependency class lives in dependency.py, along with deptype definitions that used to live in spack.spec. Move deptype definitions to spack.dependency
This commit is contained in:
parent
a3cb6b61ea
commit
0e8bb9ec5e
@ -207,8 +207,11 @@
|
||||
from spack.version import Version, ver
|
||||
__all__ += ['Version', 'ver']
|
||||
|
||||
from spack.spec import Spec, alldeps
|
||||
__all__ += ['Spec', 'alldeps']
|
||||
from spack.spec import Spec
|
||||
__all__ += ['Spec']
|
||||
|
||||
from spack.dependency import all_deptypes
|
||||
__all__ += ['all_deptypes']
|
||||
|
||||
from spack.multimethod import when
|
||||
__all__ += ['when']
|
||||
|
@ -57,7 +57,7 @@ def fetch(parser, args):
|
||||
specs = spack.cmd.parse_specs(args.packages, concretize=True)
|
||||
for spec in specs:
|
||||
if args.missing or args.dependencies:
|
||||
for s in spec.traverse(deptype_query=spack.alldeps):
|
||||
for s in spec.traverse(deptype_query=all):
|
||||
package = spack.repo.get(s)
|
||||
if args.missing and package.installed:
|
||||
continue
|
||||
|
@ -31,6 +31,7 @@
|
||||
import spack.cmd
|
||||
import spack.store
|
||||
from spack.spec import *
|
||||
from spack.dependency import *
|
||||
from spack.graph import *
|
||||
|
||||
description = "generate graphs of package dependency relationships"
|
||||
@ -64,7 +65,7 @@ def setup_parser(subparser):
|
||||
subparser.add_argument(
|
||||
'-t', '--deptype', action='store',
|
||||
help="comma-separated list of deptypes to traverse. default=%s"
|
||||
% ','.join(alldeps))
|
||||
% ','.join(all_deptypes))
|
||||
|
||||
subparser.add_argument(
|
||||
'specs', nargs=argparse.REMAINDER,
|
||||
@ -87,7 +88,7 @@ def graph(parser, args):
|
||||
setup_parser.parser.print_help()
|
||||
return 1
|
||||
|
||||
deptype = alldeps
|
||||
deptype = all_deptypes
|
||||
if args.deptype:
|
||||
deptype = tuple(args.deptype.split(','))
|
||||
if deptype == ('all',):
|
||||
|
@ -170,7 +170,7 @@ def rst_table(elts):
|
||||
reversed(sorted(pkg.versions))))
|
||||
print()
|
||||
|
||||
for deptype in spack.alldeps:
|
||||
for deptype in spack.all_deptypes:
|
||||
deps = pkg.dependencies_of_type(deptype)
|
||||
if deps:
|
||||
print('%s Dependencies' % deptype.capitalize())
|
||||
|
@ -182,7 +182,7 @@ def mirror_create(args):
|
||||
new_specs = set()
|
||||
for spec in specs:
|
||||
spec.concretize()
|
||||
for s in spec.traverse(deptype_query=spack.alldeps):
|
||||
for s in spec.traverse(deptype_query=all):
|
||||
new_specs.add(s)
|
||||
specs = list(new_specs)
|
||||
|
||||
|
@ -401,7 +401,7 @@ def find_spec(spec, condition, default=None):
|
||||
visited.add(id(relative))
|
||||
|
||||
# Then search all other relatives in the DAG *except* spec
|
||||
for relative in spec.root.traverse(deptypes=spack.alldeps):
|
||||
for relative in spec.root.traverse(deptypes=all):
|
||||
if relative is spec:
|
||||
continue
|
||||
if id(relative) in visited:
|
||||
|
90
lib/spack/spack/dependency.py
Normal file
90
lib/spack/spack/dependency.py
Normal file
@ -0,0 +1,90 @@
|
||||
##############################################################################
|
||||
# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
|
||||
# Produced at the Lawrence Livermore National Laboratory.
|
||||
#
|
||||
# This file is part of Spack.
|
||||
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||
# LLNL-CODE-647188
|
||||
#
|
||||
# For details, see https://github.com/llnl/spack
|
||||
# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License (as
|
||||
# published by the Free Software Foundation) version 2.1, February 1999.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||
# conditions of the GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
##############################################################################
|
||||
"""Data structures that represent Spack's dependency relationships.
|
||||
"""
|
||||
from six import string_types
|
||||
|
||||
#: The types of dependency relationships that Spack understands.
|
||||
all_deptypes = ('build', 'link', 'run', 'test')
|
||||
|
||||
#: Default dependency type if none is specified
|
||||
default_deptype = ('build', 'link')
|
||||
|
||||
|
||||
def canonical_deptype(deptype):
|
||||
"""Convert deptype to a canonical sorted tuple, or raise ValueError.
|
||||
|
||||
Args:
|
||||
deptype (str or list or tuple): string representing dependency
|
||||
type, or a list/tuple of such strings. Can also be the
|
||||
builtin function ``all`` or the string 'all', which result in
|
||||
a tuple of all dependency types known to Spack.
|
||||
"""
|
||||
if deptype in ('all', all):
|
||||
return all_deptypes
|
||||
|
||||
elif isinstance(deptype, string_types):
|
||||
if deptype not in all_deptypes:
|
||||
raise ValueError('Invalid dependency type: %s' % deptype)
|
||||
return (deptype,)
|
||||
|
||||
elif isinstance(deptype, (tuple, list)):
|
||||
bad = [d for d in deptype if d not in all_deptypes]
|
||||
if bad:
|
||||
raise ValueError(
|
||||
'Invalid dependency types: %s' % ','.join(str(t) for t in bad))
|
||||
return tuple(sorted(deptype))
|
||||
|
||||
elif deptype is None:
|
||||
raise ValueError('Invalid dependency type: None')
|
||||
|
||||
return deptype
|
||||
|
||||
|
||||
class Dependency(object):
|
||||
"""Class representing metadata for a dependency on a package.
|
||||
|
||||
This class differs from ``spack.spec.DependencySpec`` because it
|
||||
represents metadata at the ``Package`` level.
|
||||
``spack.spec.DependencySpec`` is a descriptor for an actual package
|
||||
confiuguration, while ``Dependency`` is a descriptor for a package's
|
||||
dependency *requirements*.
|
||||
|
||||
A dependency is a requirement for a configuration of another package
|
||||
that satisfies a particular spec. The dependency can have *types*,
|
||||
which determine *how* that package configuration is required,
|
||||
e.g. whether it is required for building the package, whether it
|
||||
needs to be linked to, or whether it is needed at runtime so that
|
||||
Spack can call commands from it.
|
||||
"""
|
||||
def __init__(self, spec, type=default_deptype):
|
||||
"""Create a new Dependency.
|
||||
|
||||
Args:
|
||||
spec (Spec): Spec indicating dependency requirements
|
||||
type (sequence): strings describing dependency relationship
|
||||
"""
|
||||
self.spec = spec
|
||||
self.type = set(type)
|
@ -54,11 +54,13 @@ class OpenMpi(Package):
|
||||
from six import string_types
|
||||
|
||||
import llnl.util.lang
|
||||
from llnl.util.filesystem import join_path
|
||||
|
||||
import spack
|
||||
import spack.error
|
||||
import spack.spec
|
||||
import spack.url
|
||||
from llnl.util.filesystem import join_path
|
||||
from spack.dependency import *
|
||||
from spack.fetch_strategy import from_kwargs
|
||||
from spack.patch import Patch
|
||||
from spack.resource import Resource
|
||||
@ -68,6 +70,9 @@ class OpenMpi(Package):
|
||||
|
||||
__all__ = []
|
||||
|
||||
#: These are variant names used by Spack internally; packages can't use them
|
||||
reserved_names = ['patches']
|
||||
|
||||
|
||||
class DirectiveMetaMixin(type):
|
||||
"""Flushes the directives that were temporarily stored in the staging
|
||||
@ -224,7 +229,7 @@ def _execute(pkg):
|
||||
return _execute
|
||||
|
||||
|
||||
def _depends_on(pkg, spec, when=None, type=None):
|
||||
def _depends_on(pkg, spec, when=None, type=default_deptype):
|
||||
# If when is False do nothing
|
||||
if when is False:
|
||||
return
|
||||
@ -233,33 +238,18 @@ def _depends_on(pkg, spec, when=None, type=None):
|
||||
when = pkg.name
|
||||
when_spec = parse_anonymous_spec(when, pkg.name)
|
||||
|
||||
if type is None:
|
||||
# The default deptype is build and link because the common case is to
|
||||
# build against a library which then turns into a runtime dependency
|
||||
# due to the linker.
|
||||
# XXX(deptype): Add 'run' to this? It's an uncommon dependency type,
|
||||
# but is most backwards-compatible.
|
||||
type = ('build', 'link')
|
||||
|
||||
type = spack.spec.canonical_deptype(type)
|
||||
|
||||
for deptype in type:
|
||||
if deptype not in spack.spec.alldeps:
|
||||
raise UnknownDependencyTypeError('depends_on', pkg.name, deptype)
|
||||
|
||||
dep_spec = Spec(spec)
|
||||
if pkg.name == dep_spec.name:
|
||||
raise CircularReferenceError('depends_on', pkg.name)
|
||||
|
||||
pkg_deptypes = pkg.dependency_types.setdefault(dep_spec.name, set())
|
||||
for deptype in type:
|
||||
pkg_deptypes.add(deptype)
|
||||
|
||||
type = canonical_deptype(type)
|
||||
conditions = pkg.dependencies.setdefault(dep_spec.name, {})
|
||||
if when_spec in conditions:
|
||||
conditions[when_spec].constrain(dep_spec, deps=False)
|
||||
if when_spec not in conditions:
|
||||
conditions[when_spec] = Dependency(dep_spec, type)
|
||||
else:
|
||||
conditions[when_spec] = dep_spec
|
||||
dependency = conditions[when_spec]
|
||||
dependency.spec.constrain(dep_spec, deps=False)
|
||||
dependency.type |= set(type)
|
||||
|
||||
|
||||
@directive('conflicts')
|
||||
@ -293,8 +283,8 @@ def _execute(pkg):
|
||||
return _execute
|
||||
|
||||
|
||||
@directive(('dependencies', 'dependency_types'))
|
||||
def depends_on(spec, when=None, type=None):
|
||||
@directive(('dependencies'))
|
||||
def depends_on(spec, when=None, type=default_deptype):
|
||||
"""Creates a dict of deps with specs defining when they apply.
|
||||
This directive is to be used inside a Package definition to declare
|
||||
that the package requires other packages to be built first.
|
||||
@ -304,7 +294,7 @@ def _execute(pkg):
|
||||
return _execute
|
||||
|
||||
|
||||
@directive(('extendees', 'dependencies', 'dependency_types'))
|
||||
@directive(('extendees', 'dependencies'))
|
||||
def extends(spec, **kwargs):
|
||||
"""Same as depends_on, but dependency is symlinked into parent prefix.
|
||||
|
||||
@ -372,10 +362,12 @@ def patch(url_or_filename, level=1, when=None, **kwargs):
|
||||
def _execute(pkg):
|
||||
constraint = pkg.name if when is None else when
|
||||
when_spec = parse_anonymous_spec(constraint, pkg.name)
|
||||
cur_patches = pkg.patches.setdefault(when_spec, [])
|
||||
|
||||
# if this spec is identical to some other, then append this
|
||||
# patch to the existing list.
|
||||
cur_patches = pkg.patches.setdefault(when_spec, [])
|
||||
cur_patches.append(Patch.create(pkg, url_or_filename, level, **kwargs))
|
||||
|
||||
return _execute
|
||||
|
||||
|
||||
@ -406,6 +398,9 @@ def variant(
|
||||
logic. It receives a tuple of values and should raise an instance
|
||||
of SpackError if the group doesn't meet the additional constraints
|
||||
"""
|
||||
if name in reserved_names:
|
||||
raise ValueError("The variant name '%s' is reserved by Spack" % name)
|
||||
|
||||
if values is None:
|
||||
if default in (True, False) or (type(default) is str and
|
||||
default.upper() in ('TRUE', 'FALSE')):
|
||||
|
@ -69,11 +69,12 @@
|
||||
from llnl.util.tty.color import *
|
||||
|
||||
from spack.spec import *
|
||||
from spack.dependency import *
|
||||
|
||||
__all__ = ['topological_sort', 'graph_ascii', 'AsciiGraph', 'graph_dot']
|
||||
|
||||
|
||||
def topological_sort(spec, reverse=False, deptype=None):
|
||||
def topological_sort(spec, reverse=False, deptype='all'):
|
||||
"""Topological sort for specs.
|
||||
|
||||
Return a list of dependency specs sorted topologically. The spec
|
||||
@ -142,7 +143,7 @@ def __init__(self):
|
||||
self.node_character = 'o'
|
||||
self.debug = False
|
||||
self.indent = 0
|
||||
self.deptype = alldeps
|
||||
self.deptype = all_deptypes
|
||||
|
||||
# These are colors in the order they'll be used for edges.
|
||||
# See llnl.util.tty.color for details on color characters.
|
||||
@ -494,7 +495,7 @@ def write(self, spec, color=None, out=None):
|
||||
|
||||
|
||||
def graph_ascii(spec, node='o', out=None, debug=False,
|
||||
indent=0, color=None, deptype=None):
|
||||
indent=0, color=None, deptype='all'):
|
||||
graph = AsciiGraph()
|
||||
graph.debug = debug
|
||||
graph.indent = indent
|
||||
@ -505,7 +506,7 @@ def graph_ascii(spec, node='o', out=None, debug=False,
|
||||
graph.write(spec, color=color, out=out)
|
||||
|
||||
|
||||
def graph_dot(specs, deptype=None, static=False, out=None):
|
||||
def graph_dot(specs, deptype='all', static=False, out=None):
|
||||
"""Generate a graph in dot format of all provided specs.
|
||||
|
||||
Print out a dot formatted graph of all the dependencies between
|
||||
@ -516,9 +517,7 @@ def graph_dot(specs, deptype=None, static=False, out=None):
|
||||
"""
|
||||
if out is None:
|
||||
out = sys.stdout
|
||||
|
||||
if deptype is None:
|
||||
deptype = alldeps
|
||||
deptype = canonical_deptype(deptype)
|
||||
|
||||
out.write('digraph G {\n')
|
||||
out.write(' labelloc = "b"\n')
|
||||
|
@ -820,9 +820,18 @@ def fetcher(self, f):
|
||||
self._fetcher = f
|
||||
|
||||
def dependencies_of_type(self, *deptypes):
|
||||
"""Get subset of the dependencies with certain types."""
|
||||
return dict((name, conds) for name, conds in self.dependencies.items()
|
||||
if any(d in self.dependency_types[name] for d in deptypes))
|
||||
"""Get dependencies that can possibly have these deptypes.
|
||||
|
||||
This analyzes the package and determines which dependencies *can*
|
||||
be a certain kind of dependency. Note that they may not *always*
|
||||
be this kind of dependency, since dependencies can be optional,
|
||||
so something may be a build dependency in one configuration and a
|
||||
run dependency in another.
|
||||
"""
|
||||
return dict(
|
||||
(name, conds) for name, conds in self.dependencies.items()
|
||||
if any(dt in self.dependencies[name][cond].type
|
||||
for cond in conds for dt in deptypes))
|
||||
|
||||
@property
|
||||
def extendee_spec(self):
|
||||
@ -1954,7 +1963,7 @@ def dump_packages(spec, path):
|
||||
# Note that we copy them in as they are in the *install* directory
|
||||
# NOT as they are in the repository, because we want a snapshot of
|
||||
# how *this* particular build was done.
|
||||
for node in spec.traverse(deptype=spack.alldeps):
|
||||
for node in spec.traverse(deptype=all):
|
||||
if node is not spec:
|
||||
# Locate the dependency package in the install tree and find
|
||||
# its provenance information.
|
||||
|
@ -97,7 +97,6 @@ def apply(self, stage):
|
||||
patch('-s', '-p', str(self.level), '-i', self.path)
|
||||
|
||||
|
||||
|
||||
class FilePatch(Patch):
|
||||
"""Describes a patch that is retrieved from a file in the repository"""
|
||||
def __init__(self, pkg, path_or_url, level):
|
||||
|
@ -121,6 +121,7 @@
|
||||
import spack.util.spack_json as sjson
|
||||
import spack.util.spack_yaml as syaml
|
||||
|
||||
from spack.dependency import *
|
||||
from spack.util.module_cmd import get_path_from_module, load_module
|
||||
from spack.error import SpecError, UnsatisfiableSpecError
|
||||
from spack.provider_index import ProviderIndex
|
||||
@ -135,8 +136,6 @@
|
||||
|
||||
__all__ = [
|
||||
'Spec',
|
||||
'alldeps',
|
||||
'canonical_deptype',
|
||||
'parse',
|
||||
'parse_anonymous_spec',
|
||||
'SpecError',
|
||||
@ -196,31 +195,10 @@
|
||||
#: every time we call str()
|
||||
_any_version = VersionList([':'])
|
||||
|
||||
#: Types of dependencies that Spack understands.
|
||||
alldeps = ('build', 'link', 'run', 'test')
|
||||
|
||||
#: Max integer helps avoid passing too large a value to cyaml.
|
||||
maxint = 2 ** (ctypes.sizeof(ctypes.c_int) * 8 - 1) - 1
|
||||
|
||||
|
||||
def canonical_deptype(deptype):
|
||||
if deptype in (None, 'all', all):
|
||||
return alldeps
|
||||
|
||||
elif isinstance(deptype, string_types):
|
||||
if deptype not in alldeps:
|
||||
raise ValueError('Invalid dependency type: %s' % deptype)
|
||||
return (deptype,)
|
||||
|
||||
elif isinstance(deptype, (tuple, list)):
|
||||
invalid = next((d for d in deptype if d not in alldeps), None)
|
||||
if invalid:
|
||||
raise ValueError('Invalid dependency type: %s' % invalid)
|
||||
return tuple(sorted(deptype))
|
||||
|
||||
return deptype
|
||||
|
||||
|
||||
def colorize_spec(spec):
|
||||
"""Returns a spec colorized according to the colors specified in
|
||||
color_formats."""
|
||||
@ -1098,19 +1076,19 @@ def _find_deps(self, where, deptype):
|
||||
if deptype and (not dep.deptypes or
|
||||
any(d in deptype for d in dep.deptypes))]
|
||||
|
||||
def dependencies(self, deptype=None):
|
||||
def dependencies(self, deptype='all'):
|
||||
return [d.spec
|
||||
for d in self._find_deps(self._dependencies, deptype)]
|
||||
|
||||
def dependents(self, deptype=None):
|
||||
def dependents(self, deptype='all'):
|
||||
return [d.parent
|
||||
for d in self._find_deps(self._dependents, deptype)]
|
||||
|
||||
def dependencies_dict(self, deptype=None):
|
||||
def dependencies_dict(self, deptype='all'):
|
||||
return dict((d.spec.name, d)
|
||||
for d in self._find_deps(self._dependencies, deptype))
|
||||
|
||||
def dependents_dict(self, deptype=None):
|
||||
def dependents_dict(self, deptype='all'):
|
||||
return dict((d.parent.name, d)
|
||||
for d in self._find_deps(self._dependents, deptype))
|
||||
|
||||
@ -1271,8 +1249,8 @@ def traverse(self, **kwargs):
|
||||
for dspec in self.traverse_edges(**kwargs):
|
||||
yield get_spec(dspec)
|
||||
|
||||
def traverse_edges(self, visited=None, d=0, deptype=None,
|
||||
deptype_query=None, dep_spec=None, **kwargs):
|
||||
def traverse_edges(self, visited=None, d=0, deptype='all',
|
||||
deptype_query=default_deptype, dep_spec=None, **kwargs):
|
||||
"""Generic traversal of the DAG represented by this spec.
|
||||
This will yield each node in the spec. Options:
|
||||
|
||||
@ -1325,8 +1303,7 @@ def traverse_edges(self, visited=None, d=0, deptype=None,
|
||||
order = kwargs.get('order', 'pre')
|
||||
|
||||
deptype = canonical_deptype(deptype)
|
||||
if deptype_query is None:
|
||||
deptype_query = ('link', 'run')
|
||||
deptype_query = canonical_deptype(deptype_query)
|
||||
|
||||
# Make sure kwargs have legal values; raise ValueError if not.
|
||||
def validate(name, val, allowed_values):
|
||||
@ -1817,7 +1794,7 @@ def concretize(self):
|
||||
changed = any(changes)
|
||||
force = True
|
||||
|
||||
for s in self.traverse(deptype_query=alldeps):
|
||||
for s in self.traverse(deptype_query=all):
|
||||
# After concretizing, assign namespaces to anything left.
|
||||
# Note that this doesn't count as a "change". The repository
|
||||
# configuration is constant throughout a spack run, and
|
||||
@ -1864,7 +1841,7 @@ def _mark_concrete(self, value=True):
|
||||
Only for internal use -- client code should use "concretize"
|
||||
unless there is a need to force a spec to be concrete.
|
||||
"""
|
||||
for s in self.traverse(deptype_query=alldeps):
|
||||
for s in self.traverse(deptype_query=all):
|
||||
s._normal = value
|
||||
s._concrete = value
|
||||
|
||||
@ -1887,7 +1864,7 @@ def flat_dependencies(self, **kwargs):
|
||||
returns them.
|
||||
"""
|
||||
copy = kwargs.get('copy', True)
|
||||
deptype_query = kwargs.get('deptype_query')
|
||||
deptype_query = kwargs.get('deptype_query', 'all')
|
||||
|
||||
flat_deps = {}
|
||||
try:
|
||||
@ -1916,7 +1893,7 @@ def flat_dependencies(self, **kwargs):
|
||||
# parser doesn't allow it. Spack must be broken!
|
||||
raise InconsistentSpecError("Invalid Spec DAG: %s" % e.message)
|
||||
|
||||
def index(self, deptype=None):
|
||||
def index(self, deptype='all'):
|
||||
"""Return DependencyMap that points to all the dependencies in this
|
||||
spec."""
|
||||
dm = DependencyMap()
|
||||
@ -1927,28 +1904,39 @@ def index(self, deptype=None):
|
||||
def _evaluate_dependency_conditions(self, name):
|
||||
"""Evaluate all the conditions on a dependency with this name.
|
||||
|
||||
If the package depends on <name> in this configuration, return
|
||||
the dependency. If no conditions are True (and we don't
|
||||
depend on it), return None.
|
||||
Args:
|
||||
name (str): name of dependency to evaluate conditions on.
|
||||
|
||||
Returns:
|
||||
(tuple): tuple of ``Spec`` and tuple of ``deptypes``.
|
||||
|
||||
If the package depends on <name> in the current spec
|
||||
configuration, return the constrained dependency and
|
||||
corresponding dependency types.
|
||||
|
||||
If no conditions are True (and we don't depend on it), return
|
||||
``(None, None)``.
|
||||
"""
|
||||
pkg = spack.repo.get(self.fullname)
|
||||
conditions = pkg.dependencies[name]
|
||||
|
||||
substitute_abstract_variants(self)
|
||||
# evaluate when specs to figure out constraints on the dependency.
|
||||
dep = None
|
||||
for when_spec, dep_spec in conditions.items():
|
||||
sat = self.satisfies(when_spec, strict=True)
|
||||
if sat:
|
||||
dep, deptypes = None, None
|
||||
for when_spec, dependency in conditions.items():
|
||||
if self.satisfies(when_spec, strict=True):
|
||||
if dep is None:
|
||||
dep = Spec(name)
|
||||
deptypes = set()
|
||||
try:
|
||||
dep.constrain(dep_spec)
|
||||
dep.constrain(dependency.spec)
|
||||
deptypes |= dependency.type
|
||||
except UnsatisfiableSpecError as e:
|
||||
e.message = ("Conflicting conditional dependencies on"
|
||||
"package %s for spec %s" % (self.name, self))
|
||||
raise e
|
||||
return dep
|
||||
|
||||
return dep, deptypes
|
||||
|
||||
def _find_provider(self, vdep, provider_index):
|
||||
"""Find provider for a virtual spec in the provider index.
|
||||
@ -2086,13 +2074,12 @@ def _normalize_helper(self, visited, spec_deps, provider_index):
|
||||
changed = False
|
||||
for dep_name in pkg.dependencies:
|
||||
# Do we depend on dep_name? If so pkg_dep is not None.
|
||||
pkg_dep = self._evaluate_dependency_conditions(dep_name)
|
||||
deptypes = pkg.dependency_types[dep_name]
|
||||
# If pkg_dep is a dependency, merge it.
|
||||
if pkg_dep and (spack.package_testing.check(self.name) or
|
||||
set(deptypes) - set(['test'])):
|
||||
dep, deptypes = self._evaluate_dependency_conditions(dep_name)
|
||||
# If dep is a needed dependency, merge it.
|
||||
if dep and (spack.package_testing.check(self.name) or
|
||||
set(deptypes) - set(['test'])):
|
||||
changed |= self._merge_dependency(
|
||||
pkg_dep, deptypes, visited, spec_deps, provider_index)
|
||||
dep, deptypes, visited, spec_deps, provider_index)
|
||||
any_change |= changed
|
||||
|
||||
return any_change
|
||||
@ -2127,7 +2114,7 @@ def normalize(self, force=False):
|
||||
# Ensure first that all packages & compilers in the DAG exist.
|
||||
self.validate_or_raise()
|
||||
# Get all the dependencies into one DependencyMap
|
||||
spec_deps = self.flat_dependencies(copy=False, deptype_query=alldeps)
|
||||
spec_deps = self.flat_dependencies(copy=False, deptype_query=all)
|
||||
|
||||
# Initialize index of virtual dependency providers if
|
||||
# concretize didn't pass us one already
|
||||
@ -2536,13 +2523,15 @@ def _dup(self, other, deps=True, cleardeps=True, caches=None):
|
||||
# If we preserved the original structure, we can copy them
|
||||
# safely. If not, they need to be recomputed.
|
||||
if caches is None:
|
||||
caches = (deps is True or deps == alldeps)
|
||||
caches = (deps is True or deps == all_deptypes)
|
||||
|
||||
# If we copy dependencies, preserve DAG structure in the new spec
|
||||
if deps:
|
||||
# If caller restricted deptypes to be copied, adjust that here.
|
||||
# By default, just copy all deptypes
|
||||
deptypes = deps if isinstance(deps, (tuple, list)) else alldeps
|
||||
deptypes = all_deptypes
|
||||
if isinstance(deps, (tuple, list)):
|
||||
deptypes = deps
|
||||
self._dup_deps(other, deptypes, caches)
|
||||
|
||||
if caches:
|
||||
@ -3013,10 +3002,10 @@ def tree(self, **kwargs):
|
||||
if show_types:
|
||||
out += '['
|
||||
if dep_spec.deptypes:
|
||||
for t in alldeps:
|
||||
for t in all_deptypes:
|
||||
out += ''.join(t[0] if t in dep_spec.deptypes else ' ')
|
||||
else:
|
||||
out += ' ' * len(alldeps)
|
||||
out += ' ' * len(all_deptypes)
|
||||
out += '] '
|
||||
|
||||
out += (" " * d)
|
||||
|
@ -42,6 +42,7 @@
|
||||
import spack.stage
|
||||
import spack.util.executable
|
||||
import spack.util.pattern
|
||||
from spack.dependency import *
|
||||
from spack.package import PackageBase
|
||||
from spack.fetch_strategy import *
|
||||
from spack.spec import Spec
|
||||
@ -567,20 +568,22 @@ def __init__(self, name, dependencies, dependency_types, conditions=None,
|
||||
versions=None):
|
||||
self.name = name
|
||||
self.spec = None
|
||||
dep_to_conditions = ordereddict_backport.OrderedDict()
|
||||
for dep in dependencies:
|
||||
self.dependencies = ordereddict_backport.OrderedDict()
|
||||
|
||||
assert len(dependencies) == len(dependency_types)
|
||||
for dep, dtype in zip(dependencies, dependency_types):
|
||||
d = Dependency(Spec(dep.name), type=dtype)
|
||||
if not conditions or dep.name not in conditions:
|
||||
dep_to_conditions[dep.name] = {name: dep.name}
|
||||
self.dependencies[dep.name] = {Spec(name): d}
|
||||
else:
|
||||
dep_to_conditions[dep.name] = conditions[dep.name]
|
||||
self.dependencies = dep_to_conditions
|
||||
self.dependency_types = dict(
|
||||
(x.name, y) for x, y in zip(dependencies, dependency_types))
|
||||
self.dependencies[dep.name] = {Spec(conditions[dep.name]): d}
|
||||
|
||||
if versions:
|
||||
self.versions = versions
|
||||
else:
|
||||
versions = list(Version(x) for x in [1, 2, 3])
|
||||
self.versions = dict((x, {'preferred': False}) for x in versions)
|
||||
|
||||
self.variants = {}
|
||||
self.provided = {}
|
||||
self.conflicts = {}
|
||||
@ -589,18 +592,18 @@ def __init__(self, name, dependencies, dependency_types, conditions=None,
|
||||
class MockPackageMultiRepo(object):
|
||||
|
||||
def __init__(self, packages):
|
||||
self.specToPkg = dict((x.name, x) for x in packages)
|
||||
self.spec_to_pkg = dict((x.name, x) for x in packages)
|
||||
|
||||
def get(self, spec):
|
||||
if not isinstance(spec, spack.spec.Spec):
|
||||
spec = Spec(spec)
|
||||
return self.specToPkg[spec.name]
|
||||
return self.spec_to_pkg[spec.name]
|
||||
|
||||
def get_pkg_class(self, name):
|
||||
return self.specToPkg[name]
|
||||
return self.spec_to_pkg[name]
|
||||
|
||||
def exists(self, name):
|
||||
return name in self.specToPkg
|
||||
return name in self.spec_to_pkg
|
||||
|
||||
def is_virtual(self, name):
|
||||
return False
|
||||
|
@ -30,8 +30,9 @@
|
||||
import spack.architecture
|
||||
import spack.package
|
||||
|
||||
from spack.spec import Spec
|
||||
from spack.dependency import *
|
||||
from spack.test.conftest import MockPackage, MockPackageMultiRepo
|
||||
from spack.spec import Spec, canonical_deptype, alldeps
|
||||
|
||||
|
||||
def check_links(spec_to_check):
|
||||
@ -54,7 +55,7 @@ def set_dependency(saved_deps):
|
||||
"""Returns a function that alters the dependency information
|
||||
for a package.
|
||||
"""
|
||||
def _mock(pkg_name, spec, deptypes=spack.alldeps):
|
||||
def _mock(pkg_name, spec, deptypes=all_deptypes):
|
||||
"""Alters dependence information for a package.
|
||||
|
||||
Adds a dependency on <spec> to pkg. Use this to mock up constraints.
|
||||
@ -65,8 +66,9 @@ def _mock(pkg_name, spec, deptypes=spack.alldeps):
|
||||
if pkg_name not in saved_deps:
|
||||
saved_deps[pkg_name] = (pkg, pkg.dependencies.copy())
|
||||
|
||||
pkg.dependencies[spec.name] = {Spec(pkg_name): spec}
|
||||
pkg.dependency_types[spec.name] = set(deptypes)
|
||||
cond = Spec(pkg.name)
|
||||
dependency = Dependency(spec, deptypes)
|
||||
pkg.dependencies[spec.name] = {cond: dependency}
|
||||
return _mock
|
||||
|
||||
|
||||
@ -592,7 +594,7 @@ def test_deptype_traversal_full(self):
|
||||
'dtlink1', 'dtlink3', 'dtlink4', 'dtrun1', 'dtlink5',
|
||||
'dtrun3', 'dtbuild3']
|
||||
|
||||
traversal = dag.traverse(deptype=spack.alldeps)
|
||||
traversal = dag.traverse(deptype=all)
|
||||
assert [x.name for x in traversal] == names
|
||||
|
||||
def test_deptype_traversal_run(self):
|
||||
@ -841,12 +843,16 @@ def test_getitem_exceptional_paths(self):
|
||||
|
||||
def test_canonical_deptype(self):
|
||||
# special values
|
||||
assert canonical_deptype(all) == alldeps
|
||||
assert canonical_deptype('all') == alldeps
|
||||
assert canonical_deptype(None) == alldeps
|
||||
assert canonical_deptype(all) == all_deptypes
|
||||
assert canonical_deptype('all') == all_deptypes
|
||||
|
||||
# everything in alldeps is canonical
|
||||
for v in alldeps:
|
||||
with pytest.raises(ValueError):
|
||||
canonical_deptype(None)
|
||||
with pytest.raises(ValueError):
|
||||
canonical_deptype([None])
|
||||
|
||||
# everything in all_deptypes is canonical
|
||||
for v in all_deptypes:
|
||||
assert canonical_deptype(v) == (v,)
|
||||
|
||||
# tuples
|
||||
|
Loading…
Reference in New Issue
Block a user