concretizer: enforce host compat when reuse only (#44428)

Fixes a bug in the concretizer where specs depending on a host
incompatible libc would be used. This bug triggers when nothing is
built.

In the case where everything is reused, there is no libc provider from
the perspective of the solver, there is only compatible_libc. This
commit ensures that we require a host compatible libc on any reused
spec, additionally to requiring compat with the chosen libc provider.

Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
This commit is contained in:
Harmen Stoppels 2024-05-30 07:31:28 +02:00 committed by GitHub
parent 0601d6a0c5
commit 39ace5fc45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 4 deletions

View File

@ -2440,7 +2440,7 @@ def setup(
if using_libc_compatibility():
for libc in self.libcs:
self.gen.fact(fn.allowed_libc(libc.name, libc.version))
self.gen.fact(fn.host_libc(libc.name, libc.version))
if not allow_deprecated:
self.gen.fact(fn.deprecated_versions_not_allowed())

View File

@ -24,12 +24,20 @@ has_built_packages() :- build(X), not external(X).
% A libc is needed in the DAG
:- has_built_packages(), not provider(_, node(0, "libc")).
% The libc must be chosen among available ones
% Non-libc reused specs must be host libc compatible. In case we build packages, we get a
% host compatible libc provider from other rules. If nothing is built, there is no libc provider,
% since it's pruned from reusable specs, meaning we have to explicitly impose reused specs are host
% compatible.
:- attr("hash", node(R, ReusedPackage), Hash),
not provider(node(R, ReusedPackage), node(0, "libc")),
not attr("compatible_libc", node(R, ReusedPackage), _, _).
% The libc provider must be one that a compiler can target
:- has_built_packages(),
provider(node(X, LibcPackage), node(0, "libc")),
attr("node", node(X, LibcPackage)),
attr("version", node(X, LibcPackage), LibcVersion),
not allowed_libc(LibcPackage, LibcVersion).
not host_libc(LibcPackage, LibcVersion).
% A built node must depend on libc
:- build(PackageNode),

View File

@ -28,7 +28,7 @@
import spack.variant as vt
from spack.concretize import find_spec
from spack.spec import CompilerSpec, Spec
from spack.version import Version, ver
from spack.version import Version, VersionList, ver
def check_spec(abstract, concrete):
@ -2570,6 +2570,29 @@ def test_can_reuse_concrete_externals_for_dependents(self, mutable_config, tmp_p
sombrero = result.specs[0]
assert sombrero["externaltool"].dag_hash() == external_spec.dag_hash()
@pytest.mark.only_clingo("Original concretizer cannot reuse")
def test_cannot_reuse_host_incompatible_libc(self):
"""Test whether reuse concretization correctly fails to reuse a spec with a host
incompatible libc."""
if not spack.solver.asp.using_libc_compatibility():
pytest.skip("This test requires libc nodes")
# We install b@1 ^glibc@2.30, and b@0 ^glibc@2.28. The former is not host compatible, the
# latter is.
fst = Spec("b@1").concretized()
fst._mark_concrete(False)
fst.dependencies("glibc")[0].versions = VersionList(["=2.30"])
fst._mark_concrete(True)
snd = Spec("b@0").concretized()
# The spec b@1 ^glibc@2.30 is "more optimal" than b@0 ^glibc@2.28, but due to glibc
# incompatibility, it should not be reused.
solver = spack.solver.asp.Solver()
setup = spack.solver.asp.SpackSolverSetup()
result, _, _ = solver.driver.solve(setup, [Spec("b")], reuse=[fst, snd])
assert len(result.specs) == 1
assert result.specs[0] == snd
@pytest.fixture()
def duplicates_test_repository():