Revert "ASP-based solver: don't declare deprecated versions unless required (#38181)" (#40010)

This reverts commit babd29da50.
This commit is contained in:
Harmen Stoppels 2023-09-13 23:33:55 +02:00 committed by GitHub
parent eee8fdc438
commit d7b5a27d1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 91 deletions

View File

@ -176,29 +176,17 @@ def solve(parser, args):
output = sys.stdout if "asp" in show else None output = sys.stdout if "asp" in show else None
setup_only = set(show) == {"asp"} setup_only = set(show) == {"asp"}
unify = spack.config.get("concretizer:unify") unify = spack.config.get("concretizer:unify")
allow_deprecated = spack.config.get("config:deprecated", False)
if unify != "when_possible": if unify != "when_possible":
# set up solver parameters # set up solver parameters
# Note: reuse and other concretizer prefs are passed as configuration # Note: reuse and other concretizer prefs are passed as configuration
result = solver.solve( result = solver.solve(
specs, specs, out=output, timers=args.timers, stats=args.stats, setup_only=setup_only
out=output,
timers=args.timers,
stats=args.stats,
setup_only=setup_only,
deprecated=allow_deprecated,
) )
if not setup_only: if not setup_only:
_process_result(result, show, required_format, kwargs) _process_result(result, show, required_format, kwargs)
else: else:
for idx, result in enumerate( for idx, result in enumerate(
solver.solve_in_rounds( solver.solve_in_rounds(specs, out=output, timers=args.timers, stats=args.stats)
specs,
out=output,
timers=args.timers,
stats=args.stats,
deprecated=allow_deprecated,
)
): ):
if "solutions" in show: if "solutions" in show:
tty.msg("ROUND {0}".format(idx)) tty.msg("ROUND {0}".format(idx))

View File

@ -744,11 +744,8 @@ def concretize_specs_together(*abstract_specs, **kwargs):
def _concretize_specs_together_new(*abstract_specs, **kwargs): def _concretize_specs_together_new(*abstract_specs, **kwargs):
import spack.solver.asp import spack.solver.asp
allow_deprecated = spack.config.get("config:deprecated", False)
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
result = solver.solve( result = solver.solve(abstract_specs, tests=kwargs.get("tests", False))
abstract_specs, tests=kwargs.get("tests", False), deprecated=allow_deprecated
)
result.raise_if_unsat() result.raise_if_unsat()
return [s.copy() for s in result.specs] return [s.copy() for s in result.specs]

View File

@ -1395,10 +1395,7 @@ def _concretize_together_where_possible(
result_by_user_spec = {} result_by_user_spec = {}
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
allow_deprecated = spack.config.get("config:deprecated", False) for result in solver.solve_in_rounds(specs_to_concretize, tests=tests):
for result in solver.solve_in_rounds(
specs_to_concretize, tests=tests, deprecated=allow_deprecated
):
result_by_user_spec.update(result.specs_by_input) result_by_user_spec.update(result.specs_by_input)
result = [] result = []

View File

@ -13,7 +13,7 @@
import re import re
import types import types
import warnings import warnings
from typing import List, NamedTuple, Optional, Sequence, Tuple, Union from typing import List, NamedTuple, Tuple, Union
import archspec.cpu import archspec.cpu
@ -137,15 +137,7 @@ class RequirementKind(enum.Enum):
PACKAGE = enum.auto() PACKAGE = enum.auto()
class DeclaredVersion(NamedTuple): DeclaredVersion = collections.namedtuple("DeclaredVersion", ["version", "idx", "origin"])
"""Data class to contain information on declared versions used in the solve"""
#: String representation of the version
version: str
#: Unique index assigned to this version
idx: int
#: Provenance of the version
origin: Provenance
# Below numbers are used to map names of criteria to the order # Below numbers are used to map names of criteria to the order
@ -792,7 +784,7 @@ def fact(self, head):
if choice: if choice:
self.assumptions.append(atom) self.assumptions.append(atom)
def solve(self, setup, specs, reuse=None, output=None, control=None, deprecated=False): def solve(self, setup, specs, reuse=None, output=None, control=None):
"""Set up the input and solve for dependencies of ``specs``. """Set up the input and solve for dependencies of ``specs``.
Arguments: Arguments:
@ -803,7 +795,6 @@ def solve(self, setup, specs, reuse=None, output=None, control=None, deprecated=
the output of this solve. the output of this solve.
control (clingo.Control): configuration for the solver. If None, control (clingo.Control): configuration for the solver. If None,
default values will be used default values will be used
deprecated: if True, allow deprecated versions in the solve
Return: Return:
A tuple of the solve result, the timer for the different phases of the A tuple of the solve result, the timer for the different phases of the
@ -823,7 +814,7 @@ def solve(self, setup, specs, reuse=None, output=None, control=None, deprecated=
timer.start("setup") timer.start("setup")
with self.control.backend() as backend: with self.control.backend() as backend:
self.backend = backend self.backend = backend
setup.setup(self, specs, reuse=reuse, deprecated=deprecated) setup.setup(self, specs, reuse=reuse)
timer.stop("setup") timer.stop("setup")
timer.start("load") timer.start("load")
@ -1903,7 +1894,7 @@ class Body:
return clauses return clauses
def define_package_versions_and_validate_preferences( def define_package_versions_and_validate_preferences(
self, possible_pkgs, *, require_checksum: bool, deprecated: bool self, possible_pkgs, require_checksum: bool
): ):
"""Declare any versions in specs not declared in packages.""" """Declare any versions in specs not declared in packages."""
packages_yaml = spack.config.get("packages") packages_yaml = spack.config.get("packages")
@ -1923,15 +1914,13 @@ def define_package_versions_and_validate_preferences(
] ]
for idx, (v, version_info) in enumerate(package_py_versions): for idx, (v, version_info) in enumerate(package_py_versions):
if version_info.get("deprecated", False):
self.deprecated_versions[pkg_name].add(v)
if not deprecated:
continue
self.possible_versions[pkg_name].add(v) self.possible_versions[pkg_name].add(v)
self.declared_versions[pkg_name].append( self.declared_versions[pkg_name].append(
DeclaredVersion(version=v, idx=idx, origin=Provenance.PACKAGE_PY) DeclaredVersion(version=v, idx=idx, origin=Provenance.PACKAGE_PY)
) )
deprecated = version_info.get("deprecated", False)
if deprecated:
self.deprecated_versions[pkg_name].add(v)
if pkg_name not in packages_yaml or "version" not in packages_yaml[pkg_name]: if pkg_name not in packages_yaml or "version" not in packages_yaml[pkg_name]:
continue continue
@ -1960,7 +1949,7 @@ def define_package_versions_and_validate_preferences(
) )
self.possible_versions[pkg_name].add(vdef) self.possible_versions[pkg_name].add(vdef)
def define_ad_hoc_versions_from_specs(self, specs, origin, *, require_checksum: bool): def define_ad_hoc_versions_from_specs(self, specs, origin, require_checksum: bool):
"""Add concrete versions to possible versions from lists of CLI/dev specs.""" """Add concrete versions to possible versions from lists of CLI/dev specs."""
for s in traverse.traverse_nodes(specs): for s in traverse.traverse_nodes(specs):
# If there is a concrete version on the CLI *that we know nothing # If there is a concrete version on the CLI *that we know nothing
@ -2340,14 +2329,7 @@ def define_concrete_input_specs(self, specs, possible):
if spec.concrete: if spec.concrete:
self._facts_from_concrete_spec(spec, possible) self._facts_from_concrete_spec(spec, possible)
def setup( def setup(self, driver, specs, reuse=None):
self,
driver: PyclingoDriver,
specs: Sequence[spack.spec.Spec],
*,
reuse: Optional[List[spack.spec.Spec]] = None,
deprecated: bool = False,
):
"""Generate an ASP program with relevant constraints for specs. """Generate an ASP program with relevant constraints for specs.
This calls methods on the solve driver to set up the problem with This calls methods on the solve driver to set up the problem with
@ -2355,10 +2337,9 @@ def setup(
specs, as well as constraints from the specs themselves. specs, as well as constraints from the specs themselves.
Arguments: Arguments:
driver: driver instance of this solve driver (PyclingoDriver): driver instance of this solve
specs: list of Specs to solve specs (list): list of Specs to solve
reuse: list of concrete specs that can be reused reuse (None or list): list of concrete specs that can be reused
deprecated: if True adds deprecated versions into the solve
""" """
self._condition_id_counter = itertools.count() self._condition_id_counter = itertools.count()
@ -2387,7 +2368,7 @@ def setup(
# Calculate develop specs # Calculate develop specs
# they will be used in addition to command line specs # they will be used in addition to command line specs
# in determining known versions/targets/os # in determining known versions/targets/os
dev_specs: Tuple[spack.spec.Spec, ...] = () dev_specs = ()
env = ev.active_environment() env = ev.active_environment()
if env: if env:
dev_specs = tuple( dev_specs = tuple(
@ -2433,17 +2414,11 @@ def setup(
self.external_packages() self.external_packages()
# TODO: make a config option for this undocumented feature # TODO: make a config option for this undocumented feature
checksummed = "SPACK_CONCRETIZER_REQUIRE_CHECKSUM" in os.environ require_checksum = "SPACK_CONCRETIZER_REQUIRE_CHECKSUM" in os.environ
self.define_package_versions_and_validate_preferences( self.define_package_versions_and_validate_preferences(self.pkgs, require_checksum)
self.pkgs, deprecated=deprecated, require_checksum=checksummed self.define_ad_hoc_versions_from_specs(specs, Provenance.SPEC, require_checksum)
) self.define_ad_hoc_versions_from_specs(dev_specs, Provenance.DEV_SPEC, require_checksum)
self.define_ad_hoc_versions_from_specs( self.validate_and_define_versions_from_requirements(require_checksum)
specs, Provenance.SPEC, require_checksum=checksummed
)
self.define_ad_hoc_versions_from_specs(
dev_specs, Provenance.DEV_SPEC, require_checksum=checksummed
)
self.validate_and_define_versions_from_requirements(require_checksum=checksummed)
self.gen.h1("Package Constraints") self.gen.h1("Package Constraints")
for pkg in sorted(self.pkgs): for pkg in sorted(self.pkgs):
@ -2492,7 +2467,7 @@ def literal_specs(self, specs):
if self.concretize_everything: if self.concretize_everything:
self.gen.fact(fn.solve_literal(idx)) self.gen.fact(fn.solve_literal(idx))
def validate_and_define_versions_from_requirements(self, *, require_checksum: bool): def validate_and_define_versions_from_requirements(self, require_checksum: bool):
"""If package requirements mention concrete versions that are not mentioned """If package requirements mention concrete versions that are not mentioned
elsewhere, then we need to collect those to mark them as possible elsewhere, then we need to collect those to mark them as possible
versions. If they are abstract and statically have no match, then we versions. If they are abstract and statically have no match, then we
@ -2957,16 +2932,7 @@ def _reusable_specs(self, specs):
return reusable_specs return reusable_specs
def solve( def solve(self, specs, out=None, timers=False, stats=False, tests=False, setup_only=False):
self,
specs,
out=None,
timers=False,
stats=False,
tests=False,
setup_only=False,
deprecated=False,
):
""" """
Arguments: Arguments:
specs (list): List of ``Spec`` objects to solve for. specs (list): List of ``Spec`` objects to solve for.
@ -2977,7 +2943,6 @@ def solve(
If a tuple of package names, concretize test dependencies for named If a tuple of package names, concretize test dependencies for named
packages (defaults to False: do not concretize test dependencies). packages (defaults to False: do not concretize test dependencies).
setup_only (bool): if True, stop after setup and don't solve (default False). setup_only (bool): if True, stop after setup and don't solve (default False).
deprecated (bool): allow deprecated version in the solve
""" """
# Check upfront that the variants are admissible # Check upfront that the variants are admissible
specs = [s.lookup_hash() for s in specs] specs = [s.lookup_hash() for s in specs]
@ -2985,14 +2950,10 @@ def solve(
reusable_specs.extend(self._reusable_specs(specs)) reusable_specs.extend(self._reusable_specs(specs))
setup = SpackSolverSetup(tests=tests) setup = SpackSolverSetup(tests=tests)
output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=setup_only) output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=setup_only)
result, _, _ = self.driver.solve( result, _, _ = self.driver.solve(setup, specs, reuse=reusable_specs, output=output)
setup, specs, reuse=reusable_specs, output=output, deprecated=deprecated
)
return result return result
def solve_in_rounds( def solve_in_rounds(self, specs, out=None, timers=False, stats=False, tests=False):
self, specs, out=None, timers=False, stats=False, tests=False, deprecated=False
):
"""Solve for a stable model of specs in multiple rounds. """Solve for a stable model of specs in multiple rounds.
This relaxes the assumption of solve that everything must be consistent and This relaxes the assumption of solve that everything must be consistent and
@ -3007,7 +2968,6 @@ def solve_in_rounds(
timers (bool): print timing if set to True timers (bool): print timing if set to True
stats (bool): print internal statistics if set to True stats (bool): print internal statistics if set to True
tests (bool): add test dependencies to the solve tests (bool): add test dependencies to the solve
deprecated (bool): allow deprecated version in the solve
""" """
specs = [s.lookup_hash() for s in specs] specs = [s.lookup_hash() for s in specs]
reusable_specs = self._check_input_and_extract_concrete_specs(specs) reusable_specs = self._check_input_and_extract_concrete_specs(specs)
@ -3021,7 +2981,7 @@ def solve_in_rounds(
output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=False) output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=False)
while True: while True:
result, _, _ = self.driver.solve( result, _, _ = self.driver.solve(
setup, input_specs, reuse=reusable_specs, output=output, deprecated=deprecated setup, input_specs, reuse=reusable_specs, output=output
) )
yield result yield result

View File

@ -1361,6 +1361,16 @@ opt_criterion(75, "requirement weight").
build_priority(PackageNode, Priority) build_priority(PackageNode, Priority)
}. }.
% Minimize the number of deprecated versions being used
opt_criterion(73, "deprecated versions used").
#minimize{ 0@273: #true }.
#minimize{ 0@73: #true }.
#minimize{
1@73+Priority,PackageNode
: attr("deprecated", PackageNode, _),
build_priority(PackageNode, Priority)
}.
% Minimize the: % Minimize the:
% 1. Version weight % 1. Version weight
% 2. Number of variants with a non default value, if not set % 2. Number of variants with a non default value, if not set

View File

@ -2963,9 +2963,8 @@ def _new_concretize(self, tests=False):
if self._concrete: if self._concrete:
return return
allow_deprecated = spack.config.get("config:deprecated", False)
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
result = solver.solve([self], tests=tests, deprecated=allow_deprecated) result = solver.solve([self], tests=tests)
result.raise_if_unsat() result.raise_if_unsat()
# take the best answer # take the best answer

View File

@ -1356,7 +1356,7 @@ def test_multivalued_variants_from_cli(self, spec_str, expected_dict):
# Version 1.1.0 is deprecated and should not be selected, unless we # Version 1.1.0 is deprecated and should not be selected, unless we
# explicitly asked for that # explicitly asked for that
("deprecated-versions", ["deprecated-versions@1.0.0"]), ("deprecated-versions", ["deprecated-versions@1.0.0"]),
("deprecated-versions@=1.1.0", ["deprecated-versions@1.1.0"]), ("deprecated-versions@1.1.0", ["deprecated-versions@1.1.0"]),
], ],
) )
@pytest.mark.only_clingo("Use case not supported by the original concretizer") @pytest.mark.only_clingo("Use case not supported by the original concretizer")

View File

@ -545,13 +545,13 @@ def test_reuse_oneof(concretize_scope, create_test_repo, mutable_database, fake_
def test_requirements_are_higher_priority_than_deprecation(concretize_scope, test_repo): def test_requirements_are_higher_priority_than_deprecation(concretize_scope, test_repo):
"""Test that users can override a deprecated version with a requirement.""" """Test that users can override a deprecated version with a requirement."""
# 2.3 is a deprecated versions. Ensure that any_of picks both constraints, # @2.3 is a deprecated versions. Ensure that any_of picks both constraints,
# since they are possible # since they are possible
conf_str = """\ conf_str = """\
packages: packages:
y: y:
require: require:
- any_of: ["@=2.3", "%gcc"] - any_of: ["@2.3", "%gcc"]
""" """
update_packages_config(conf_str) update_packages_config(conf_str)