From fc2ac099cddc8896f0741091b6be96d4379fd5e2 Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Wed, 12 May 2021 16:17:38 +0200 Subject: ASP-based solver: account for deprecated versions (#23491) fixes #22351 The ASP-based solver now accounts for the presence in the DAG of deprecated versions and tries to minimize their number at highest priority. --- lib/spack/spack/solver/asp.py | 16 +++++++++++++++- lib/spack/spack/solver/concretize.lp | 9 +++++++++ lib/spack/spack/solver/display.lp | 3 +++ lib/spack/spack/test/concretize.py | 16 ++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 8fb426c325..99e556a764 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -411,6 +411,7 @@ class SpackSolverSetup(object): self.gen = None # set by setup() self.possible_versions = {} self.versions_in_package_py = {} + self.deprecated_versions = {} self.versions_from_externals = {} self.possible_virtuals = None self.possible_compilers = [] @@ -481,6 +482,11 @@ class SpackSolverSetup(object): for i, v in enumerate(most_to_least_preferred): self.gen.fact(fn.version_declared(pkg.name, v, i)) + # Declare deprecated versions for this package, if any + deprecated = self.deprecated_versions[pkg.name] + for v in sorted(deprecated): + self.gen.fact(fn.deprecated_version(pkg.name, v)) + def spec_versions(self, spec): """Return list of clauses expressing spec's version constraints.""" spec = specify(spec) @@ -1020,12 +1026,16 @@ class SpackSolverSetup(object): self.possible_versions = collections.defaultdict(set) self.versions_in_package_py = collections.defaultdict(set) self.versions_from_externals = collections.defaultdict(set) + self.deprecated_versions = collections.defaultdict(set) for pkg_name in possible_pkgs: pkg = spack.repo.get(pkg_name) - for v in pkg.versions: + for v, version_info in pkg.versions.items(): self.versions_in_package_py[pkg_name].add(v) self.possible_versions[pkg_name].add(v) + deprecated = version_info.get('deprecated', False) + if deprecated: + self.deprecated_versions[pkg_name].add(v) for spec in specs: for dep in spec.traverse(): @@ -1553,6 +1563,10 @@ class SpecBuilder(object): check_same_flags(spec.compiler_flags, flags) spec.compiler_flags.update(flags) + def deprecated(self, pkg, version): + msg = 'using "{0}@{1}" which is a deprecated version' + tty.warn(msg.format(pkg, version)) + def build_specs(self, function_tuples): # Functions don't seem to be in particular order in output. Sort # them here so that directives that build objects (like node and diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index 8b5671ee4e..ec0d4fdf5a 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -19,6 +19,9 @@ version_declared(Package, Version) :- version_declared(Package, Version, _). 1 { version(Package, Version) : version_declared(Package, Version) } 1 :- node(Package). +% If we select a deprecated version, mark the package as deprecated +deprecated(Package, Version) :- version(Package, Version), deprecated_version(Package, Version). + possible_version_weight(Package, Weight) :- version(Package, Version), version_declared(Package, Version, Weight), not preferred_version_declared(Package, Version, _). @@ -37,6 +40,7 @@ version_satisfies(Package, Constraint) #defined preferred_version_declared/3. #defined version_satisfies/3. +#defined deprecated_version/2. %----------------------------------------------------------------------------- % Spec conditions and imposed constraints @@ -696,6 +700,11 @@ no_flags(Package, FlagType) % 2. a `#minimize{ 0@2 : #true }.` statement that ensures the criterion % is displayed (clingo doesn't display sums over empty sets by default) +% Minimize the number of deprecated versions being used +opt_criterion(16, "deprecated versions used"). +#minimize{ 0@16 : #true }. +#minimize{ 1@16,Package : deprecated(Package, _)}. + % The highest priority is to minimize the: % 1. Version weight % 2. Number of variants with a non default value, if not set diff --git a/lib/spack/spack/solver/display.lp b/lib/spack/spack/solver/display.lp index c1231ca62a..818ce2d90a 100644 --- a/lib/spack/spack/solver/display.lp +++ b/lib/spack/spack/solver/display.lp @@ -28,3 +28,6 @@ % names of optimization criteria #show opt_criterion/2. + +% deprecated packages +#show deprecated/2. diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index fd97c8684f..f392387099 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -1228,3 +1228,19 @@ class TestConcretize(object): for constraint, value in expected_dict.items(): assert s.satisfies(constraint) == value + + @pytest.mark.regression('22351') + @pytest.mark.parametrize('spec_str,expected', [ + # Version 1.1.0 is deprecated and should not be selected, unless we + # explicitly asked for that + ('deprecated-versions', ['deprecated-versions@1.0.0']), + ('deprecated-versions@1.1.0', ['deprecated-versions@1.1.0']), + ]) + def test_deprecated_versions_not_selected(self, spec_str, expected): + if spack.config.get('config:concretizer') == 'original': + pytest.xfail('Known failure of the original concretizer') + + s = Spec(spec_str).concretized() + + for abstract_spec in expected: + assert abstract_spec in s -- cgit v1.2.3-60-g2f50