diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 0083dbc070..094ebaf170 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -1939,6 +1939,11 @@ def _spec_clauses( for virtual in virtuals: clauses.append(fn.attr("virtual_on_incoming_edges", spec.name, virtual)) + # If the spec is external and concrete, we allow all the libcs on the system + if spec.external and spec.concrete and using_libc_compatibility(): + for libc in self.libcs: + clauses.append(fn.attr("compatible_libc", spec.name, libc.name, libc.version)) + # add all clauses from dependencies if transitive: # TODO: Eventually distinguish 2 deps on the same pkg (build and link) diff --git a/lib/spack/spack/solver/libc_compatibility.lp b/lib/spack/spack/solver/libc_compatibility.lp index 28c7c57fda..3e2c00e372 100644 --- a/lib/spack/spack/solver/libc_compatibility.lp +++ b/lib/spack/spack/solver/libc_compatibility.lp @@ -10,12 +10,13 @@ %============================================================================= % A package cannot be reused if the libc is not compatible with it -:- provider(node(X, LibcPackage), node(0, "libc")), - attr("version", node(X, LibcPackage), LibcVersion), - attr("hash", node(R, ReusedPackage), Hash), - % Libc packages can be reused without the "compatible_libc" attribute - ReusedPackage != LibcPackage, - not attr("compatible_libc", node(R, ReusedPackage), LibcPackage, LibcVersion). +error(100, "Cannot reuse {0} since we cannot determine libc compatibility", ReusedPackage) + :- provider(node(X, LibcPackage), node(0, "libc")), + attr("version", node(X, LibcPackage), LibcVersion), + attr("hash", node(R, ReusedPackage), Hash), + % Libc packages can be reused without the "compatible_libc" attribute + ReusedPackage != LibcPackage, + not attr("compatible_libc", node(R, ReusedPackage), LibcPackage, LibcVersion). % Check whether the DAG has any built package has_built_packages() :- build(X), not external(X). diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 797c865a4f..0b4ce5b42a 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -2546,6 +2546,30 @@ def test_include_specs_from_externals_and_libcs( assert result["deprecated-versions"].satisfies("@1.0.0") + @pytest.mark.regression("44085") + @pytest.mark.only_clingo("Use case not supported by the original concretizer") + def test_can_reuse_concrete_externals_for_dependents(self, mutable_config, tmp_path): + """Test that external specs that are in the DB can be reused. This means they are + preferred to concretizing another external from packages.yaml + """ + packages_yaml = { + "externaltool": {"externals": [{"spec": "externaltool@2.0", "prefix": "/fake/path"}]} + } + mutable_config.set("packages", packages_yaml) + # Concretize with gcc@9 to get a suboptimal spec, since we have gcc@10 available + external_spec = Spec("externaltool@2 %gcc@9").concretized() + assert external_spec.external + + root_specs = [Spec("sombrero")] + with spack.config.override("concretizer:reuse", True): + solver = spack.solver.asp.Solver() + setup = spack.solver.asp.SpackSolverSetup() + result, _, _ = solver.driver.solve(setup, root_specs, reuse=[external_spec]) + + assert len(result.specs) == 1 + sombrero = result.specs[0] + assert sombrero["externaltool"].dag_hash() == external_spec.dag_hash() + @pytest.fixture() def duplicates_test_repository(): diff --git a/var/spack/repos/builtin.mock/packages/sombrero/package.py b/var/spack/repos/builtin.mock/packages/sombrero/package.py new file mode 100644 index 0000000000..452274cb9c --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/sombrero/package.py @@ -0,0 +1,16 @@ +# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack.package import * + + +class Sombrero(Package): + """Simple package with a dependency on an external spec.""" + + homepage = "http://www.example.com" + url = "http://www.example.com/b-1.0.tar.gz" + + version("1.0", md5="0123456789abcdef0123456789abcdef") + depends_on("externaltool")