summaryrefslogtreecommitdiff
path: root/lib/spack/spack/spec.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/spec.py')
-rw-r--r--lib/spack/spack/spec.py46
1 files changed, 33 insertions, 13 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 8d9ecb320d..33ca196ad9 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1833,13 +1833,26 @@ class Spec(object):
changed = True
force = False
+ user_spec_deps = self.flat_dependencies(copy=False)
+
while changed:
- changes = (self.normalize(force, tests),
+ changes = (self.normalize(force, tests=tests,
+ user_spec_deps=user_spec_deps),
self._expand_virtual_packages(),
self._concretize_helper())
changed = any(changes)
force = True
+ visited_user_specs = set()
+ for dep in self.traverse():
+ visited_user_specs.add(dep.name)
+ visited_user_specs.update(x.name for x in dep.package.provided)
+
+ extra = set(user_spec_deps.keys()).difference(visited_user_specs)
+ if extra:
+ raise InvalidDependencyError(
+ self.name + " does not depend on " + comma_or(extra))
+
for s in self.traverse():
# After concretizing, assign namespaces to anything left.
# Note that this doesn't count as a "change". The repository
@@ -2190,7 +2203,7 @@ class Spec(object):
return any_change
- def normalize(self, force=False, tests=False):
+ def normalize(self, force=False, tests=False, user_spec_deps=None):
"""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:
@@ -2219,27 +2232,34 @@ class Spec(object):
# Ensure first that all packages & compilers in the DAG exist.
self.validate_or_raise()
- # Get all the dependencies into one DependencyMap
- spec_deps = self.flat_dependencies(copy=False)
+ # Clear the DAG and collect all dependencies in the DAG, which will be
+ # reapplied as constraints. All dependencies collected this way will
+ # have been created by a previous execution of 'normalize'.
+ # A dependency extracted here will only be reintegrated if it is
+ # discovered to apply according to _normalize_helper, so
+ # user-specified dependencies are recorded separately in case they
+ # refer to specs which take several normalization passes to
+ # materialize.
+ all_spec_deps = self.flat_dependencies(copy=False)
+
+ if user_spec_deps:
+ for name, spec in user_spec_deps.items():
+ if name not in all_spec_deps:
+ all_spec_deps[name] = spec
+ else:
+ all_spec_deps[name].constrain(spec)
# Initialize index of virtual dependency providers if
# concretize didn't pass us one already
provider_index = ProviderIndex(
- [s for s in spec_deps.values()], restrict=True)
+ [s for s in all_spec_deps.values()], restrict=True)
# traverse the package DAG and fill out dependencies according
# to package files & their 'when' specs
visited = set()
any_change = self._normalize_helper(
- visited, spec_deps, provider_index, tests)
-
- # 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)
- if extra:
- raise InvalidDependencyError(
- self.name + " does not depend on " + comma_or(extra))
+ visited, all_spec_deps, provider_index, tests)
# Mark the spec as normal once done.
self._normal = True