summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/dependency.py7
-rw-r--r--lib/spack/spack/package.py50
-rw-r--r--lib/spack/spack/test/package_class.py52
3 files changed, 93 insertions, 16 deletions
diff --git a/lib/spack/spack/dependency.py b/lib/spack/spack/dependency.py
index b8fb1771de..3e427196b5 100644
--- a/lib/spack/spack/dependency.py
+++ b/lib/spack/spack/dependency.py
@@ -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):
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 99dfa0be1c..226e0d1643 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -35,6 +35,7 @@ import spack.paths
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 @@ class PackageBase(with_metaclass(PackageMeta, PackageViewMixin, object)):
@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.
+
+ 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 = set([cls.name])
+ visited = {cls.name: set()}
- for i, name in enumerate(cls.dependencies):
+ 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
diff --git a/lib/spack/spack/test/package_class.py b/lib/spack/spack/test/package_class.py
new file mode 100644
index 0000000000..7ae5c64ec7
--- /dev/null
+++ b/lib/spack/spack/test/package_class.py
@@ -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(),
+ }