diff options
4 files changed, 58 insertions, 10 deletions
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index c7d4c1ae44..9bc0d42516 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -145,7 +145,7 @@ path(Parent, Descendant) :- path(Parent, A), depends_on(A, Descendant). % provider for that virtual then it depends on the provider depends_on(Package, Provider, Type) :- dependency_holds(Package, Virtual, Type), - provides_virtual(Provider, Virtual), + provider(Provider, Virtual), not external(Package). % dependencies on virtuals also imply that the virtual is a virtual node @@ -153,14 +153,24 @@ virtual_node(Virtual) :- dependency_holds(Package, Virtual, Type), virtual(Virtual), not external(Package). -% if there's a virtual node, we must select one provider -1 { provides_virtual(Package, Virtual) : possible_provider(Package, Virtual) } 1 +% If there's a virtual node, we must select one and only one provider. +% The provider must be selected among the possible providers. +1 { provider(Package, Virtual) : possible_provider(Package, Virtual) } 1 :- virtual_node(Virtual). % virtual roots imply virtual nodes, and that one provider is a root virtual_node(Virtual) :- virtual_root(Virtual). -1 { root(Package) : provides_virtual(Package, Virtual) } 1 - :- virtual_root(Virtual). + +% If we asked for a virtual root and we have a provider for that, +% then the provider is the root package. +root(Package) :- virtual_root(Virtual), provider(Package, Virtual). + +% If we asked for a root package and that root provides a virtual, +% the root is a provider for that virtual. This rule is mostly relevant +% for environments that are concretized together (e.g. where we +% asks to install "mpich" and "hdf5+mpi" and we want "mpich" to +% be the mpi provider) +provider(Package, Virtual) :- root(Package), provides_virtual(Package, Virtual). % The provider provides the virtual if some provider condition holds. provides_virtual(Provider, Virtual) :- @@ -168,12 +178,15 @@ provides_virtual(Provider, Virtual) :- condition_holds(ID), virtual(Virtual). -% a node that provides a virtual is a provider -provider(Package, Virtual) - :- node(Package), provides_virtual(Package, Virtual). +% A package cannot be the actual provider for a virtual if it does not +% fulfill the conditions to provide that virtual +:- provider(Package, Virtual), not provides_virtual(Package, Virtual). -% for any virtual, there can be at most one provider in the DAG -0 { node(Package) : provides_virtual(Package, Virtual) } 1 :- virtual(Virtual). +% If a package is selected as a provider, it is provider of all +% the virtuals it provides +:- provides_virtual(Package, V1), provides_virtual(Package, V2), V1 != V2, + provider(Package, V1), not provider(Package, V2), + virtual_node(V1), virtual_node(V2). #defined possible_provider/2. diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index c0f7b4c1f8..b9354ffe10 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -1253,3 +1253,11 @@ class TestConcretize(object): # criteria. s = spack.spec.Spec('root').concretized() assert s['gmt'].satisfies('@2.0') + + @pytest.mark.regression('24205') + def test_provider_must_meet_requirements(self): + # A package can be a provider of a virtual only if the underlying + # requirements are met. + s = spack.spec.Spec('unsat-virtual-dependency') + with pytest.raises((RuntimeError, spack.error.UnsatisfiableSpecError)): + s.concretize() diff --git a/var/spack/repos/builtin.mock/packages/unsat-provider/package.py b/var/spack/repos/builtin.mock/packages/unsat-provider/package.py new file mode 100644 index 0000000000..a801b2c547 --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/unsat-provider/package.py @@ -0,0 +1,15 @@ +# Copyright 2013-2021 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) +class UnsatProvider(Package): + """This package has a dependency on a virtual that cannot be provided""" + homepage = "http://www.example.com" + url = "http://www.example.com/v1.0.tgz" + + version('1.0', sha256='foobarbaz') + + variant('foo', default=True, description='') + + provides('unsatvdep', when='+foo') + conflicts('+foo') diff --git a/var/spack/repos/builtin.mock/packages/unsat-virtual-dependency/package.py b/var/spack/repos/builtin.mock/packages/unsat-virtual-dependency/package.py new file mode 100644 index 0000000000..0d3a688565 --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/unsat-virtual-dependency/package.py @@ -0,0 +1,12 @@ +# Copyright 2013-2021 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) +class UnsatVirtualDependency(Package): + """This package has a dependency on a virtual that cannot be provided""" + homepage = "http://www.example.com" + url = "http://www.example.com/v1.0.tgz" + + version('1.0', sha256='foobarbaz') + + depends_on('unsatvdep') |