Update concretize to check for more changes and iterate further.
This commit is contained in:
parent
9087f26537
commit
e097696390
@ -63,9 +63,9 @@ def concretize_version(self, spec):
|
||||
"""
|
||||
# return if already concrete.
|
||||
if spec.versions.concrete:
|
||||
return
|
||||
return False
|
||||
|
||||
# If there are known avaialble versions, return the most recent
|
||||
# If there are known available versions, return the most recent
|
||||
# version that satisfies the spec
|
||||
pkg = spec.package
|
||||
valid_versions = sorted(
|
||||
@ -93,6 +93,8 @@ def concretize_version(self, spec):
|
||||
else:
|
||||
spec.versions = ver([last])
|
||||
|
||||
return True # Things changed
|
||||
|
||||
|
||||
def concretize_architecture(self, spec):
|
||||
"""If the spec already had an architecture, return. Otherwise if
|
||||
@ -109,22 +111,27 @@ def concretize_architecture(self, spec):
|
||||
they're not explicit.
|
||||
"""
|
||||
if spec.architecture is not None:
|
||||
return
|
||||
return False
|
||||
|
||||
if spec.root.architecture:
|
||||
spec.architecture = spec.root.architecture
|
||||
else:
|
||||
spec.architecture = spack.architecture.sys_type()
|
||||
|
||||
assert(spec.architecture is not None)
|
||||
return True # changed
|
||||
|
||||
|
||||
def concretize_variants(self, spec):
|
||||
"""If the spec already has variants filled in, return. Otherwise, add
|
||||
the default variants from the package specification.
|
||||
"""
|
||||
changed = False
|
||||
for name, variant in spec.package.variants.items():
|
||||
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
|
||||
return changed
|
||||
|
||||
|
||||
def concretize_compiler(self, spec):
|
||||
@ -144,7 +151,7 @@ def concretize_compiler(self, spec):
|
||||
if (spec.compiler and
|
||||
spec.compiler.concrete and
|
||||
spec.compiler in all_compilers):
|
||||
return
|
||||
return False
|
||||
|
||||
try:
|
||||
nearest = next(p for p in spec.traverse(direction='parents')
|
||||
@ -165,6 +172,8 @@ def concretize_compiler(self, spec):
|
||||
except StopIteration:
|
||||
spec.compiler = spack.compilers.default_compiler().copy()
|
||||
|
||||
return True # things changed.
|
||||
|
||||
|
||||
def choose_provider(self, spec, providers):
|
||||
"""This is invoked for virtual specs. Given a spec with a virtual name,
|
||||
|
@ -736,26 +736,31 @@ def _concretize_helper(self, presets=None, visited=None):
|
||||
if visited is None: visited = set()
|
||||
|
||||
if self.name in visited:
|
||||
return
|
||||
return False
|
||||
|
||||
changed = False
|
||||
|
||||
# Concretize deps first -- this is a bottom-up process.
|
||||
for name in sorted(self.dependencies.keys()):
|
||||
self.dependencies[name]._concretize_helper(presets, visited)
|
||||
changed |= self.dependencies[name]._concretize_helper(presets, visited)
|
||||
|
||||
if self.name in presets:
|
||||
self.constrain(presets[self.name])
|
||||
changed |= self.constrain(presets[self.name])
|
||||
|
||||
else:
|
||||
# Concretize virtual dependencies last. Because they're added
|
||||
# to presets below, their constraints will all be merged, but we'll
|
||||
# still need to select a concrete package later.
|
||||
if not self.virtual:
|
||||
spack.concretizer.concretize_architecture(self)
|
||||
spack.concretizer.concretize_compiler(self)
|
||||
spack.concretizer.concretize_version(self)
|
||||
spack.concretizer.concretize_variants(self)
|
||||
changed |= any(
|
||||
(spack.concretizer.concretize_architecture(self),
|
||||
spack.concretizer.concretize_compiler(self),
|
||||
spack.concretizer.concretize_version(self),
|
||||
spack.concretizer.concretize_variants(self)))
|
||||
presets[self.name] = self
|
||||
|
||||
visited.add(self.name)
|
||||
return changed
|
||||
|
||||
|
||||
def _replace_with(self, concrete):
|
||||
@ -783,20 +788,22 @@ def _expand_virtual_packages(self):
|
||||
this are infrequent, but should implement this before it is
|
||||
a problem.
|
||||
"""
|
||||
changed = False
|
||||
while True:
|
||||
virtuals =[v for v in self.traverse() if v.virtual]
|
||||
if not virtuals:
|
||||
return
|
||||
return changed
|
||||
|
||||
for spec in virtuals:
|
||||
providers = spack.db.providers_for(spec)
|
||||
concrete = spack.concretizer.choose_provider(spec, providers)
|
||||
concrete = concrete.copy()
|
||||
spec._replace_with(concrete)
|
||||
changed = True
|
||||
|
||||
# If there are duplicate providers or duplicate provider deps, this
|
||||
# consolidates them and merge constraints.
|
||||
self.normalize(force=True)
|
||||
changed |= self.normalize(force=True)
|
||||
|
||||
|
||||
def concretize(self):
|
||||
@ -814,9 +821,16 @@ def concretize(self):
|
||||
if self._concrete:
|
||||
return
|
||||
|
||||
self.normalize()
|
||||
self._expand_virtual_packages()
|
||||
self._concretize_helper()
|
||||
changed = True
|
||||
force = False
|
||||
|
||||
while changed:
|
||||
changes = (self.normalize(force=force),
|
||||
self._expand_virtual_packages(),
|
||||
self._concretize_helper())
|
||||
changed = any(changes)
|
||||
force=True
|
||||
|
||||
self._concrete = True
|
||||
|
||||
|
||||
@ -982,6 +996,7 @@ def _merge_dependency(self, dep, visited, spec_deps, provider_index):
|
||||
# it from the package description.
|
||||
if dep.name not in spec_deps:
|
||||
spec_deps[dep.name] = dep.copy()
|
||||
changed = True
|
||||
|
||||
# Constrain package information with spec info
|
||||
try:
|
||||
@ -998,7 +1013,6 @@ def _merge_dependency(self, dep, visited, spec_deps, provider_index):
|
||||
dependency = spec_deps[dep.name]
|
||||
if dep.name not in self.dependencies:
|
||||
self._add_dependency(dependency)
|
||||
changed = True
|
||||
|
||||
changed |= dependency._normalize_helper(visited, spec_deps, provider_index)
|
||||
return changed
|
||||
@ -1031,13 +1045,12 @@ def _normalize_helper(self, visited, spec_deps, provider_index):
|
||||
if pkg_dep:
|
||||
changed |= self._merge_dependency(
|
||||
pkg_dep, visited, spec_deps, provider_index)
|
||||
|
||||
any_change |= changed
|
||||
|
||||
return any_change
|
||||
|
||||
|
||||
def normalize(self, **kwargs):
|
||||
def normalize(self, force=False):
|
||||
"""When specs are parsed, any dependencies specified are hanging off
|
||||
the root, and ONLY the ones that were explicitly provided are there.
|
||||
Normalization turns a partial flat spec into a DAG, where:
|
||||
@ -1050,13 +1063,13 @@ def normalize(self, **kwargs):
|
||||
package that provides a virtual package that is in the spec,
|
||||
then we replace the virtual package with the non-virtual one.
|
||||
|
||||
4. The spec DAG matches package DAG.
|
||||
4. The spec DAG matches package DAG, including default variant values.
|
||||
|
||||
TODO: normalize should probably implement some form of cycle detection,
|
||||
to ensure that the spec is actually a DAG.
|
||||
"""
|
||||
if self._normal and not kwargs.get('force', False):
|
||||
return
|
||||
if self._normal and not force:
|
||||
return False
|
||||
|
||||
# Ensure first that all packages & compilers in the DAG exist.
|
||||
self.validate_names()
|
||||
@ -1070,19 +1083,18 @@ def normalize(self, **kwargs):
|
||||
# traverse the package DAG and fill out dependencies according
|
||||
# to package files & their 'when' specs
|
||||
visited = set()
|
||||
self._normalize_helper(visited, spec_deps, index)
|
||||
any_change = self._normalize_helper(visited, spec_deps, index)
|
||||
|
||||
# If there are deps specified but not visited, they're not
|
||||
# actually deps of this package. Raise an error.
|
||||
extra = set(spec_deps.keys()).difference(visited)
|
||||
|
||||
# Anything left over is not a valid part of the spec.
|
||||
if extra:
|
||||
raise InvalidDependencyException(
|
||||
self.name + " does not depend on " + comma_or(extra))
|
||||
|
||||
# Mark the spec as normal once done.
|
||||
self._normal = True
|
||||
return any_change
|
||||
|
||||
|
||||
def normalized(self):
|
||||
|
@ -86,9 +86,24 @@ def test_chained_mpi(self):
|
||||
Spec('mpi'))))
|
||||
|
||||
|
||||
def test_default_variant(self):
|
||||
spec = Spec('optional-dep-test-3')
|
||||
spec.concretize()
|
||||
self.assertTrue('a' in spec)
|
||||
|
||||
spec = Spec('optional-dep-test-3~var')
|
||||
spec.concretize()
|
||||
self.assertTrue('a' in spec)
|
||||
|
||||
spec = Spec('optional-dep-test-3+var')
|
||||
spec.concretize()
|
||||
self.assertTrue('b' in spec)
|
||||
|
||||
|
||||
def test_transitive_chain(self):
|
||||
# Each of these dependencies comes from a conditional
|
||||
# dependency on another. This requires iterating to evaluate
|
||||
# the whole chain.
|
||||
self.check_normalize('optional-dep-test+f',
|
||||
Spec('optional-dep-test+f', Spec('f'), Spec('g'), Spec('mpi')))
|
||||
self.check_normalize(
|
||||
'optional-dep-test+f',
|
||||
Spec('optional-dep-test+f', Spec('f'), Spec('g'), Spec('mpi')))
|
||||
|
17
var/spack/mock_packages/optional-dep-test-3/package.py
Normal file
17
var/spack/mock_packages/optional-dep-test-3/package.py
Normal file
@ -0,0 +1,17 @@
|
||||
from spack import *
|
||||
|
||||
class OptionalDepTest3(Package):
|
||||
"""Depends on the optional-dep-test package"""
|
||||
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/optional-dep-test-3-1.0.tar.gz"
|
||||
|
||||
version('1.0', '0123456789abcdef0123456789abcdef')
|
||||
|
||||
variant('var', default=False)
|
||||
|
||||
depends_on('a', when='~var')
|
||||
depends_on('b', when='+var')
|
||||
|
||||
def install(self, spec, prefix):
|
||||
pass
|
Loading…
Reference in New Issue
Block a user