Merge pull request #549 from LLNL/bugfix/gh538-less-greedy-concretize
Bugfix/gh538 less greedy concretize
This commit is contained in:
commit
108ea1522a
8 changed files with 294 additions and 115 deletions
|
@ -235,11 +235,11 @@ def setter(name, value):
|
||||||
if not has_method(cls, '_cmp_key'):
|
if not has_method(cls, '_cmp_key'):
|
||||||
raise TypeError("'%s' doesn't define _cmp_key()." % cls.__name__)
|
raise TypeError("'%s' doesn't define _cmp_key()." % cls.__name__)
|
||||||
|
|
||||||
setter('__eq__', lambda s,o: o is not None and s._cmp_key() == o._cmp_key())
|
setter('__eq__', lambda s,o: (s is o) or (o is not None and s._cmp_key() == o._cmp_key()))
|
||||||
setter('__lt__', lambda s,o: o is not None and s._cmp_key() < o._cmp_key())
|
setter('__lt__', lambda s,o: o is not None and s._cmp_key() < o._cmp_key())
|
||||||
setter('__le__', lambda s,o: o is not None and s._cmp_key() <= o._cmp_key())
|
setter('__le__', lambda s,o: o is not None and s._cmp_key() <= o._cmp_key())
|
||||||
|
|
||||||
setter('__ne__', lambda s,o: o is None or s._cmp_key() != o._cmp_key())
|
setter('__ne__', lambda s,o: (s is not o) and (o is None or s._cmp_key() != o._cmp_key()))
|
||||||
setter('__gt__', lambda s,o: o is None or s._cmp_key() > o._cmp_key())
|
setter('__gt__', lambda s,o: o is None or s._cmp_key() > o._cmp_key())
|
||||||
setter('__ge__', lambda s,o: o is None or s._cmp_key() >= o._cmp_key())
|
setter('__ge__', lambda s,o: o is None or s._cmp_key() >= o._cmp_key())
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,10 @@ class DefaultConcretizer(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _valid_virtuals_and_externals(self, spec):
|
def _valid_virtuals_and_externals(self, spec):
|
||||||
"""Returns a list of spec/external-path pairs for both virtuals and externals
|
"""Returns a list of candidate virtual dep providers and external
|
||||||
that can concretize this spec."""
|
packages that coiuld be used to concretize a spec."""
|
||||||
# Get a list of candidate packages that could satisfy this spec
|
# First construct a list of concrete candidates to replace spec with.
|
||||||
packages = []
|
candidates = [spec]
|
||||||
if spec.virtual:
|
if spec.virtual:
|
||||||
providers = spack.repo.providers_for(spec)
|
providers = spack.repo.providers_for(spec)
|
||||||
if not providers:
|
if not providers:
|
||||||
|
@ -64,96 +64,72 @@ def _valid_virtuals_and_externals(self, spec):
|
||||||
if not spec_w_preferred_providers:
|
if not spec_w_preferred_providers:
|
||||||
spec_w_preferred_providers = spec
|
spec_w_preferred_providers = spec
|
||||||
provider_cmp = partial(spack.pkgsort.provider_compare, spec_w_preferred_providers.name, spec.name)
|
provider_cmp = partial(spack.pkgsort.provider_compare, spec_w_preferred_providers.name, spec.name)
|
||||||
packages = sorted(providers, cmp=provider_cmp)
|
candidates = sorted(providers, cmp=provider_cmp)
|
||||||
else:
|
|
||||||
packages = [spec]
|
|
||||||
|
|
||||||
# For each candidate package, if it has externals add those to the candidates
|
# For each candidate package, if it has externals, add those to the usable list.
|
||||||
# if it's not buildable, then only add the externals.
|
# if it's not buildable, then *only* add the externals.
|
||||||
candidates = []
|
usable = []
|
||||||
all_compilers = spack.compilers.all_compilers()
|
for cspec in candidates:
|
||||||
for pkg in packages:
|
if is_spec_buildable(cspec):
|
||||||
externals = spec_externals(pkg)
|
usable.append(cspec)
|
||||||
buildable = is_spec_buildable(pkg)
|
externals = spec_externals(cspec)
|
||||||
if buildable:
|
|
||||||
candidates.append((pkg, None))
|
|
||||||
for ext in externals:
|
for ext in externals:
|
||||||
if ext[0].satisfies(spec):
|
if ext.satisfies(spec):
|
||||||
candidates.append(ext)
|
usable.append(ext)
|
||||||
if not candidates:
|
|
||||||
|
# If nothing is in the usable list now, it's because we aren't
|
||||||
|
# allowed to build anything.
|
||||||
|
if not usable:
|
||||||
raise NoBuildError(spec)
|
raise NoBuildError(spec)
|
||||||
|
|
||||||
def cmp_externals(a, b):
|
def cmp_externals(a, b):
|
||||||
if a[0].name != b[0].name:
|
if a.name != b.name:
|
||||||
#We're choosing between different providers. Maintain order from above sort
|
# We're choosing between different providers, so
|
||||||
|
# maintain order from provider sort
|
||||||
return candidates.index(a) - candidates.index(b)
|
return candidates.index(a) - candidates.index(b)
|
||||||
result = cmp_specs(a[0], b[0])
|
|
||||||
|
result = cmp_specs(a, b)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
return result
|
return result
|
||||||
if not a[1] and b[1]:
|
|
||||||
return 1
|
|
||||||
if not b[1] and a[1]:
|
|
||||||
return -1
|
|
||||||
return cmp(a[1], b[1])
|
|
||||||
|
|
||||||
candidates = sorted(candidates, cmp=cmp_externals)
|
# prefer external packages to internal packages.
|
||||||
return candidates
|
if a.external is None or b.external is None:
|
||||||
|
return -cmp(a.external, b.external)
|
||||||
|
else:
|
||||||
|
return cmp(a.external, b.external)
|
||||||
|
|
||||||
|
usable.sort(cmp=cmp_externals)
|
||||||
|
return usable
|
||||||
|
|
||||||
|
|
||||||
def concretize_virtual_and_external(self, spec):
|
def choose_virtual_or_external(self, spec):
|
||||||
"""From a list of candidate virtual and external packages, concretize to one that
|
"""Given a list of candidate virtual and external packages, try to
|
||||||
is ABI compatible with the rest of the DAG."""
|
find one that is most ABI compatible.
|
||||||
|
"""
|
||||||
candidates = self._valid_virtuals_and_externals(spec)
|
candidates = self._valid_virtuals_and_externals(spec)
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return False
|
return candidates
|
||||||
|
|
||||||
# Find the nearest spec in the dag that has a compiler. We'll use that
|
# Find the nearest spec in the dag that has a compiler. We'll
|
||||||
# spec to test compiler compatibility.
|
# use that spec to calibrate compiler compatibility.
|
||||||
other_spec = find_spec(spec, lambda(x): x.compiler)
|
abi_exemplar = find_spec(spec, lambda(x): x.compiler)
|
||||||
if not other_spec:
|
if not abi_exemplar:
|
||||||
other_spec = spec.root
|
abi_exemplar = spec.root
|
||||||
|
|
||||||
# Choose an ABI-compatible candidate, or the first match otherwise.
|
# Make a list including ABI compatibility of specs with the exemplar.
|
||||||
candidate = None
|
strict = [spack.abi.compatible(c, abi_exemplar) for c in candidates]
|
||||||
if other_spec:
|
loose = [spack.abi.compatible(c, abi_exemplar, loose=True) for c in candidates]
|
||||||
candidate = next((c for c in candidates if spack.abi.compatible(c[0], other_spec)), None)
|
keys = zip(strict, loose, candidates)
|
||||||
if not candidate:
|
|
||||||
# Try a looser ABI matching
|
|
||||||
candidate = next((c for c in candidates if spack.abi.compatible(c[0], other_spec, loose=True)), None)
|
|
||||||
if not candidate:
|
|
||||||
# No ABI matches. Pick the top choice based on the orignal preferences.
|
|
||||||
candidate = candidates[0]
|
|
||||||
candidate_spec = candidate[0]
|
|
||||||
external = candidate[1]
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
# If we're external then trim the dependencies
|
# Sort candidates from most to least compatibility.
|
||||||
if external:
|
# Note:
|
||||||
if (spec.dependencies):
|
# 1. We reverse because True > False.
|
||||||
changed = True
|
# 2. Sort is stable, so c's keep their order.
|
||||||
spec.dependencies = DependencyMap()
|
keys.sort(key=lambda k:k[:2], reverse=True)
|
||||||
candidate_spec.dependencies = DependencyMap()
|
|
||||||
|
|
||||||
def fequal(candidate_field, spec_field):
|
# Pull the candidates back out and return them in order
|
||||||
return (not candidate_field) or (candidate_field == spec_field)
|
candidates = [c for s,l,c in keys]
|
||||||
if (fequal(candidate_spec.name, spec.name) and
|
return candidates
|
||||||
fequal(candidate_spec.versions, spec.versions) and
|
|
||||||
fequal(candidate_spec.compiler, spec.compiler) and
|
|
||||||
fequal(candidate_spec.architecture, spec.architecture) and
|
|
||||||
fequal(candidate_spec.dependencies, spec.dependencies) and
|
|
||||||
fequal(candidate_spec.variants, spec.variants) and
|
|
||||||
fequal(external, spec.external)):
|
|
||||||
return changed
|
|
||||||
|
|
||||||
# Refine this spec to the candidate.
|
|
||||||
if spec.virtual:
|
|
||||||
spec._replace_with(candidate_spec)
|
|
||||||
changed = True
|
|
||||||
if spec._dup(candidate_spec, deps=False, cleardeps=False):
|
|
||||||
changed = True
|
|
||||||
spec.external = external
|
|
||||||
|
|
||||||
return changed
|
|
||||||
|
|
||||||
|
|
||||||
def concretize_version(self, spec):
|
def concretize_version(self, spec):
|
||||||
|
@ -238,7 +214,7 @@ def concretize_variants(self, spec):
|
||||||
the default variants from the package specification.
|
the default variants from the package specification.
|
||||||
"""
|
"""
|
||||||
changed = False
|
changed = False
|
||||||
for name, variant in spec.package.variants.items():
|
for name, variant in spec.package_class.variants.items():
|
||||||
if name not in spec.variants:
|
if name not in spec.variants:
|
||||||
spec.variants[name] = spack.spec.VariantSpec(name, variant.default)
|
spec.variants[name] = spack.spec.VariantSpec(name, variant.default)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
|
@ -539,22 +539,25 @@ def print_section(section):
|
||||||
|
|
||||||
|
|
||||||
def spec_externals(spec):
|
def spec_externals(spec):
|
||||||
"""Return a list of spec, directory pairs for each external location for spec"""
|
"""Return a list of external specs (with external directory path filled in),
|
||||||
|
one for each known external installation."""
|
||||||
allpkgs = get_config('packages')
|
allpkgs = get_config('packages')
|
||||||
name = spec.name
|
name = spec.name
|
||||||
spec_locations = []
|
|
||||||
|
|
||||||
|
external_specs = []
|
||||||
pkg_paths = allpkgs.get(name, {}).get('paths', None)
|
pkg_paths = allpkgs.get(name, {}).get('paths', None)
|
||||||
if not pkg_paths:
|
if not pkg_paths:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
for pkg,path in pkg_paths.iteritems():
|
for external_spec, path in pkg_paths.iteritems():
|
||||||
if not spec.satisfies(pkg):
|
|
||||||
continue
|
|
||||||
if not path:
|
if not path:
|
||||||
|
# skip entries without paths (avoid creating extra Specs)
|
||||||
continue
|
continue
|
||||||
spec_locations.append( (spack.spec.Spec(pkg), path) )
|
|
||||||
return spec_locations
|
external_spec = spack.spec.Spec(external_spec, external=path)
|
||||||
|
if external_spec.satisfies(spec):
|
||||||
|
external_specs.append(external_spec)
|
||||||
|
return external_specs
|
||||||
|
|
||||||
|
|
||||||
def is_spec_buildable(spec):
|
def is_spec_buildable(spec):
|
||||||
|
|
|
@ -316,6 +316,11 @@ def get(self, spec, new=False):
|
||||||
return self.repo_for_pkg(spec).get(spec)
|
return self.repo_for_pkg(spec).get(spec)
|
||||||
|
|
||||||
|
|
||||||
|
def get_pkg_class(self, pkg_name):
|
||||||
|
"""Find a class for the spec's package and return the class object."""
|
||||||
|
return self.repo_for_pkg(pkg_name).get_pkg_class(pkg_name)
|
||||||
|
|
||||||
|
|
||||||
@_autospec
|
@_autospec
|
||||||
def dump_provenance(self, spec, path):
|
def dump_provenance(self, spec, path):
|
||||||
"""Dump provenance information for a spec to a particular path.
|
"""Dump provenance information for a spec to a particular path.
|
||||||
|
@ -550,7 +555,7 @@ def get(self, spec, new=False):
|
||||||
|
|
||||||
key = hash(spec)
|
key = hash(spec)
|
||||||
if new or key not in self._instances:
|
if new or key not in self._instances:
|
||||||
package_class = self._get_pkg_class(spec.name)
|
package_class = self.get_pkg_class(spec.name)
|
||||||
try:
|
try:
|
||||||
copy = spec.copy() # defensive copy. Package owns its spec.
|
copy = spec.copy() # defensive copy. Package owns its spec.
|
||||||
self._instances[key] = package_class(copy)
|
self._instances[key] = package_class(copy)
|
||||||
|
@ -715,7 +720,7 @@ def _get_pkg_module(self, pkg_name):
|
||||||
return self._modules[pkg_name]
|
return self._modules[pkg_name]
|
||||||
|
|
||||||
|
|
||||||
def _get_pkg_class(self, pkg_name):
|
def get_pkg_class(self, pkg_name):
|
||||||
"""Get the class for the package out of its module.
|
"""Get the class for the package out of its module.
|
||||||
|
|
||||||
First loads (or fetches from cache) a module for the
|
First loads (or fetches from cache) a module for the
|
||||||
|
|
|
@ -353,7 +353,7 @@ def constrain(self, other):
|
||||||
@property
|
@property
|
||||||
def concrete(self):
|
def concrete(self):
|
||||||
return self.spec._concrete or all(
|
return self.spec._concrete or all(
|
||||||
v in self for v in self.spec.package.variants)
|
v in self for v in self.spec.package_class.variants)
|
||||||
|
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
@ -420,7 +420,9 @@ def __init__(self, spec_like, *dep_like, **kwargs):
|
||||||
# package.py files for.
|
# package.py files for.
|
||||||
self._normal = kwargs.get('normal', False)
|
self._normal = kwargs.get('normal', False)
|
||||||
self._concrete = kwargs.get('concrete', False)
|
self._concrete = kwargs.get('concrete', False)
|
||||||
self.external = None
|
|
||||||
|
# Allow a spec to be constructed with an external path.
|
||||||
|
self.external = kwargs.get('external', None)
|
||||||
|
|
||||||
# This allows users to construct a spec DAG with literals.
|
# This allows users to construct a spec DAG with literals.
|
||||||
# Note that given two specs a and b, Spec(a) copies a, but
|
# Note that given two specs a and b, Spec(a) copies a, but
|
||||||
|
@ -498,6 +500,14 @@ def package(self):
|
||||||
return spack.repo.get(self)
|
return spack.repo.get(self)
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def package_class(self):
|
||||||
|
"""Internal package call gets only the class object for a package.
|
||||||
|
Use this to just get package metadata.
|
||||||
|
"""
|
||||||
|
return spack.repo.get_pkg_class(self.name)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def virtual(self):
|
def virtual(self):
|
||||||
"""Right now, a spec is virtual if no package exists with its name.
|
"""Right now, a spec is virtual if no package exists with its name.
|
||||||
|
@ -786,10 +796,32 @@ def _replace_with(self, concrete):
|
||||||
"""Replace this virtual spec with a concrete spec."""
|
"""Replace this virtual spec with a concrete spec."""
|
||||||
assert(self.virtual)
|
assert(self.virtual)
|
||||||
for name, dependent in self.dependents.items():
|
for name, dependent in self.dependents.items():
|
||||||
|
# remove self from all dependents.
|
||||||
del dependent.dependencies[self.name]
|
del dependent.dependencies[self.name]
|
||||||
|
|
||||||
|
# add the replacement, unless it is already a dep of dependent.
|
||||||
|
if concrete.name not in dependent.dependencies:
|
||||||
dependent._add_dependency(concrete)
|
dependent._add_dependency(concrete)
|
||||||
|
|
||||||
|
|
||||||
|
def _replace_node(self, replacement):
|
||||||
|
"""Replace this spec with another.
|
||||||
|
|
||||||
|
Connects all dependents of this spec to its replacement, and
|
||||||
|
disconnects this spec from any dependencies it has. New spec
|
||||||
|
will have any dependencies the replacement had, and may need
|
||||||
|
to be normalized.
|
||||||
|
|
||||||
|
"""
|
||||||
|
for name, dependent in self.dependents.items():
|
||||||
|
del dependent.dependencies[self.name]
|
||||||
|
dependent._add_dependency(replacement)
|
||||||
|
|
||||||
|
for name, dep in self.dependencies.items():
|
||||||
|
del dep.dependents[self.name]
|
||||||
|
del self.dependencies[dep.name]
|
||||||
|
|
||||||
|
|
||||||
def _expand_virtual_packages(self):
|
def _expand_virtual_packages(self):
|
||||||
"""Find virtual packages in this spec, replace them with providers,
|
"""Find virtual packages in this spec, replace them with providers,
|
||||||
and normalize again to include the provider's (potentially virtual)
|
and normalize again to include the provider's (potentially virtual)
|
||||||
|
@ -807,18 +839,80 @@ def _expand_virtual_packages(self):
|
||||||
this are infrequent, but should implement this before it is
|
this are infrequent, but should implement this before it is
|
||||||
a problem.
|
a problem.
|
||||||
"""
|
"""
|
||||||
|
# Make an index of stuff this spec already provides
|
||||||
|
self_index = ProviderIndex(self.traverse(), restrict=True)
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
done = False
|
done = False
|
||||||
while not done:
|
while not done:
|
||||||
done = True
|
done = True
|
||||||
for spec in list(self.traverse()):
|
for spec in list(self.traverse()):
|
||||||
if spack.concretizer.concretize_virtual_and_external(spec):
|
replacement = None
|
||||||
done = False
|
if spec.virtual:
|
||||||
|
replacement = self._find_provider(spec, self_index)
|
||||||
|
if replacement:
|
||||||
|
# TODO: may break if in-place on self but
|
||||||
|
# shouldn't happen if root is traversed first.
|
||||||
|
spec._replace_with(replacement)
|
||||||
|
done=False
|
||||||
|
break
|
||||||
|
|
||||||
|
if not replacement:
|
||||||
|
# Get a list of possible replacements in order of preference.
|
||||||
|
candidates = spack.concretizer.choose_virtual_or_external(spec)
|
||||||
|
|
||||||
|
# Try the replacements in order, skipping any that cause
|
||||||
|
# satisfiability problems.
|
||||||
|
for replacement in candidates:
|
||||||
|
if replacement is spec:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Replace spec with the candidate and normalize
|
||||||
|
copy = self.copy()
|
||||||
|
copy[spec.name]._dup(replacement.copy(deps=False))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# If there are duplicate providers or duplicate provider
|
||||||
|
# deps, consolidate them and merge constraints.
|
||||||
|
copy.normalize(force=True)
|
||||||
|
break
|
||||||
|
except SpecError as e:
|
||||||
|
# On error, we'll try the next replacement.
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If replacement is external then trim the dependencies
|
||||||
|
if replacement.external:
|
||||||
|
if (spec.dependencies):
|
||||||
|
changed = True
|
||||||
|
spec.dependencies = DependencyMap()
|
||||||
|
replacement.dependencies = DependencyMap()
|
||||||
|
|
||||||
|
# TODO: could this and the stuff in _dup be cleaned up?
|
||||||
|
def feq(cfield, sfield):
|
||||||
|
return (not cfield) or (cfield == sfield)
|
||||||
|
|
||||||
|
if replacement is spec or (feq(replacement.name, spec.name) and
|
||||||
|
feq(replacement.versions, spec.versions) and
|
||||||
|
feq(replacement.compiler, spec.compiler) and
|
||||||
|
feq(replacement.architecture, spec.architecture) and
|
||||||
|
feq(replacement.dependencies, spec.dependencies) and
|
||||||
|
feq(replacement.variants, spec.variants) and
|
||||||
|
feq(replacement.external, spec.external)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Refine this spec to the candidate. This uses
|
||||||
|
# replace_with AND dup so that it can work in
|
||||||
|
# place. TODO: make this more efficient.
|
||||||
|
if spec.virtual:
|
||||||
|
spec._replace_with(replacement)
|
||||||
|
changed = True
|
||||||
|
if spec._dup(replacement, deps=False, cleardeps=False):
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
# If there are duplicate providers or duplicate provider deps, this
|
self_index.update(spec)
|
||||||
# consolidates them and merge constraints.
|
done=False
|
||||||
changed |= self.normalize(force=True)
|
break
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
|
|
||||||
|
@ -842,7 +936,7 @@ def concretize(self):
|
||||||
force = False
|
force = False
|
||||||
|
|
||||||
while changed:
|
while changed:
|
||||||
changes = (self.normalize(force=force),
|
changes = (self.normalize(force),
|
||||||
self._expand_virtual_packages(),
|
self._expand_virtual_packages(),
|
||||||
self._concretize_helper())
|
self._concretize_helper())
|
||||||
changed = any(changes)
|
changed = any(changes)
|
||||||
|
@ -1010,17 +1104,14 @@ def _merge_dependency(self, dep, visited, spec_deps, provider_index):
|
||||||
"""
|
"""
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
# If it's a virtual dependency, try to find a provider and
|
# If it's a virtual dependency, try to find an existing
|
||||||
# merge that.
|
# provider in the spec, and merge that.
|
||||||
if dep.virtual:
|
if dep.virtual:
|
||||||
visited.add(dep.name)
|
visited.add(dep.name)
|
||||||
provider = self._find_provider(dep, provider_index)
|
provider = self._find_provider(dep, provider_index)
|
||||||
if provider:
|
if provider:
|
||||||
dep = provider
|
dep = provider
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# if it's a real dependency, check whether it provides
|
|
||||||
# something already required in the spec.
|
|
||||||
index = ProviderIndex([dep], restrict=True)
|
index = ProviderIndex([dep], restrict=True)
|
||||||
for vspec in (v for v in spec_deps.values() if v.virtual):
|
for vspec in (v for v in spec_deps.values() if v.virtual):
|
||||||
if index.providers_for(vspec):
|
if index.providers_for(vspec):
|
||||||
|
@ -1117,13 +1208,14 @@ def normalize(self, force=False):
|
||||||
# Get all the dependencies into one DependencyMap
|
# Get all the dependencies into one DependencyMap
|
||||||
spec_deps = self.flat_dependencies(copy=False)
|
spec_deps = self.flat_dependencies(copy=False)
|
||||||
|
|
||||||
# Initialize index of virtual dependency providers
|
# Initialize index of virtual dependency providers if
|
||||||
index = ProviderIndex(spec_deps.values(), restrict=True)
|
# concretize didn't pass us one already
|
||||||
|
provider_index = ProviderIndex(spec_deps.values(), restrict=True)
|
||||||
|
|
||||||
# traverse the package DAG and fill out dependencies according
|
# traverse the package DAG and fill out dependencies according
|
||||||
# to package files & their 'when' specs
|
# to package files & their 'when' specs
|
||||||
visited = set()
|
visited = set()
|
||||||
any_change = self._normalize_helper(visited, spec_deps, index)
|
any_change = self._normalize_helper(visited, spec_deps, provider_index)
|
||||||
|
|
||||||
# If there are deps specified but not visited, they're not
|
# If there are deps specified but not visited, they're not
|
||||||
# actually deps of this package. Raise an error.
|
# actually deps of this package. Raise an error.
|
||||||
|
@ -1161,7 +1253,7 @@ def validate_names(self):
|
||||||
|
|
||||||
# Ensure that variants all exist.
|
# Ensure that variants all exist.
|
||||||
for vname, variant in spec.variants.items():
|
for vname, variant in spec.variants.items():
|
||||||
if vname not in spec.package.variants:
|
if vname not in spec.package_class.variants:
|
||||||
raise UnknownVariantError(spec.name, vname)
|
raise UnknownVariantError(spec.name, vname)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1402,13 +1494,12 @@ def _dup(self, other, **kwargs):
|
||||||
Whether deps should be copied too. Set to false to copy a
|
Whether deps should be copied too. Set to false to copy a
|
||||||
spec but not its dependencies.
|
spec but not its dependencies.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# We don't count dependencies as changes here
|
# We don't count dependencies as changes here
|
||||||
changed = True
|
changed = True
|
||||||
if hasattr(self, 'name'):
|
if hasattr(self, 'name'):
|
||||||
changed = (self.name != other.name and self.versions != other.versions and \
|
changed = (self.name != other.name and self.versions != other.versions and
|
||||||
self.architecture != other.architecture and self.compiler != other.compiler and \
|
self.architecture != other.architecture and self.compiler != other.compiler and
|
||||||
self.variants != other.variants and self._normal != other._normal and \
|
self.variants != other.variants and self._normal != other._normal and
|
||||||
self.concrete != other.concrete and self.external != other.external)
|
self.concrete != other.concrete and self.external != other.external)
|
||||||
|
|
||||||
# Local node attributes get copied first.
|
# Local node attributes get copied first.
|
||||||
|
|
|
@ -142,6 +142,34 @@ def test_concretize_with_provides_when(self):
|
||||||
for spec in spack.repo.providers_for('mpi@3')))
|
for spec in spack.repo.providers_for('mpi@3')))
|
||||||
|
|
||||||
|
|
||||||
|
def test_concretize_two_virtuals(self):
|
||||||
|
"""Test a package with multiple virtual dependencies."""
|
||||||
|
s = Spec('hypre').concretize()
|
||||||
|
|
||||||
|
|
||||||
|
def test_concretize_two_virtuals_with_one_bound(self):
|
||||||
|
"""Test a package with multiple virtual dependencies and one preset."""
|
||||||
|
s = Spec('hypre ^openblas').concretize()
|
||||||
|
|
||||||
|
|
||||||
|
def test_concretize_two_virtuals_with_two_bound(self):
|
||||||
|
"""Test a package with multiple virtual dependencies and two of them preset."""
|
||||||
|
s = Spec('hypre ^openblas ^netlib-lapack').concretize()
|
||||||
|
|
||||||
|
|
||||||
|
def test_concretize_two_virtuals_with_dual_provider(self):
|
||||||
|
"""Test a package with multiple virtual dependencies and force a provider
|
||||||
|
that provides both."""
|
||||||
|
s = Spec('hypre ^openblas-with-lapack').concretize()
|
||||||
|
|
||||||
|
|
||||||
|
def test_concretize_two_virtuals_with_dual_provider_and_a_conflict(self):
|
||||||
|
"""Test a package with multiple virtual dependencies and force a provider
|
||||||
|
that provides both, and another conflicting package that provides one."""
|
||||||
|
s = Spec('hypre ^openblas-with-lapack ^netlib-lapack')
|
||||||
|
self.assertRaises(spack.spec.MultipleProviderError, s.concretize)
|
||||||
|
|
||||||
|
|
||||||
def test_virtual_is_fully_expanded_for_callpath(self):
|
def test_virtual_is_fully_expanded_for_callpath(self):
|
||||||
# force dependence on fake "zmpi" by asking for MPI 10.0
|
# force dependence on fake "zmpi" by asking for MPI 10.0
|
||||||
spec = Spec('callpath ^mpi@10.0')
|
spec = Spec('callpath ^mpi@10.0')
|
||||||
|
@ -281,4 +309,3 @@ def test_find_spec_none(self):
|
||||||
Spec('d')),
|
Spec('d')),
|
||||||
Spec('e'))
|
Spec('e'))
|
||||||
self.assertEqual(None, find_spec(s['b'], lambda s: '+foo' in s))
|
self.assertEqual(None, find_spec(s['b'], lambda s: '+foo' in s))
|
||||||
|
|
||||||
|
|
39
var/spack/repos/builtin.mock/packages/hypre/package.py
Normal file
39
var/spack/repos/builtin.mock/packages/hypre/package.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://github.com/llnl/spack
|
||||||
|
# Please also see the LICENSE file for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License (as published by
|
||||||
|
# the Free Software Foundation) version 2.1 dated February 1999.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||||
|
# conditions of the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
from spack import *
|
||||||
|
|
||||||
|
class Hypre(Package):
|
||||||
|
"""Hypre is included here as an example of a package that depends on
|
||||||
|
both LAPACK and BLAS."""
|
||||||
|
homepage = "http://www.openblas.net"
|
||||||
|
url = "http://github.com/xianyi/OpenBLAS/archive/v0.2.15.tar.gz"
|
||||||
|
|
||||||
|
version('0.2.15', 'b1190f3d3471685f17cfd1ec1d252ac9')
|
||||||
|
|
||||||
|
depends_on('lapack')
|
||||||
|
depends_on('blas')
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
pass
|
|
@ -0,0 +1,38 @@
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://github.com/llnl/spack
|
||||||
|
# Please also see the LICENSE file for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License (as published by
|
||||||
|
# the Free Software Foundation) version 2.1 dated February 1999.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||||
|
# conditions of the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
from spack import *
|
||||||
|
|
||||||
|
class OpenblasWithLapack(Package):
|
||||||
|
"""Dummy version of OpenBLAS that also provides LAPACK, for testing."""
|
||||||
|
homepage = "http://www.openblas.net"
|
||||||
|
url = "http://github.com/xianyi/OpenBLAS/archive/v0.2.15.tar.gz"
|
||||||
|
|
||||||
|
version('0.2.15', 'b1190f3d3471685f17cfd1ec1d252ac9')
|
||||||
|
|
||||||
|
provides('lapack')
|
||||||
|
provides('blas')
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
pass
|
Loading…
Reference in a new issue