Add a "sticky" property to variants (#28630)
* Add sticky variants
* Add unit tests for sticky variants
* Add documentation for sticky variants
* Revert "Revert 19736 because conflicts are avoided by clingo by default (#26721)"
This reverts commit 33ef7d57c1
.
* Add stickiness to "allow-unsupported-compiler"
This commit is contained in:
parent
dd7acecf3d
commit
cd04109e17
@ -1441,6 +1441,32 @@ The ``when`` clause follows the same syntax and accepts the same
|
|||||||
values as the ``when`` argument of
|
values as the ``when`` argument of
|
||||||
:py:func:`spack.directives.depends_on`
|
:py:func:`spack.directives.depends_on`
|
||||||
|
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
Sticky Variants
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The variant directive can be marked as ``sticky`` by setting to ``True`` the
|
||||||
|
corresponding argument:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
variant('bar', default=False, sticky=True)
|
||||||
|
|
||||||
|
A ``sticky`` variant differs from a regular one in that it is always set
|
||||||
|
to either:
|
||||||
|
|
||||||
|
#. An explicit value appearing in a spec literal or
|
||||||
|
#. Its default value
|
||||||
|
|
||||||
|
The concretizer thus is not free to pick an alternate value to work
|
||||||
|
around conflicts, but will error out instead.
|
||||||
|
Setting this property on a variant is useful in cases where the
|
||||||
|
variant allows some dangerous or controversial options (e.g. using unsupported versions
|
||||||
|
of a compiler for a library) and the packager wants to ensure that
|
||||||
|
allowing these options is done on purpose by the user, rather than
|
||||||
|
automatically by the solver.
|
||||||
|
|
||||||
|
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
Overriding Variants
|
Overriding Variants
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import spack.variant
|
import spack.variant
|
||||||
from spack.directives import conflicts, depends_on, variant
|
from spack.directives import conflicts, depends_on, variant
|
||||||
|
from spack.multimethod import when
|
||||||
from spack.package import PackageBase
|
from spack.package import PackageBase
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ def cuda_flags(arch_list):
|
|||||||
|
|
||||||
# Linux x86_64 compiler conflicts from here:
|
# Linux x86_64 compiler conflicts from here:
|
||||||
# https://gist.github.com/ax3l/9489132
|
# https://gist.github.com/ax3l/9489132
|
||||||
|
with when('^cuda~allow-unsupported-compilers'):
|
||||||
# GCC
|
# GCC
|
||||||
# According to
|
# According to
|
||||||
# https://github.com/spack/spack/pull/25054#issuecomment-886531664
|
# https://github.com/spack/spack/pull/25054#issuecomment-886531664
|
||||||
|
@ -562,7 +562,9 @@ def variant(
|
|||||||
values=None,
|
values=None,
|
||||||
multi=None,
|
multi=None,
|
||||||
validator=None,
|
validator=None,
|
||||||
when=None):
|
when=None,
|
||||||
|
sticky=False
|
||||||
|
):
|
||||||
"""Define a variant for the package. Packager can specify a default
|
"""Define a variant for the package. Packager can specify a default
|
||||||
value as well as a text description.
|
value as well as a text description.
|
||||||
|
|
||||||
@ -583,7 +585,8 @@ def variant(
|
|||||||
doesn't meet the additional constraints
|
doesn't meet the additional constraints
|
||||||
when (spack.spec.Spec, bool): optional condition on which the
|
when (spack.spec.Spec, bool): optional condition on which the
|
||||||
variant applies
|
variant applies
|
||||||
|
sticky (bool): the variant should not be changed by the concretizer to
|
||||||
|
find a valid concrete spec.
|
||||||
Raises:
|
Raises:
|
||||||
DirectiveError: if arguments passed to the directive are invalid
|
DirectiveError: if arguments passed to the directive are invalid
|
||||||
"""
|
"""
|
||||||
@ -657,7 +660,7 @@ def _execute_variant(pkg):
|
|||||||
when_specs += orig_when
|
when_specs += orig_when
|
||||||
|
|
||||||
pkg.variants[name] = (spack.variant.Variant(
|
pkg.variants[name] = (spack.variant.Variant(
|
||||||
name, default, description, values, multi, validator
|
name, default, description, values, multi, validator, sticky
|
||||||
), when_specs)
|
), when_specs)
|
||||||
return _execute_variant
|
return _execute_variant
|
||||||
|
|
||||||
|
@ -852,6 +852,9 @@ def pkg_rules(self, pkg, tests):
|
|||||||
for value in sorted(values):
|
for value in sorted(values):
|
||||||
self.gen.fact(fn.variant_possible_value(pkg.name, name, value))
|
self.gen.fact(fn.variant_possible_value(pkg.name, name, value))
|
||||||
|
|
||||||
|
if variant.sticky:
|
||||||
|
self.gen.fact(fn.variant_sticky(pkg.name, name))
|
||||||
|
|
||||||
self.gen.newline()
|
self.gen.newline()
|
||||||
|
|
||||||
# conflicts
|
# conflicts
|
||||||
|
@ -402,6 +402,14 @@ variant(Package, Variant) :- variant_condition(ID, Package, Variant),
|
|||||||
build(Package),
|
build(Package),
|
||||||
error("Unsatisfied conditional variants cannot take on a variant value").
|
error("Unsatisfied conditional variants cannot take on a variant value").
|
||||||
|
|
||||||
|
% if a variant is sticky and not set its value is the default value
|
||||||
|
variant_value(Package, Variant, Value) :-
|
||||||
|
variant(Package, Variant),
|
||||||
|
not variant_set(Package, Variant),
|
||||||
|
variant_sticky(Package, Variant),
|
||||||
|
variant_default_value(Package, Variant, Value),
|
||||||
|
build(Package).
|
||||||
|
|
||||||
% one variant value for single-valued variants.
|
% one variant value for single-valued variants.
|
||||||
1 {
|
1 {
|
||||||
variant_value(Package, Variant, Value)
|
variant_value(Package, Variant, Value)
|
||||||
@ -523,6 +531,7 @@ variant_single_value(Package, "dev_path")
|
|||||||
% spec or some package sets it, and without this, clingo will give
|
% spec or some package sets it, and without this, clingo will give
|
||||||
% warnings like 'info: atom does not occur in any rule head'.
|
% warnings like 'info: atom does not occur in any rule head'.
|
||||||
#defined variant/2.
|
#defined variant/2.
|
||||||
|
#defined variant_sticky/2.
|
||||||
#defined variant_set/3.
|
#defined variant_set/3.
|
||||||
#defined variant_condition/3.
|
#defined variant_condition/3.
|
||||||
#defined variant_single_value/2.
|
#defined variant_single_value/2.
|
||||||
|
@ -1357,3 +1357,21 @@ def test_concrete_specs_are_not_modified_on_reuse(
|
|||||||
s = spack.spec.Spec(spec_str).concretized(reuse=True)
|
s = spack.spec.Spec(spec_str).concretized(reuse=True)
|
||||||
assert s.package.installed is expect_installed
|
assert s.package.installed is expect_installed
|
||||||
assert s.satisfies(spec_str, strict=True)
|
assert s.satisfies(spec_str, strict=True)
|
||||||
|
|
||||||
|
@pytest.mark.regression('26721,19736')
|
||||||
|
def test_sticky_variant_in_package(self):
|
||||||
|
if spack.config.get('config:concretizer') == 'original':
|
||||||
|
pytest.skip('Original concretizer cannot use sticky variants')
|
||||||
|
|
||||||
|
# Here we test that a sticky variant cannot be changed from its default value
|
||||||
|
# by the ASP solver if not set explicitly. The package used in the test needs
|
||||||
|
# to have +allow-gcc set to be concretized with %gcc and clingo is not allowed
|
||||||
|
# to change the default ~allow-gcc
|
||||||
|
with pytest.raises(spack.error.SpackError):
|
||||||
|
spack.spec.Spec('sticky-variant %gcc').concretized()
|
||||||
|
|
||||||
|
s = spack.spec.Spec('sticky-variant+allow-gcc %gcc').concretized()
|
||||||
|
assert s.satisfies('%gcc') and s.satisfies('+allow-gcc')
|
||||||
|
|
||||||
|
s = spack.spec.Spec('sticky-variant %clang').concretized()
|
||||||
|
assert s.satisfies('%clang') and s.satisfies('~allow-gcc')
|
||||||
|
@ -416,3 +416,13 @@ def test_multivalued_variants_are_lower_priority_than_providers(self):
|
|||||||
):
|
):
|
||||||
s = Spec('somevirtual').concretized()
|
s = Spec('somevirtual').concretized()
|
||||||
assert s.name == 'some-virtual-preferred'
|
assert s.name == 'some-virtual-preferred'
|
||||||
|
|
||||||
|
@pytest.mark.regression('26721,19736')
|
||||||
|
def test_sticky_variant_accounts_for_packages_yaml(self):
|
||||||
|
with spack.config.override(
|
||||||
|
'packages:sticky-variant', {
|
||||||
|
'variants': '+allow-gcc'
|
||||||
|
}
|
||||||
|
):
|
||||||
|
s = Spec('sticky-variant %gcc').concretized()
|
||||||
|
assert s.satisfies('%gcc') and s.satisfies('+allow-gcc')
|
||||||
|
@ -37,7 +37,9 @@ def __init__(
|
|||||||
description,
|
description,
|
||||||
values=(True, False),
|
values=(True, False),
|
||||||
multi=False,
|
multi=False,
|
||||||
validator=None):
|
validator=None,
|
||||||
|
sticky=False
|
||||||
|
):
|
||||||
"""Initialize a package variant.
|
"""Initialize a package variant.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -51,6 +53,8 @@ def __init__(
|
|||||||
multi (bool): whether multiple CSV are allowed
|
multi (bool): whether multiple CSV are allowed
|
||||||
validator (callable): optional callable used to enforce
|
validator (callable): optional callable used to enforce
|
||||||
additional logic on the set of values being validated
|
additional logic on the set of values being validated
|
||||||
|
sticky (bool): if true the variant is set to the default value at
|
||||||
|
concretization time
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.default = default
|
self.default = default
|
||||||
@ -83,6 +87,7 @@ def isa_type(v):
|
|||||||
|
|
||||||
self.multi = multi
|
self.multi = multi
|
||||||
self.group_validator = validator
|
self.group_validator = validator
|
||||||
|
self.sticky = sticky
|
||||||
|
|
||||||
def validate_or_raise(self, vspec, pkg=None):
|
def validate_or_raise(self, vspec, pkg=None):
|
||||||
"""Validate a variant spec against this package variant. Raises an
|
"""Validate a variant spec against this package variant. Raises an
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# Copyright 2013-2022 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 import *
|
||||||
|
|
||||||
|
|
||||||
|
class StickyVariant(AutotoolsPackage):
|
||||||
|
"""Package with a sticky variant and a conflict"""
|
||||||
|
|
||||||
|
homepage = "http://www.example.com"
|
||||||
|
url = "http://www.example.com/a-1.0.tar.gz"
|
||||||
|
|
||||||
|
version('1.0', '0123456789abcdef0123456789abcdef')
|
||||||
|
|
||||||
|
variant('allow-gcc', description='', default=False, sticky=True)
|
||||||
|
|
||||||
|
conflicts('%gcc', when='~allow-gcc')
|
@ -144,6 +144,8 @@ class Cuda(Package):
|
|||||||
conflicts('arch=darwin-mojave-x86_64')
|
conflicts('arch=darwin-mojave-x86_64')
|
||||||
|
|
||||||
variant('dev', default=False, description='Enable development dependencies, i.e to use cuda-gdb')
|
variant('dev', default=False, description='Enable development dependencies, i.e to use cuda-gdb')
|
||||||
|
variant('allow-unsupported-compilers', default=False, sticky=True,
|
||||||
|
description='Allow unsupported host compiler and CUDA version combinations')
|
||||||
|
|
||||||
depends_on('libxml2', when='@10.1.243:')
|
depends_on('libxml2', when='@10.1.243:')
|
||||||
# cuda-gdb needed libncurses.so.5 before 11.4.0
|
# cuda-gdb needed libncurses.so.5 before 11.4.0
|
||||||
|
Loading…
Reference in New Issue
Block a user