Spec.satisfies accesses Spec.concrete as property (#2928)

* Spec.satisfies accesses Spec.concrete as property

Fixes #2760

When copying a spec, _concrete is always set to False for each
dependency. "Spec.satisfies" was accessing the member "_concrete"
directly instead of using the property "concrete". This means that
if you copy a spec, the dependencies will be considered equal, but
did not necessarily satisfy one another. Spec.satisfies is a
prerequisite for a package to be considered an extension; as a
consequence, an extension with run-time dependencies that were also
extensions did not activate those extensions. This updates
Spec.satisfies to avoid checking the cached member "_concrete"
directly.

* Added test to check for activation of dependency extension

* Added test to check for transitive satisfiability between a spec and its copy
This commit is contained in:
scheibelp
2017-01-25 20:43:12 -08:00
committed by Todd Gamblin
parent 596190c83c
commit e4d2d747ce
7 changed files with 143 additions and 6 deletions

View File

@@ -1560,10 +1560,9 @@ def do_activate(self, force=False):
# Activate any package dependencies that are also extensions.
if not force:
for spec in self.spec.traverse(root=False, deptype='run'):
if spec.package.extends(self.extendee_spec):
if not spec.package.activated:
spec.package.do_activate(force=force)
for spec in self.dependency_activations():
if not spec.package.activated:
spec.package.do_activate(force=force)
self.extendee_spec.package.activate(self, **self.extendee_args)
@@ -1571,6 +1570,10 @@ def do_activate(self, force=False):
tty.msg("Activated extension %s for %s" %
(self.spec.short_spec, self.extendee_spec.format("$_$@$+$%@")))
def dependency_activations(self):
return (spec for spec in self.spec.traverse(root=False, deptype='run')
if spec.package.extends(self.extendee_spec))
def activate(self, extension, **kwargs):
"""Symlinks all files from the extension into extendee's install dir.

View File

@@ -2011,8 +2011,8 @@ def satisfies(self, other, deps=True, strict=False, strict_deps=False):
other = self._autospec(other)
# The only way to satisfy a concrete spec is to match its hash exactly.
if other._concrete:
return self._concrete and self.dag_hash() == other.dag_hash()
if other.concrete:
return self.concrete and self.dag_hash() == other.dag_hash()
# A concrete provider can satisfy a virtual dependency.
if not self.virtual and other.virtual:

View File

@@ -108,6 +108,13 @@ def test_inheritance_of_diretives():
assert 'mpi' in s
def test_dependency_extensions():
s = Spec('extension2')
s.concretize()
deps = set(x.name for x in s.package.dependency_activations())
assert deps == set(['extension1'])
def test_import_class_from_package(builtin_mock):
from spack.pkg.builtin.mock.mpich import Mpich # noqa

View File

@@ -287,6 +287,14 @@ def test_unsatisfiable_compiler_flag(self):
# 'mpich' is concrete:
check_unsatisfiable('mpich', 'mpich cppflags="-O3"', True)
def test_copy_satisfies_transitive(self):
spec = Spec('dttop')
spec.concretize()
copy = spec.copy()
for s in spec.traverse():
assert s.satisfies(copy[s.name])
assert copy[s.name].satisfies(s)
def test_unsatisfiable_compiler_flag_mismatch(self):
# No matchi in specs
check_unsatisfiable(