package: make possible_dependencies consider deptypes
- `PackageBase.possible_dependencies` now: - accepts a deptype param that controls dependency types traversed - returns a dict mapping possible depnames to their immediate possible dependencies (this lets you build a graph easily) - Add tests for PackageBaes
This commit is contained in:
parent
087a511da7
commit
3dac78fc19
3 changed files with 95 additions and 18 deletions
|
@ -34,17 +34,14 @@ def canonical_deptype(deptype):
|
|||
raise ValueError('Invalid dependency type: %s' % deptype)
|
||||
return (deptype,)
|
||||
|
||||
elif isinstance(deptype, (tuple, list)):
|
||||
elif isinstance(deptype, (tuple, list, set)):
|
||||
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
|
||||
raise ValueError('Invalid dependency type: %s' % repr(deptype))
|
||||
|
||||
|
||||
class Dependency(object):
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
import spack.store
|
||||
import spack.compilers
|
||||
import spack.directives
|
||||
import spack.dependency
|
||||
import spack.directory_layout
|
||||
import spack.error
|
||||
import spack.fetch_strategy as fs
|
||||
|
@ -530,39 +531,66 @@ def installed_upstream(self):
|
|||
|
||||
@classmethod
|
||||
def possible_dependencies(
|
||||
cls, transitive=True, expand_virtuals=True, visited=None):
|
||||
"""Return set of possible transitive dependencies of this package.
|
||||
|
||||
Note: the set returned *includes* the package itself.
|
||||
cls, transitive=True, expand_virtuals=True, deptype='all',
|
||||
visited=None):
|
||||
"""Return dict of possible dependencies of this package.
|
||||
|
||||
Args:
|
||||
transitive (bool): return all transitive dependencies if True,
|
||||
only direct dependencies if False.
|
||||
expand_virtuals (bool): expand virtual dependencies into all
|
||||
possible implementations.
|
||||
deptype (str or tuple): dependency types to consider
|
||||
visited (set): set of names of dependencies visited so far.
|
||||
"""
|
||||
if visited is None:
|
||||
visited = set([cls.name])
|
||||
|
||||
for i, name in enumerate(cls.dependencies):
|
||||
Returns:
|
||||
(dict): dictionary mapping dependency names to *their*
|
||||
immediate dependencies
|
||||
|
||||
Each item in the returned dictionary maps a (potentially
|
||||
transitive) dependency of this package to its possible
|
||||
*immediate* dependencies. If ``expand_virtuals`` is ``False``,
|
||||
virtual package names wil be inserted as keys mapped to empty
|
||||
sets of dependencies. Virtuals, if not expanded, are treated as
|
||||
though they have no immediate dependencies
|
||||
|
||||
Note: the returned dict *includes* the package itself.
|
||||
|
||||
"""
|
||||
deptype = spack.dependency.canonical_deptype(deptype)
|
||||
|
||||
if visited is None:
|
||||
visited = {cls.name: set()}
|
||||
|
||||
for name, conditions in cls.dependencies.items():
|
||||
# check whether this dependency could be of the type asked for
|
||||
types = [dep.type for cond, dep in conditions.items()]
|
||||
types = set.union(*types)
|
||||
if not any(d in types for d in deptype):
|
||||
continue
|
||||
|
||||
# expand virtuals if enabled, otherwise just stop at virtuals
|
||||
if spack.repo.path.is_virtual(name):
|
||||
if expand_virtuals:
|
||||
providers = spack.repo.path.providers_for(name)
|
||||
dep_names = [spec.name for spec in providers]
|
||||
else:
|
||||
visited.add(name)
|
||||
visited.setdefault(name, set())
|
||||
continue
|
||||
else:
|
||||
dep_names = [name]
|
||||
|
||||
# add the dependency names to the visited dict
|
||||
visited.setdefault(cls.name, set()).update(set(dep_names))
|
||||
|
||||
# recursively traverse dependencies
|
||||
for dep_name in dep_names:
|
||||
if dep_name not in visited:
|
||||
visited.add(dep_name)
|
||||
visited.setdefault(dep_name, set())
|
||||
if transitive:
|
||||
pkg = spack.repo.get(dep_name)
|
||||
pkg.possible_dependencies(
|
||||
transitive, expand_virtuals, visited)
|
||||
dep_cls = spack.repo.path.get_pkg_class(dep_name)
|
||||
dep_cls.possible_dependencies(
|
||||
transitive, expand_virtuals, deptype, visited)
|
||||
|
||||
return visited
|
||||
|
||||
|
|
52
lib/spack/spack/test/package_class.py
Normal file
52
lib/spack/spack/test/package_class.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
"""Test class methods on Package objects.
|
||||
|
||||
This doesn't include methods on package *instances* (like do_install(),
|
||||
etc.). Only methods like ``possible_dependencies()`` that deal with the
|
||||
static DSL metadata for packages.
|
||||
"""
|
||||
|
||||
import spack.repo
|
||||
|
||||
|
||||
def test_possible_dependencies(mock_packages):
|
||||
mpileaks = spack.repo.get('mpileaks')
|
||||
mpi_names = [spec.name for spec in spack.repo.path.providers_for('mpi')]
|
||||
|
||||
assert mpileaks.possible_dependencies() == {
|
||||
'callpath': set(['dyninst'] + mpi_names),
|
||||
'dyninst': set(['libdwarf', 'libelf']),
|
||||
'fake': set(),
|
||||
'libdwarf': set(['libelf']),
|
||||
'libelf': set(),
|
||||
'mpich': set(),
|
||||
'mpich2': set(),
|
||||
'mpileaks': set(['callpath'] + mpi_names),
|
||||
'multi-provider-mpi': set(),
|
||||
'zmpi': set(['fake']),
|
||||
}
|
||||
|
||||
|
||||
def test_possible_dependencies_with_deptypes(mock_packages):
|
||||
dtbuild1 = spack.repo.get('dtbuild1')
|
||||
|
||||
assert dtbuild1.possible_dependencies(deptype=('link', 'run')) == {
|
||||
'dtbuild1': set(['dtrun2', 'dtlink2']),
|
||||
'dtlink2': set(),
|
||||
'dtrun2': set(),
|
||||
}
|
||||
|
||||
assert dtbuild1.possible_dependencies(deptype=('build')) == {
|
||||
'dtbuild1': set(['dtbuild2', 'dtlink2']),
|
||||
'dtbuild2': set(),
|
||||
'dtlink2': set(),
|
||||
}
|
||||
|
||||
assert dtbuild1.possible_dependencies(deptype=('link')) == {
|
||||
'dtbuild1': set(['dtlink2']),
|
||||
'dtlink2': set(),
|
||||
}
|
Loading…
Reference in a new issue