Allow default requirements in packages.yaml (#32260)

Allow users to express default requirements in packages.yaml. 

These requirements are overridden if more specific requirements
are present for a given package.
This commit is contained in:
Massimiliano Culpo 2022-08-24 09:33:55 +02:00 committed by GitHub
parent b4df535e8d
commit e2468c8928
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 3 deletions

View file

@ -396,10 +396,34 @@ choose between a set of options using ``any_of`` or ``one_of``:
``mpich`` already includes a conflict, so this is redundant but
still demonstrates the concept).
You can also set default requirements for all packages under ``all``
like this:
.. code-block:: yaml
packages:
all:
require: '%clang'
which means every spec will be required to use ``clang`` as a compiler.
Note that in this case ``all`` represents a *default set of requirements* -
if there are specific package requirements, then the default requirements
under ``all`` are disregarded. For example, with a configuration like this:
.. code-block:: yaml
packages:
all:
require: '%clang'
cmake:
require: '%gcc'
Spack requires ``cmake`` to use ``gcc`` and all other nodes (including cmake dependencies)
to use ``clang``.
Other notes about ``requires``:
* You can only specify requirements for specific packages: you cannot
add ``requires`` under ``all``.
* You cannot specify requirements for virtual packages (e.g. you can
specify requirements for ``openmpi`` but not ``mpi``).
* For ``any_of`` and ``one_of``, the order of specs indicates a

View file

@ -942,7 +942,9 @@ def package_compiler_defaults(self, pkg):
def package_requirement_rules(self, pkg):
pkg_name = pkg.name
config = spack.config.get("packages")
requirements = config.get(pkg_name, {}).get("require", [])
requirements = config.get(pkg_name, {}).get("require", []) or config.get("all", {}).get(
"require", []
)
if isinstance(requirements, string_types):
rules = [(pkg_name, "one_of", [requirements])]
else:

View file

@ -297,3 +297,55 @@ def test_requirements_are_higher_priority_than_deprecation(concretize_scope, tes
s1 = Spec("y").concretized()
assert s1.satisfies("@2.3")
assert s1.satisfies("%gcc")
@pytest.mark.parametrize("spec_str,requirement_str", [("x", "%gcc"), ("x", "%clang")])
def test_default_requirements_with_all(spec_str, requirement_str, concretize_scope, test_repo):
"""Test that default requirements are applied to all packages."""
if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements")
conf_str = """\
packages:
all:
require: "{}"
""".format(
requirement_str
)
update_packages_config(conf_str)
spec = Spec(spec_str).concretized()
for s in spec.traverse():
assert s.satisfies(requirement_str)
@pytest.mark.parametrize(
"requirements,expectations",
[
(("%gcc", "%clang"), ("%gcc", "%clang")),
(("%gcc ~shared", "@1.0"), ("%gcc ~shared", "@1.0 +shared")),
],
)
def test_default_and_package_specific_requirements(
concretize_scope, requirements, expectations, test_repo
):
"""Test that specific package requirements override default package requirements."""
if spack.config.get("config:concretizer") == "original":
pytest.skip("Original concretizer does not support configuration" " requirements")
generic_req, specific_req = requirements
generic_exp, specific_exp = expectations
conf_str = """\
packages:
all:
require: "{}"
x:
require: "{}"
""".format(
generic_req, specific_req
)
update_packages_config(conf_str)
spec = Spec("x").concretized()
assert spec.satisfies(specific_exp)
for s in spec.traverse(root=False):
assert s.satisfies(generic_exp)