SPACK-14: Bugfix in Spec.normalize()
- Normalize now updates the provider index as it addes package dependencies. - Fixes problem where this breaks: a depends_on mpi a depends_on b b depends_on mpich - Packages now restrict the mpi dependency to mpich
This commit is contained in:
parent
d0b82d291f
commit
df0c1134c9
@ -5,17 +5,23 @@
|
||||
import spack.url as url
|
||||
import spack
|
||||
|
||||
|
||||
description = "parse specs and print them out to the command line."
|
||||
description = "print out abstract and concrete versions of a spec."
|
||||
|
||||
def setup_parser(subparser):
|
||||
subparser.add_argument('specs', nargs=argparse.REMAINDER, help="specs of packages")
|
||||
|
||||
def spec(parser, args):
|
||||
specs = spack.cmd.parse_specs(args.specs)
|
||||
for spec in specs:
|
||||
spec.normalize()
|
||||
print spec.tree(color=True)
|
||||
for spec in spack.cmd.parse_specs(args.specs):
|
||||
print "Input spec"
|
||||
print "------------------------------"
|
||||
print spec.tree(color=True, indent=2)
|
||||
|
||||
print "Normalized"
|
||||
print "------------------------------"
|
||||
spec.normalize()
|
||||
print spec.tree(color=True, indent=2)
|
||||
|
||||
print "Concretized"
|
||||
print "------------------------------"
|
||||
spec.concretize()
|
||||
print spec.tree(color=True)
|
||||
print spec.tree(color=True, indent=2)
|
||||
|
@ -52,34 +52,42 @@ def __init__(self, specs, **kwargs):
|
||||
# TODO: come up with another name for this. This "restricts" values to
|
||||
# the verbatim impu specs (i.e., it doesn't pre-apply package's constraints, and
|
||||
# keeps things as broad as possible, so it's really the wrong name)
|
||||
restrict = kwargs.setdefault('restrict', False)
|
||||
self.restrict = kwargs.setdefault('restrict', False)
|
||||
|
||||
self.providers = {}
|
||||
|
||||
for spec in specs:
|
||||
if type(spec) != spack.spec.Spec:
|
||||
if not isinstance(spec, spack.spec.Spec):
|
||||
spec = spack.spec.Spec(spec)
|
||||
|
||||
if spec.virtual:
|
||||
continue
|
||||
|
||||
pkg = spec.package
|
||||
for provided_spec, provider_spec in pkg.provided.iteritems():
|
||||
if provider_spec.satisfies(spec, deps=False):
|
||||
provided_name = provided_spec.name
|
||||
if provided_name not in self.providers:
|
||||
self.providers[provided_name] = {}
|
||||
self.update(spec)
|
||||
|
||||
if restrict:
|
||||
self.providers[provided_name][provided_spec] = spec
|
||||
|
||||
else:
|
||||
# Before putting the spec in the map, constrain it so that
|
||||
# it provides what was asked for.
|
||||
constrained = spec.copy()
|
||||
constrained.constrain(provider_spec)
|
||||
self.providers[provided_name][provided_spec] = constrained
|
||||
def update(self, spec):
|
||||
if type(spec) != spack.spec.Spec:
|
||||
spec = spack.spec.Spec(spec)
|
||||
|
||||
assert(not spec.virtual)
|
||||
|
||||
pkg = spec.package
|
||||
for provided_spec, provider_spec in pkg.provided.iteritems():
|
||||
if provider_spec.satisfies(spec, deps=False):
|
||||
provided_name = provided_spec.name
|
||||
if provided_name not in self.providers:
|
||||
self.providers[provided_name] = {}
|
||||
|
||||
if self.restrict:
|
||||
self.providers[provided_name][provided_spec] = spec
|
||||
|
||||
else:
|
||||
# Before putting the spec in the map, constrain it so that
|
||||
# it provides what was asked for.
|
||||
constrained = spec.copy()
|
||||
constrained.constrain(provider_spec)
|
||||
self.providers[provided_name][provided_spec] = constrained
|
||||
|
||||
|
||||
def providers_for(self, *vpkg_specs):
|
||||
|
@ -476,11 +476,21 @@ def _concretize_helper(self, presets=None, visited=None):
|
||||
visited.add(self.name)
|
||||
|
||||
|
||||
def _replace_with(self, concrete):
|
||||
"""Replace this virtual spec with a concrete spec."""
|
||||
assert(self.virtual)
|
||||
for name, dependent in self.dependents.items():
|
||||
del dependent.dependencies[self.name]
|
||||
dependent._add_dependency(concrete)
|
||||
|
||||
|
||||
def _expand_virtual_packages(self):
|
||||
"""Find virtual packages in this spec, replace them with providers,
|
||||
and normalize again to include the provider's (potentially virtual)
|
||||
dependencies. Repeat until there are no virtual deps.
|
||||
|
||||
Precondition: spec is normalized.
|
||||
|
||||
.. todo::
|
||||
|
||||
If a provider depends on something that conflicts with
|
||||
@ -500,10 +510,7 @@ def _expand_virtual_packages(self):
|
||||
providers = packages.providers_for(spec)
|
||||
concrete = spack.concretizer.choose_provider(spec, providers)
|
||||
concrete = concrete.copy()
|
||||
|
||||
for name, dependent in spec.dependents.items():
|
||||
del dependent.dependencies[spec.name]
|
||||
dependent._add_dependency(concrete)
|
||||
spec._replace_with(concrete)
|
||||
|
||||
# If there are duplicate providers or duplicate provider deps, this
|
||||
# consolidates them and merges constraints.
|
||||
@ -612,12 +619,26 @@ def _normalize_helper(self, visited, spec_deps, provider_index):
|
||||
# The user might have required something insufficient for
|
||||
# pkg_dep -- so we'll get a conflict. e.g., user asked for
|
||||
# mpi@:1.1 but some package required mpi@2.1:.
|
||||
providers = provider_index.providers_for(name)
|
||||
if len(providers) > 1:
|
||||
raise MultipleProviderError(pkg_dep, providers)
|
||||
if providers:
|
||||
raise UnsatisfiableProviderSpecError(providers[0], pkg_dep)
|
||||
|
||||
required = provider_index.providers_for(name)
|
||||
if len(required) > 1:
|
||||
raise MultipleProviderError(pkg_dep, required)
|
||||
elif required:
|
||||
raise UnsatisfiableProviderSpecError(
|
||||
required[0], pkg_dep)
|
||||
else:
|
||||
# if it's a real dependency, check whether it provides something
|
||||
# already required in the spec.
|
||||
index = packages.ProviderIndex([pkg_dep], restrict=True)
|
||||
for vspec in (v for v in spec_deps.values() if v.virtual):
|
||||
if index.providers_for(vspec):
|
||||
vspec._replace_with(pkg_dep)
|
||||
del spec_deps[vspec.name]
|
||||
else:
|
||||
required = index.providers_for(vspec.name)
|
||||
if required:
|
||||
raise UnsatisfiableProviderSpecError(
|
||||
required[0], pkg_dep)
|
||||
provider_index.update(pkg_dep)
|
||||
|
||||
if name not in spec_deps:
|
||||
# If the spec doesn't reference a dependency that this package
|
||||
@ -673,6 +694,7 @@ def normalize(self):
|
||||
spec_packages = [d.package for d in spec_deps.values() if not d.virtual]
|
||||
|
||||
index = packages.ProviderIndex(spec_deps.values(), restrict=True)
|
||||
|
||||
visited = set()
|
||||
self._normalize_helper(visited, spec_deps, index)
|
||||
|
||||
|
@ -133,3 +133,13 @@ def test_virtual_is_fully_expanded_for_mpileaks(self):
|
||||
self.assertIn('fake', spec.dependencies['callpath'].dependencies['zmpi'].dependencies)
|
||||
|
||||
self.assertNotIn('mpi', spec)
|
||||
|
||||
|
||||
def test_my_dep_depends_on_provider_of_my_virtual_dep(self):
|
||||
spec = Spec('indirect_mpich')
|
||||
spec.normalize()
|
||||
|
||||
print
|
||||
print spec.tree(color=True)
|
||||
|
||||
spec.concretize()
|
||||
|
12
lib/spack/spack/test/mock_packages/direct_mpich.py
Normal file
12
lib/spack/spack/test/mock_packages/direct_mpich.py
Normal file
@ -0,0 +1,12 @@
|
||||
from spack import *
|
||||
|
||||
class DirectMpich(Package):
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/direct_mpich-1.0.tar.gz"
|
||||
|
||||
versions = { 1.0 : 'foobarbaz' }
|
||||
|
||||
depends_on('mpich')
|
||||
|
||||
def install(self, spec, prefix):
|
||||
pass
|
17
lib/spack/spack/test/mock_packages/indirect_mpich.py
Normal file
17
lib/spack/spack/test/mock_packages/indirect_mpich.py
Normal file
@ -0,0 +1,17 @@
|
||||
from spack import *
|
||||
|
||||
class IndirectMpich(Package):
|
||||
"""Test case for a package that depends on MPI and one of its
|
||||
dependencies requires a *particular version* of MPI.
|
||||
"""
|
||||
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/indirect_mpich-1.0.tar.gz"
|
||||
|
||||
versions = { 1.0 : 'foobarbaz' }
|
||||
|
||||
depends_on('mpi')
|
||||
depends_on('direct_mpich')
|
||||
|
||||
def install(self, spec, prefix):
|
||||
pass
|
Loading…
Reference in New Issue
Block a user