diff options
author | Harmen Stoppels <me@harmenstoppels.nl> | 2024-07-11 15:19:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-11 15:19:55 +0200 |
commit | 278a38f4af4fcc45cf0e47a9986dd18b21a3bf70 (patch) | |
tree | e502a2aef4d006cdc4ec302ade4182b8cd72f759 /lib | |
parent | 39bbedf517e3fe570b723b3e79e999fc30487ce1 (diff) | |
download | spack-278a38f4af4fcc45cf0e47a9986dd18b21a3bf70.tar.gz spack-278a38f4af4fcc45cf0e47a9986dd18b21a3bf70.tar.bz2 spack-278a38f4af4fcc45cf0e47a9986dd18b21a3bf70.tar.xz spack-278a38f4af4fcc45cf0e47a9986dd18b21a3bf70.zip |
external find --not-buildable: mark virtuals (#45159)
This change makes `spack external find --not-buildable` mark virtuals
provided by detected packages as non-buildable, so that it's sufficient
for users to let spack detect say mpich and have the concretizer pick it
up as mpi provider even when openmpi is "more preferred".
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/cmd/external.py | 25 | ||||
-rw-r--r-- | lib/spack/spack/detection/__init__.py | 8 | ||||
-rw-r--r-- | lib/spack/spack/detection/common.py | 21 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/external.py | 27 |
4 files changed, 74 insertions, 7 deletions
diff --git a/lib/spack/spack/cmd/external.py b/lib/spack/spack/cmd/external.py index 8b6e750932..421685d42a 100644 --- a/lib/spack/spack/cmd/external.py +++ b/lib/spack/spack/cmd/external.py @@ -7,7 +7,7 @@ import errno import os import re import sys -from typing import List, Optional +from typing import List, Optional, Set import llnl.util.tty as tty import llnl.util.tty.colify as colify @@ -19,6 +19,7 @@ import spack.cray_manifest as cray_manifest import spack.detection import spack.error import spack.repo +import spack.spec import spack.util.environment from spack.cmd.common import arguments @@ -138,14 +139,26 @@ def external_find(args): candidate_packages, path_hints=args.path, max_workers=args.jobs ) - new_entries = spack.detection.update_configuration( + new_specs = spack.detection.update_configuration( detected_packages, scope=args.scope, buildable=not args.not_buildable ) - if new_entries: + + # If the user runs `spack external find --not-buildable mpich` we also mark `mpi` non-buildable + # to avoid that the concretizer picks a different mpi provider. + if new_specs and args.not_buildable: + virtuals: Set[str] = { + virtual.name + for new_spec in new_specs + for virtual_specs in spack.repo.PATH.get_pkg_class(new_spec.name).provided.values() + for virtual in virtual_specs + } + new_virtuals = spack.detection.set_virtuals_nonbuildable(virtuals, scope=args.scope) + new_specs.extend(spack.spec.Spec(name) for name in new_virtuals) + + if new_specs: path = spack.config.CONFIG.get_config_filename(args.scope, "packages") - msg = "The following specs have been detected on this system and added to {0}" - tty.msg(msg.format(path)) - spack.cmd.display_specs(new_entries) + tty.msg(f"The following specs have been detected on this system and added to {path}") + spack.cmd.display_specs(new_specs) else: tty.msg("No new external packages detected") diff --git a/lib/spack/spack/detection/__init__.py b/lib/spack/spack/detection/__init__.py index 44225ef062..1cff789c26 100644 --- a/lib/spack/spack/detection/__init__.py +++ b/lib/spack/spack/detection/__init__.py @@ -2,7 +2,12 @@ # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) -from .common import DetectedPackage, executable_prefix, update_configuration +from .common import ( + DetectedPackage, + executable_prefix, + set_virtuals_nonbuildable, + update_configuration, +) from .path import by_path, executables_in_path from .test import detection_tests @@ -12,5 +17,6 @@ __all__ = [ "executables_in_path", "executable_prefix", "update_configuration", + "set_virtuals_nonbuildable", "detection_tests", ] diff --git a/lib/spack/spack/detection/common.py b/lib/spack/spack/detection/common.py index 516bd11c8d..ae3c1927d2 100644 --- a/lib/spack/spack/detection/common.py +++ b/lib/spack/spack/detection/common.py @@ -252,6 +252,27 @@ def update_configuration( return all_new_specs +def set_virtuals_nonbuildable(virtuals: Set[str], scope: Optional[str] = None) -> List[str]: + """Update packages:virtual:buildable:False for the provided virtual packages, if the property + is not set by the user. Returns the list of virtual packages that have been updated.""" + packages = spack.config.get("packages") + new_config = {} + for virtual in virtuals: + # If the user has set the buildable prop do not override it + if virtual in packages and "buildable" in packages[virtual]: + continue + new_config[virtual] = {"buildable": False} + + # Update the provided scope + spack.config.set( + "packages", + spack.config.merge_yaml(spack.config.get("packages", scope=scope), new_config), + scope=scope, + ) + + return list(new_config.keys()) + + def _windows_drive() -> str: """Return Windows drive string extracted from the PROGRAMFILES environment variable, which is guaranteed to be defined for all logins. diff --git a/lib/spack/spack/test/cmd/external.py b/lib/spack/spack/test/cmd/external.py index e5bb29c6ea..2de6f18b50 100644 --- a/lib/spack/spack/test/cmd/external.py +++ b/lib/spack/spack/test/cmd/external.py @@ -11,6 +11,7 @@ import pytest from llnl.util.filesystem import getuid, touch import spack +import spack.cmd.external import spack.detection import spack.detection.path from spack.main import SpackCommand @@ -311,3 +312,29 @@ def test_failures_in_scanning_do_not_result_in_an_error( assert "cmake" in output assert "3.23.3" in output assert "3.19.1" not in output + + +def test_detect_virtuals(mock_executable, mutable_config, monkeypatch): + """Test whether external find --not-buildable sets virtuals as non-buildable (unless user + config sets them to buildable)""" + mpich = mock_executable("mpichversion", output="echo MPICH Version: 4.0.2") + prefix = os.path.dirname(mpich) + external("find", "--path", prefix, "--not-buildable", "mpich") + + # Check that mpich was correctly detected + mpich = mutable_config.get("packages:mpich") + assert mpich["buildable"] is False + assert Spec(mpich["externals"][0]["spec"]).satisfies("mpich@4.0.2") + + # Check that the virtual package mpi was marked as non-buildable + assert mutable_config.get("packages:mpi:buildable") is False + + # Delete the mpich entry, and set mpi explicitly to buildable + mutable_config.set("packages:mpich", {}) + mutable_config.set("packages:mpi:buildable", True) + + # Run the detection again + external("find", "--path", prefix, "--not-buildable", "mpich") + + # Check that the mpi:buildable entry was not overwritten + assert mutable_config.get("packages:mpi:buildable") is True |