summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/solver/asp.py8
-rw-r--r--lib/spack/spack/solver/concretize.lp29
-rw-r--r--lib/spack/spack/test/concretize.py52
3 files changed, 71 insertions, 18 deletions
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index 0cca744359..4514bd0e96 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -1922,7 +1922,7 @@ class SpackSolverSetup:
node_flag = fn.attr("node_flag_set")
node_flag_source = fn.attr("node_flag_source")
node_flag_propagate = fn.attr("node_flag_propagate")
- variant_propagate = fn.attr("variant_propagate")
+ variant_propagation_candidate = fn.attr("variant_propagation_candidate")
class Body:
node = fn.attr("node")
@@ -1936,7 +1936,7 @@ class SpackSolverSetup:
node_flag = fn.attr("node_flag")
node_flag_source = fn.attr("node_flag_source")
node_flag_propagate = fn.attr("node_flag_propagate")
- variant_propagate = fn.attr("variant_propagate")
+ variant_propagation_candidate = fn.attr("variant_propagation_candidate")
f = Body if body else Head
@@ -1985,7 +1985,9 @@ class SpackSolverSetup:
clauses.append(f.variant_value(spec.name, vname, value))
if variant.propagate:
- clauses.append(f.variant_propagate(spec.name, vname, value, spec.name))
+ clauses.append(
+ f.variant_propagation_candidate(spec.name, vname, value, spec.name)
+ )
# Tell the concretizer that this is a possible value for the
# variant, to account for things like int/str values where we
diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp
index 5e98e5cf11..d5f24ddc3b 100644
--- a/lib/spack/spack/solver/concretize.lp
+++ b/lib/spack/spack/solver/concretize.lp
@@ -757,23 +757,36 @@ node_has_variant(node(ID, Package), Variant) :-
pkg_fact(Package, variant(Variant)),
attr("node", node(ID, Package)).
-attr("variant_propagate", PackageNode, Variant, Value, Source) :-
+% Variant propagation is forwarded to dependencies
+attr("variant_propagation_candidate", PackageNode, Variant, Value, Source) :-
attr("node", PackageNode),
depends_on(ParentNode, PackageNode),
- attr("variant_propagate", ParentNode, Variant, Value, Source),
- not attr("variant_set", PackageNode, Variant).
+ attr("variant_value", node(_, Source), Variant, Value),
+ attr("variant_propagation_candidate", ParentNode, Variant, _, Source).
-attr("variant_value", node(ID, Package), Variant, Value) :-
- attr("node", node(ID, Package)),
+% If the node is a candidate, and it has the variant and value,
+% then those variant and value should be propagated
+attr("variant_propagate", node(ID, Package), Variant, Value, Source) :-
+ attr("variant_propagation_candidate", node(ID, Package), Variant, Value, Source),
node_has_variant(node(ID, Package), Variant),
- attr("variant_propagate", node(ID, Package), Variant, Value, _),
- pkg_fact(Package, variant_possible_value(Variant, Value)).
+ pkg_fact(Package, variant_possible_value(Variant, Value)),
+ not attr("variant_set", node(ID, Package), Variant).
+
+% Propagate the value, if there is the corresponding attribute
+attr("variant_value", PackageNode, Variant, Value) :- attr("variant_propagate", PackageNode, Variant, Value, _).
+
+% If a variant is propagated, we cannot have extraneous values (this is for multi valued variants)
+variant_is_propagated(PackageNode, Variant) :- attr("variant_propagate", PackageNode, Variant, _, _).
+:- variant_is_propagated(PackageNode, Variant),
+ attr("variant_value", PackageNode, Variant, Value),
+ not attr("variant_propagate", PackageNode, Variant, Value, _).
+% Cannot receive different values from different sources on the same variant
error(100, "{0} and {1} cannot both propagate variant '{2}' to package {3} with values '{4}' and '{5}'", Source1, Source2, Variant, Package, Value1, Value2) :-
attr("variant_propagate", node(X, Package), Variant, Value1, Source1),
attr("variant_propagate", node(X, Package), Variant, Value2, Source2),
node_has_variant(node(X, Package), Variant),
- Value1 < Value2.
+ Value1 < Value2, Source1 < Source2.
% a variant cannot be set if it is not a variant on the package
error(100, "Cannot set variant '{0}' for package '{1}' because the variant condition cannot be satisfied for the given spec", Variant, Package)
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
index 0af689ddd5..eba86d14fc 100644
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -349,6 +349,9 @@ class TestConcretize:
spec.concretize()
assert spec.satisfies("cflags=-O2")
+ @pytest.mark.only_clingo(
+ "Optional compiler propagation isn't deprecated for original concretizer"
+ )
def test_concretize_compiler_flag_propagate(self):
spec = Spec("hypre cflags=='-g' ^openblas")
spec.concretize()
@@ -458,19 +461,54 @@ class TestConcretize:
@pytest.mark.only_clingo(
"Optional compiler propagation isn't deprecated for original concretizer"
)
- def test_concretize_propagate_disabled_variant(self):
+ @pytest.mark.parametrize(
+ "spec_str,expected_propagation",
+ [
+ ("hypre~~shared ^openblas+shared", [("hypre", "~shared"), ("openblas", "+shared")]),
+ # Propagates past a node that doesn't have the variant
+ ("hypre~~shared ^openblas", [("hypre", "~shared"), ("openblas", "~shared")]),
+ (
+ "ascent~~shared +adios2",
+ [("ascent", "~shared"), ("adios2", "~shared"), ("bzip2", "~shared")],
+ ),
+ # Propagates below a node that uses the other value explicitly
+ (
+ "ascent~~shared +adios2 ^adios2+shared",
+ [("ascent", "~shared"), ("adios2", "+shared"), ("bzip2", "~shared")],
+ ),
+ (
+ "ascent++shared +adios2 ^adios2~shared",
+ [("ascent", "+shared"), ("adios2", "~shared"), ("bzip2", "+shared")],
+ ),
+ ],
+ )
+ def test_concretize_propagate_disabled_variant(self, spec_str, expected_propagation):
+ """Tests various patterns of boolean variant propagation"""
+ spec = Spec(spec_str).concretized()
+ for key, expected_satisfies in expected_propagation:
+ spec[key].satisfies(expected_satisfies)
+
+ @pytest.mark.only_clingo(
+ "Optional compiler propagation isn't deprecated for original concretizer"
+ )
+ def test_concretize_propagated_variant_is_not_passed_to_dependent(self):
"""Test a package variant value was passed from its parent."""
- spec = Spec("hypre~~shared ^openblas")
+ spec = Spec("ascent~~shared +adios2 ^adios2+shared")
spec.concretize()
- assert spec.satisfies("^openblas~shared")
+ assert spec.satisfies("^adios2+shared")
+ assert spec.satisfies("^bzip2~shared")
- def test_concretize_propagated_variant_is_not_passed_to_dependent(self):
- """Test a package variant value was passed from its parent."""
- spec = Spec("hypre~~shared ^openblas+shared")
+ @pytest.mark.only_clingo(
+ "Optional compiler propagation isn't deprecated for original concretizer"
+ )
+ def test_concretize_propagate_specified_variant(self):
+ """Test that only the specified variant is propagated to the dependencies"""
+ spec = Spec("parent-foo-bar ~~foo")
spec.concretize()
- assert spec.satisfies("^openblas+shared")
+ assert spec.satisfies("~foo") and spec.satisfies("^dependency-foo-bar~foo")
+ assert spec.satisfies("+bar") and not spec.satisfies("^dependency-foo-bar+bar")
@pytest.mark.only_clingo("Original concretizer is allowed to forego variant propagation")
def test_concretize_propagate_multivalue_variant(self):