Fix #11: bug in ProviderIndex
- packages that provided same spec (e.g. mpe) were overwritten in the index - Index now has a set of providers instead of a single provider per provided spec. - see https://github.com/scalability-llnl/spack/issues/11
This commit is contained in:
@@ -26,20 +26,21 @@
|
|||||||
The ``virtual`` module contains utility classes for virtual dependencies.
|
The ``virtual`` module contains utility classes for virtual dependencies.
|
||||||
"""
|
"""
|
||||||
import spack.spec
|
import spack.spec
|
||||||
|
import itertools
|
||||||
|
|
||||||
class ProviderIndex(object):
|
class ProviderIndex(object):
|
||||||
"""This is a dict of dicts used for finding providers of particular
|
"""This is a dict of dicts used for finding providers of particular
|
||||||
virtual dependencies. The dict of dicts looks like:
|
virtual dependencies. The dict of dicts looks like:
|
||||||
|
|
||||||
{ vpkg name :
|
{ vpkg name :
|
||||||
{ full vpkg spec : package providing spec } }
|
{ full vpkg spec : set(packages providing spec) } }
|
||||||
|
|
||||||
Callers can use this to first find which packages provide a vpkg,
|
Callers can use this to first find which packages provide a vpkg,
|
||||||
then find a matching full spec. e.g., in this scenario:
|
then find a matching full spec. e.g., in this scenario:
|
||||||
|
|
||||||
{ 'mpi' :
|
{ 'mpi' :
|
||||||
{ mpi@:1.1 : mpich,
|
{ mpi@:1.1 : set([mpich]),
|
||||||
mpi@:2.3 : mpich2@1.9: } }
|
mpi@:2.3 : set([mpich2@1.9:]) } }
|
||||||
|
|
||||||
Calling providers_for(spec) will find specs that provide a
|
Calling providers_for(spec) will find specs that provide a
|
||||||
matching implementation of MPI.
|
matching implementation of MPI.
|
||||||
@@ -75,15 +76,19 @@ def update(self, spec):
|
|||||||
if provided_name not in self.providers:
|
if provided_name not in self.providers:
|
||||||
self.providers[provided_name] = {}
|
self.providers[provided_name] = {}
|
||||||
|
|
||||||
|
provider_map = self.providers[provided_name]
|
||||||
|
if not provided_spec in provider_map:
|
||||||
|
provider_map[provided_spec] = set()
|
||||||
|
|
||||||
if self.restrict:
|
if self.restrict:
|
||||||
self.providers[provided_name][provided_spec] = spec
|
provider_map[provided_spec].add(spec)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Before putting the spec in the map, constrain it so that
|
# Before putting the spec in the map, constrain it so that
|
||||||
# it provides what was asked for.
|
# it provides what was asked for.
|
||||||
constrained = spec.copy()
|
constrained = spec.copy()
|
||||||
constrained.constrain(provider_spec)
|
constrained.constrain(provider_spec)
|
||||||
self.providers[provided_name][provided_spec] = constrained
|
provider_map[provided_spec].add(constrained)
|
||||||
|
|
||||||
|
|
||||||
def providers_for(self, *vpkg_specs):
|
def providers_for(self, *vpkg_specs):
|
||||||
@@ -97,9 +102,9 @@ def providers_for(self, *vpkg_specs):
|
|||||||
|
|
||||||
# Add all the providers that satisfy the vpkg spec.
|
# Add all the providers that satisfy the vpkg spec.
|
||||||
if vspec.name in self.providers:
|
if vspec.name in self.providers:
|
||||||
for provider_spec, spec in self.providers[vspec.name].items():
|
for provider_spec, spec_set in self.providers[vspec.name].items():
|
||||||
if provider_spec.satisfies(vspec, deps=False):
|
if provider_spec.satisfies(vspec, deps=False):
|
||||||
providers.add(spec)
|
providers.update(spec_set)
|
||||||
|
|
||||||
# Return providers in order
|
# Return providers in order
|
||||||
return sorted(providers)
|
return sorted(providers)
|
||||||
@@ -108,16 +113,22 @@ def providers_for(self, *vpkg_specs):
|
|||||||
# TODO: this is pretty darned nasty, and inefficient.
|
# TODO: this is pretty darned nasty, and inefficient.
|
||||||
def _cross_provider_maps(self, lmap, rmap):
|
def _cross_provider_maps(self, lmap, rmap):
|
||||||
result = {}
|
result = {}
|
||||||
for lspec in lmap:
|
for lspec, rspec in itertools.product(lmap, rmap):
|
||||||
for rspec in rmap:
|
try:
|
||||||
try:
|
constrained = lspec.copy().constrain(rspec)
|
||||||
constrained = lspec.copy().constrain(rspec)
|
except spack.spec.UnsatisfiableSpecError:
|
||||||
if lmap[lspec].name != rmap[rspec].name:
|
continue
|
||||||
|
|
||||||
|
# lp and rp are left and right provider specs.
|
||||||
|
for lp_spec, rp_spec in itertools.product(lmap[lspec], rmap[rspec]):
|
||||||
|
if lp_spec.name == rp_spec.name:
|
||||||
|
try:
|
||||||
|
const = lp_spec.copy().constrain(rp_spec,deps=False)
|
||||||
|
if constrained not in result:
|
||||||
|
result[constrained] = set()
|
||||||
|
result[constrained].add(const)
|
||||||
|
except spack.spec.UnsatisfiableSpecError:
|
||||||
continue
|
continue
|
||||||
result[constrained] = lmap[lspec].copy().constrain(
|
|
||||||
rmap[rspec], deps=False)
|
|
||||||
except spack.spec.UnsatisfiableSpecError:
|
|
||||||
continue
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -132,6 +143,8 @@ def satisfies(self, other):
|
|||||||
if not common:
|
if not common:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# This ensures that some provider in other COULD satisfy the
|
||||||
|
# vpkg constraints on self.
|
||||||
result = {}
|
result = {}
|
||||||
for name in common:
|
for name in common:
|
||||||
crossed = self._cross_provider_maps(self.providers[name],
|
crossed = self._cross_provider_maps(self.providers[name],
|
||||||
|
Reference in New Issue
Block a user