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.
This commit is contained in:
Massimiliano Culpo 2020-10-21 23:57:09 +02:00 committed by Todd Gamblin
parent a1fe88c95b
commit ada2fa36a9
2 changed files with 81 additions and 70 deletions

View File

@ -1351,6 +1351,12 @@ def define_version_constraints(self):
if v.satisfies(versions) 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 # don't bother restricting anything if all versions are allowed
if len(allowed_versions) == len(self.possible_versions[pkg_name]): if len(allowed_versions) == len(self.possible_versions[pkg_name]):
continue continue
@ -1658,16 +1664,19 @@ def build_specs(self, function_tuples):
repo = spack.repo.path.repo_for_pkg(spec) repo = spack.repo.path.repo_for_pkg(spec)
spec.namespace = repo.namespace spec.namespace = repo.namespace
# once this is done, everything is concrete
spec._mark_concrete()
# fix flags after all specs are constructed # fix flags after all specs are constructed
self.reorder_flags() 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 # Add external paths to specs with just external modules
for s in self._specs.values(): for s in self._specs.values():
spack.spec.Spec.ensure_external_path_if_external(s) spack.spec.Spec.ensure_external_path_if_external(s)
for s in self._specs.values():
s._mark_concrete()
for s in self._specs.values(): for s in self._specs.values():
spack.spec.Spec.ensure_no_deprecated(s) spack.spec.Spec.ensure_no_deprecated(s)

View File

@ -2293,73 +2293,7 @@ def _old_concretize(self, tests=False):
if extra: if extra:
raise InvalidDependencyError(self.name, extra) raise InvalidDependencyError(self.name, extra)
# This dictionary will store object IDs rather than Specs as keys Spec.inject_patches_variant(self)
# 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)
for s in self.traverse(): for s in self.traverse():
# TODO: Refactor this into a common method to build external specs # 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) # there are declared inconsistencies)
self.architecture.target.optimization_flags(self.compiler) 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 @staticmethod
def ensure_external_path_if_external(external_spec): def ensure_external_path_if_external(external_spec):
if external_spec.external_modules and not external_spec.external_path: if external_spec.external_modules and not external_spec.external_path: