Skip to content

Commit 3e99a43

Browse files
authored
solver: fix rule for virtuals that are provided together (spack#52021)
* solver: fix rule for virtuals that are provided together fixes spack#51512 Sometimes a unified environment has roots compiled with different compilers. In those cases the rule to ensure that virtuals that need to be provided together ARE provided together was wrong. In this PR we fix it, and we add a test case to avoid regression. Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
1 parent 8841665 commit 3e99a43

File tree

2 files changed

+62
-14
lines changed

2 files changed

+62
-14
lines changed

lib/spack/spack/solver/concretize.lp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -980,20 +980,19 @@ error(1, Msg) :-
980980
% Virtual dependencies
981981
%-----------------------------------------------------------------------------
982982

983-
% Enforces all virtuals to be provided, if multiple of them are provided together
984-
error(100, "Package '{0}' needs to provide both '{1}' and '{2}' together, but provides only '{1}'", Package, Virtual1, Virtual2)
985-
:- % This package provides 2 or more virtuals together
986-
condition_holds(ID, node(X, Package)),
987-
pkg_fact(Package, provided_together(ID, SetID, Virtual1)),
988-
pkg_fact(Package, provided_together(ID, SetID, Virtual2)),
989-
Virtual1 != Virtual2,
990-
% One node depends on those virtuals AND on this package
991-
node_depends_on_virtual(ClientNode, Virtual1),
992-
node_depends_on_virtual(ClientNode, Virtual2),
993-
depends_on(ClientNode, node(X, Package)),
994-
% But this package is a provider of only one of them
995-
provider(node(X, Package), node(_, Virtual1)),
996-
not provider(node(X, Package), node(_, Virtual2)).
983+
% Package provides to this client at least one virtual from those that need to be provided together
984+
node_uses_provider_with_constraints(ClientNode, node(X, Package), ID, SetID) :-
985+
condition_holds(ID, node(X, Package)),
986+
pkg_fact(Package, provided_together(ID, SetID, V)),
987+
attr("virtual_on_edge", ClientNode, node(X, Package), V).
988+
989+
% This error is triggered if the package provides some but not all required virtuals from
990+
% the set that needs to be provided together
991+
error(100, "Package '{0}' needs to also provide '{1}' (provided_together constraint)", Package, Virtual)
992+
:- node_uses_provider_with_constraints(ClientNode, node(X, Package), ID, SetID),
993+
pkg_fact(Package, provided_together(ID, SetID, Virtual)),
994+
node_depends_on_virtual(ClientNode, Virtual),
995+
not attr("virtual_on_edge", ClientNode, node(X, Package), Virtual).
997996

998997
% if a package depends on a virtual, it's not external and we have a
999998
% provider for that virtual then it depends on the provider

lib/spack/spack/test/env.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,3 +2038,52 @@ def test_mixed_compilers_and_libllvm(tmp_path, config):
20382038
assert paraview.satisfies(f"%{mesa}")
20392039
assert mesa.satisfies("%cxx=gcc %libllvm=llvm")
20402040
assert paraview["cxx"].dag_hash() == mesa["libllvm"].dag_hash()
2041+
2042+
2043+
@pytest.mark.regression("51512")
2044+
def test_unified_environment_with_mixed_compilers_and_fortran(tmp_path, config):
2045+
"""Tests that we can concretize a unified environment using two C/C++ compilers for the root
2046+
specs and GCC for Fortran, where both roots depend on Fortran.
2047+
"""
2048+
spack_yaml = """
2049+
spack:
2050+
specs:
2051+
- mpich %c,cxx=llvm
2052+
- openblas %c,fortran=gcc
2053+
packages:
2054+
gcc::
2055+
externals:
2056+
- spec: gcc@13.2.0 languages:='c,c++,fortran'
2057+
prefix: /path
2058+
extra_attributes:
2059+
compilers:
2060+
c: /path/bin/gcc
2061+
cxx: /path/bin/g++
2062+
fortran: /path/bin/gfortran
2063+
llvm::
2064+
externals:
2065+
- spec: llvm@20.1.8+clang~flang
2066+
prefix: /usr
2067+
extra_attributes:
2068+
compilers:
2069+
c: /usr/bin/gcc
2070+
cxx: /usr/bin/g++
2071+
fortran: /usr/bin/gfortran
2072+
concretizer:
2073+
unify: true
2074+
"""
2075+
manifest = tmp_path / "spack.yaml"
2076+
manifest.write_text(spack_yaml)
2077+
with ev.Environment(tmp_path) as e:
2078+
e.concretize()
2079+
2080+
for x in e.concrete_roots():
2081+
if x.name == "mpich":
2082+
mpich = x
2083+
else:
2084+
openblas = x
2085+
2086+
assert mpich.satisfies("%c,cxx=llvm")
2087+
assert mpich.satisfies("%fortran=gcc")
2088+
assert openblas.satisfies("%c,fortran=gcc")
2089+
assert mpich["fortran"].dag_hash() == openblas["fortran"].dag_hash()

0 commit comments

Comments
 (0)