From 39ace5fc45f67a870fef352ca5592892c54cf114 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Thu, 30 May 2024 07:31:28 +0200 Subject: 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 --- lib/spack/spack/solver/asp.py | 2 +- lib/spack/spack/solver/libc_compatibility.lp | 12 ++++++++++-- lib/spack/spack/test/concretize.py | 25 ++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 094ebaf170..49b5c626ce 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -2440,7 +2440,7 @@ class SpackSolverSetup: 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()) diff --git a/lib/spack/spack/solver/libc_compatibility.lp b/lib/spack/spack/solver/libc_compatibility.lp index 3e2c00e372..6c32493656 100644 --- a/lib/spack/spack/solver/libc_compatibility.lp +++ b/lib/spack/spack/solver/libc_compatibility.lp @@ -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), diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 0b4ce5b42a..b1cd35bff9 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -28,7 +28,7 @@ import spack.util.libc 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 @@ class TestConcretize: 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(): -- cgit v1.2.3-70-g09d2