From ada2fa36a9aae3b2f328ac5c553c30cf25bd79df Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Wed, 21 Oct 2020 23:57:09 +0200 Subject: concretizer: account for patches variant This is done after the builder has actually built the specs, to respect the semantics use with the old concretizer. Later we could move this to the solver as a multivalued variant. --- lib/spack/spack/solver/asp.py | 15 ++++++-- lib/spack/spack/spec.py | 84 ++++++++++++++++++++++--------------------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 1d4602ce62..292d3a5a3f 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -1351,6 +1351,12 @@ class SpackSolverSetup(object): 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 @@ class SpecBuilder(object): 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 1e42e90214..fb7c0a93ce 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -2293,10 +2293,50 @@ class Spec(object): if extra: raise InvalidDependencyError(self.name, extra) + Spec.inject_patches_variant(self) + + for s in self.traverse(): + # TODO: Refactor this into a common method to build external specs + # TODO: or turn external_path into a lazy property + Spec.ensure_external_path_if_external(s) + + # Mark everything in the spec as concrete, as well. + self._mark_concrete() + + # If any spec in the DAG is deprecated, throw an error + Spec.ensure_no_deprecated(self) + + # Now that the spec is concrete we should check if + # there are declared conflicts + # + # TODO: this needs rethinking, as currently we can only express + # TODO: internal configuration conflicts within one package. + matches = [] + for x in self.traverse(): + if x.external: + # external specs are already built, don't worry about whether + # it's possible to build that configuration with Spack + continue + for conflict_spec, when_list in x.package_class.conflicts.items(): + if x.satisfies(conflict_spec, strict=True): + for when_spec, msg in when_list: + if x.satisfies(when_spec, strict=True): + when = when_spec.copy() + when.name = x.name + matches.append((x, conflict_spec, when, msg)) + if matches: + raise ConflictsInSpecError(self, matches) + + # Check if we can produce an optimized binary (will throw if + # 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 self.traverse(): + 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 @@ -2319,10 +2359,9 @@ class Spec(object): 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, + 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: @@ -2341,8 +2380,7 @@ class Spec(object): if patches: all_patches = spec_to_patches.setdefault(id(dspec.spec), []) all_patches.extend(patches) - - for spec in self.traverse(): + for spec in root.traverse(): if id(spec) not in spec_to_patches: continue @@ -2361,42 +2399,6 @@ class Spec(object): mvar._patches_in_order_of_appearance = list( t[-1] for t in ordered_hashes) - for s in self.traverse(): - # TODO: Refactor this into a common method to build external specs - # TODO: or turn external_path into a lazy property - Spec.ensure_external_path_if_external(s) - - # Mark everything in the spec as concrete, as well. - self._mark_concrete() - - # If any spec in the DAG is deprecated, throw an error - Spec.ensure_no_deprecated(self) - - # Now that the spec is concrete we should check if - # there are declared conflicts - # - # TODO: this needs rethinking, as currently we can only express - # TODO: internal configuration conflicts within one package. - matches = [] - for x in self.traverse(): - if x.external: - # external specs are already built, don't worry about whether - # it's possible to build that configuration with Spack - continue - for conflict_spec, when_list in x.package_class.conflicts.items(): - if x.satisfies(conflict_spec, strict=True): - for when_spec, msg in when_list: - if x.satisfies(when_spec, strict=True): - when = when_spec.copy() - when.name = x.name - matches.append((x, conflict_spec, when, msg)) - if matches: - raise ConflictsInSpecError(self, matches) - - # Check if we can produce an optimized binary (will throw if - # there are declared inconsistencies) - self.architecture.target.optimization_flags(self.compiler) - @staticmethod def ensure_external_path_if_external(external_spec): if external_spec.external_modules and not external_spec.external_path: -- cgit v1.2.3-70-g09d2