diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 1d4602ce621..292d3a5a3f5 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -1351,6 +1351,12 @@ def define_version_constraints(self): if v.satisfies(versions) ] + # This is needed to account for a variable number of + # numbers e.g. if both 1.0 and 1.0.2 are possible versions + exact_match = [v for v in allowed_versions if v == versions] + if exact_match: + allowed_versions = exact_match + # don't bother restricting anything if all versions are allowed if len(allowed_versions) == len(self.possible_versions[pkg_name]): continue @@ -1658,16 +1664,19 @@ def build_specs(self, function_tuples): repo = spack.repo.path.repo_for_pkg(spec) spec.namespace = repo.namespace - # once this is done, everything is concrete - spec._mark_concrete() - # fix flags after all specs are constructed self.reorder_flags() + for s in self._specs.values(): + spack.spec.Spec.inject_patches_variant(s) + # Add external paths to specs with just external modules for s in self._specs.values(): spack.spec.Spec.ensure_external_path_if_external(s) + for s in self._specs.values(): + s._mark_concrete() + for s in self._specs.values(): spack.spec.Spec.ensure_no_deprecated(s) diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 1e42e90214d..fb7c0a93cea 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -2293,73 +2293,7 @@ def _old_concretize(self, tests=False): if extra: raise InvalidDependencyError(self.name, extra) - # This dictionary will store object IDs rather than Specs as keys - # since the Spec __hash__ will change as patches are added to them - spec_to_patches = {} - for s in self.traverse(): - # After concretizing, assign namespaces to anything left. - # Note that this doesn't count as a "change". The repository - # configuration is constant throughout a spack run, and - # normalize and concretize evaluate Packages using Repo.get(), - # which respects precedence. So, a namespace assignment isn't - # changing how a package name would have been interpreted and - # we can do it as late as possible to allow as much - # compatibility across repositories as possible. - if s.namespace is None: - s.namespace = spack.repo.path.repo_for_pkg(s.name).namespace - - if s.concrete: - continue - - # Add any patches from the package to the spec. - patches = [] - for cond, patch_list in s.package_class.patches.items(): - if s.satisfies(cond, strict=True): - for patch in patch_list: - patches.append(patch) - if patches: - spec_to_patches[id(s)] = patches - - # Also record all patches required on dependencies by - # depends_on(..., patch=...) - for dspec in self.traverse_edges(deptype=all, - cover='edges', root=False): - pkg_deps = dspec.parent.package_class.dependencies - if dspec.spec.name not in pkg_deps: - continue - - if dspec.spec.concrete: - continue - - patches = [] - for cond, dependency in pkg_deps[dspec.spec.name].items(): - if dspec.parent.satisfies(cond, strict=True): - for pcond, patch_list in dependency.patches.items(): - if dspec.spec.satisfies(pcond): - for patch in patch_list: - patches.append(patch) - if patches: - all_patches = spec_to_patches.setdefault(id(dspec.spec), []) - all_patches.extend(patches) - - for spec in self.traverse(): - if id(spec) not in spec_to_patches: - continue - - patches = list(lang.dedupe(spec_to_patches[id(spec)])) - mvar = spec.variants.setdefault( - 'patches', vt.MultiValuedVariant('patches', ()) - ) - mvar.value = tuple(p.sha256 for p in patches) - # FIXME: Monkey patches mvar to store patches order - full_order_keys = list(tuple(p.ordering_key) + (p.sha256,) for p - in patches) - ordered_hashes = sorted(full_order_keys) - tty.debug("Ordered hashes [{0}]: ".format(spec.name) + - ', '.join('/'.join(str(e) for e in t) - for t in ordered_hashes)) - mvar._patches_in_order_of_appearance = list( - t[-1] for t in ordered_hashes) + Spec.inject_patches_variant(self) for s in self.traverse(): # TODO: Refactor this into a common method to build external specs @@ -2397,6 +2331,74 @@ def _old_concretize(self, tests=False): # there are declared inconsistencies) self.architecture.target.optimization_flags(self.compiler) + @staticmethod + def inject_patches_variant(root): + # This dictionary will store object IDs rather than Specs as keys + # since the Spec __hash__ will change as patches are added to them + spec_to_patches = {} + for s in root.traverse(): + # After concretizing, assign namespaces to anything left. + # Note that this doesn't count as a "change". The repository + # configuration is constant throughout a spack run, and + # normalize and concretize evaluate Packages using Repo.get(), + # which respects precedence. So, a namespace assignment isn't + # changing how a package name would have been interpreted and + # we can do it as late as possible to allow as much + # compatibility across repositories as possible. + if s.namespace is None: + s.namespace = spack.repo.path.repo_for_pkg(s.name).namespace + + if s.concrete: + continue + + # Add any patches from the package to the spec. + patches = [] + for cond, patch_list in s.package_class.patches.items(): + if s.satisfies(cond, strict=True): + for patch in patch_list: + patches.append(patch) + if patches: + spec_to_patches[id(s)] = patches + # Also record all patches required on dependencies by + # depends_on(..., patch=...) + for dspec in root.traverse_edges(deptype=all, + cover='edges', root=False): + pkg_deps = dspec.parent.package_class.dependencies + if dspec.spec.name not in pkg_deps: + continue + + if dspec.spec.concrete: + continue + + patches = [] + for cond, dependency in pkg_deps[dspec.spec.name].items(): + if dspec.parent.satisfies(cond, strict=True): + for pcond, patch_list in dependency.patches.items(): + if dspec.spec.satisfies(pcond): + for patch in patch_list: + patches.append(patch) + if patches: + all_patches = spec_to_patches.setdefault(id(dspec.spec), []) + all_patches.extend(patches) + for spec in root.traverse(): + if id(spec) not in spec_to_patches: + continue + + patches = list(lang.dedupe(spec_to_patches[id(spec)])) + mvar = spec.variants.setdefault( + 'patches', vt.MultiValuedVariant('patches', ()) + ) + mvar.value = tuple(p.sha256 for p in patches) + # FIXME: Monkey patches mvar to store patches order + full_order_keys = list(tuple(p.ordering_key) + (p.sha256,) for p + in patches) + ordered_hashes = sorted(full_order_keys) + tty.debug("Ordered hashes [{0}]: ".format(spec.name) + + ', '.join('/'.join(str(e) for e in t) + for t in ordered_hashes)) + mvar._patches_in_order_of_appearance = list( + t[-1] for t in ordered_hashes) + @staticmethod def ensure_external_path_if_external(external_spec): if external_spec.external_modules and not external_spec.external_path: