ASP-based solver: fix rules on version weights selection (#31153)

* ASP: sort and deduplicate version weights from installed specs

* Pick version weights according to provenance

* Add unit test
This commit is contained in:
Massimiliano Culpo 2022-06-16 23:17:40 +02:00 committed by GitHub
parent 392b548312
commit 267358a799
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 4 deletions

View File

@ -748,7 +748,13 @@ def key_fn(version):
pkg = packagize(pkg)
declared_versions = self.declared_versions[pkg.name]
most_to_least_preferred = sorted(declared_versions, key=key_fn)
partially_sorted_versions = sorted(set(declared_versions), key=key_fn)
most_to_least_preferred = []
for _, group in itertools.groupby(partially_sorted_versions, key=key_fn):
most_to_least_preferred.extend(list(sorted(
group, reverse=True, key=lambda x: spack.version.ver(x.version)
)))
for weight, declared_version in enumerate(most_to_least_preferred):
self.gen.fact(fn.version_declared(

View File

@ -107,10 +107,28 @@ possible_version_weight(Package, Weight)
:- version(Package, Version),
version_declared(Package, Version, Weight).
version_weight(Package, Weight)
% we can't use the weight for an external version if we don't use the
% corresponding external spec.
:- version(Package, Version),
node(Package),
Weight = #min{W : version_declared(Package, Version, W)}.
version_weight(Package, Weight),
version_declared(Package, Version, Weight, "external"),
not external(Package).
% we can't use a weight from an installed spec if we are building it
% and vice-versa
:- version(Package, Version),
version_weight(Package, Weight),
version_declared(Package, Version, Weight, "installed"),
build(Package).
:- version(Package, Version),
version_weight(Package, Weight),
not version_declared(Package, Version, Weight, "installed"),
not build(Package).
1 { version_weight(Package, Weight) : version_declared(Package, Version, Weight) } 1
:- version(Package, Version),
node(Package).
% node_version_satisfies implies that exactly one of the satisfying versions
% is the package's version, and vice versa.

View File

@ -1753,3 +1753,36 @@ def test_misleading_error_message_on_version(self, mutable_database):
with pytest.raises(spack.solver.asp.UnsatisfiableSpecError,
match="'dep-with-variants' satisfies '@999'"):
solver.driver.solve(setup, [root_spec], reuse=reusable_specs)
@pytest.mark.regression('31148')
def test_version_weight_and_provenance(self):
"""Test package preferences during coconcretization."""
import spack.solver.asp
if spack.config.get('config:concretizer') == 'original':
pytest.skip('Original concretizer cannot reuse')
reusable_specs = [
spack.spec.Spec(spec_str).concretized()
for spec_str in ('b@0.9', 'b@1.0')
]
root_spec = spack.spec.Spec('a foobar=bar')
with spack.config.override("concretizer:reuse", True):
solver = spack.solver.asp.Solver()
setup = spack.solver.asp.SpackSolverSetup()
result = solver.driver.solve(
setup, [root_spec], reuse=reusable_specs, out=sys.stdout
)
# The result here should have a single spec to build ('a')
# and it should be using b@1.0 with a version badness of 2
# The provenance is:
# version_declared("b","1.0",0,"package_py").
# version_declared("b","0.9",1,"package_py").
# version_declared("b","1.0",2,"installed").
# version_declared("b","0.9",3,"installed").
for criterion in [
(1, None, 'number of packages to build (vs. reuse)'),
(2, 0, 'version badness')
]:
assert criterion in result.criteria
assert result.specs[0].satisfies('^b@1.0')