Add type-hints to spack.bootstrap (#36491)
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							f5624f096c
						
					
				
				
					commit
					e1a104e3a2
				
			@@ -9,6 +9,7 @@
 | 
			
		||||
import sys
 | 
			
		||||
import sysconfig
 | 
			
		||||
import warnings
 | 
			
		||||
from typing import Dict, Optional, Sequence, Union
 | 
			
		||||
 | 
			
		||||
import archspec.cpu
 | 
			
		||||
 | 
			
		||||
@@ -21,8 +22,10 @@
 | 
			
		||||
 | 
			
		||||
from .config import spec_for_current_python
 | 
			
		||||
 | 
			
		||||
QueryInfo = Dict[str, "spack.spec.Spec"]
 | 
			
		||||
 | 
			
		||||
def _python_import(module):
 | 
			
		||||
 | 
			
		||||
def _python_import(module: str) -> bool:
 | 
			
		||||
    try:
 | 
			
		||||
        __import__(module)
 | 
			
		||||
    except ImportError:
 | 
			
		||||
@@ -30,7 +33,9 @@ def _python_import(module):
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _try_import_from_store(module, query_spec, query_info=None):
 | 
			
		||||
def _try_import_from_store(
 | 
			
		||||
    module: str, query_spec: Union[str, "spack.spec.Spec"], query_info: Optional[QueryInfo] = None
 | 
			
		||||
) -> bool:
 | 
			
		||||
    """Return True if the module can be imported from an already
 | 
			
		||||
    installed spec, False otherwise.
 | 
			
		||||
 | 
			
		||||
@@ -52,7 +57,7 @@ def _try_import_from_store(module, query_spec, query_info=None):
 | 
			
		||||
        module_paths = [
 | 
			
		||||
            os.path.join(candidate_spec.prefix, pkg.purelib),
 | 
			
		||||
            os.path.join(candidate_spec.prefix, pkg.platlib),
 | 
			
		||||
        ]  # type: list[str]
 | 
			
		||||
        ]
 | 
			
		||||
        path_before = list(sys.path)
 | 
			
		||||
 | 
			
		||||
        # NOTE: try module_paths first and last, last allows an existing version in path
 | 
			
		||||
@@ -89,7 +94,7 @@ def _try_import_from_store(module, query_spec, query_info=None):
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _fix_ext_suffix(candidate_spec):
 | 
			
		||||
def _fix_ext_suffix(candidate_spec: "spack.spec.Spec"):
 | 
			
		||||
    """Fix the external suffixes of Python extensions on the fly for
 | 
			
		||||
    platforms that may need it
 | 
			
		||||
 | 
			
		||||
@@ -157,7 +162,11 @@ def _fix_ext_suffix(candidate_spec):
 | 
			
		||||
        os.symlink(abs_path, link_name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _executables_in_store(executables, query_spec, query_info=None):
 | 
			
		||||
def _executables_in_store(
 | 
			
		||||
    executables: Sequence[str],
 | 
			
		||||
    query_spec: Union["spack.spec.Spec", str],
 | 
			
		||||
    query_info: Optional[QueryInfo] = None,
 | 
			
		||||
) -> bool:
 | 
			
		||||
    """Return True if at least one of the executables can be retrieved from
 | 
			
		||||
    a spec in store, False otherwise.
 | 
			
		||||
 | 
			
		||||
@@ -193,7 +202,7 @@ def _executables_in_store(executables, query_spec, query_info=None):
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _root_spec(spec_str):
 | 
			
		||||
def _root_spec(spec_str: str) -> str:
 | 
			
		||||
    """Add a proper compiler and target to a spec used during bootstrapping.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
import contextlib
 | 
			
		||||
import os.path
 | 
			
		||||
import sys
 | 
			
		||||
from typing import Any, Dict, Generator, MutableSequence, Sequence
 | 
			
		||||
 | 
			
		||||
from llnl.util import tty
 | 
			
		||||
 | 
			
		||||
@@ -24,12 +25,12 @@
 | 
			
		||||
_REF_COUNT = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_bootstrapping():
 | 
			
		||||
def is_bootstrapping() -> bool:
 | 
			
		||||
    """Return True if we are in a bootstrapping context, False otherwise."""
 | 
			
		||||
    return _REF_COUNT > 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def spec_for_current_python():
 | 
			
		||||
def spec_for_current_python() -> str:
 | 
			
		||||
    """For bootstrapping purposes we are just interested in the Python
 | 
			
		||||
    minor version (all patches are ABI compatible with the same minor).
 | 
			
		||||
 | 
			
		||||
@@ -41,14 +42,14 @@ def spec_for_current_python():
 | 
			
		||||
    return f"python@{version_str}"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def root_path():
 | 
			
		||||
def root_path() -> str:
 | 
			
		||||
    """Root of all the bootstrap related folders"""
 | 
			
		||||
    return spack.util.path.canonicalize_path(
 | 
			
		||||
        spack.config.get("bootstrap:root", spack.paths.default_user_bootstrap_path)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def store_path():
 | 
			
		||||
def store_path() -> str:
 | 
			
		||||
    """Path to the store used for bootstrapped software"""
 | 
			
		||||
    enabled = spack.config.get("bootstrap:enable", True)
 | 
			
		||||
    if not enabled:
 | 
			
		||||
@@ -59,7 +60,7 @@ def store_path():
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextlib.contextmanager
 | 
			
		||||
def spack_python_interpreter():
 | 
			
		||||
def spack_python_interpreter() -> Generator:
 | 
			
		||||
    """Override the current configuration to set the interpreter under
 | 
			
		||||
    which Spack is currently running as the only Python external spec
 | 
			
		||||
    available.
 | 
			
		||||
@@ -76,18 +77,18 @@ def spack_python_interpreter():
 | 
			
		||||
        yield
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _store_path():
 | 
			
		||||
def _store_path() -> str:
 | 
			
		||||
    bootstrap_root_path = root_path()
 | 
			
		||||
    return spack.util.path.canonicalize_path(os.path.join(bootstrap_root_path, "store"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _config_path():
 | 
			
		||||
def _config_path() -> str:
 | 
			
		||||
    bootstrap_root_path = root_path()
 | 
			
		||||
    return spack.util.path.canonicalize_path(os.path.join(bootstrap_root_path, "config"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextlib.contextmanager
 | 
			
		||||
def ensure_bootstrap_configuration():
 | 
			
		||||
def ensure_bootstrap_configuration() -> Generator:
 | 
			
		||||
    """Swap the current configuration for the one used to bootstrap Spack.
 | 
			
		||||
 | 
			
		||||
    The context manager is reference counted to ensure we don't swap multiple
 | 
			
		||||
@@ -107,7 +108,7 @@ def ensure_bootstrap_configuration():
 | 
			
		||||
        _REF_COUNT -= 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _read_and_sanitize_configuration():
 | 
			
		||||
def _read_and_sanitize_configuration() -> Dict[str, Any]:
 | 
			
		||||
    """Read the user configuration that needs to be reused for bootstrapping
 | 
			
		||||
    and remove the entries that should not be copied over.
 | 
			
		||||
    """
 | 
			
		||||
@@ -120,9 +121,11 @@ def _read_and_sanitize_configuration():
 | 
			
		||||
    return user_configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _bootstrap_config_scopes():
 | 
			
		||||
def _bootstrap_config_scopes() -> Sequence["spack.config.ConfigScope"]:
 | 
			
		||||
    tty.debug("[BOOTSTRAP CONFIG SCOPE] name=_builtin")
 | 
			
		||||
    config_scopes = [spack.config.InternalConfigScope("_builtin", spack.config.config_defaults)]
 | 
			
		||||
    config_scopes: MutableSequence["spack.config.ConfigScope"] = [
 | 
			
		||||
        spack.config.InternalConfigScope("_builtin", spack.config.config_defaults)
 | 
			
		||||
    ]
 | 
			
		||||
    configuration_paths = (spack.config.configuration_defaults_path, ("bootstrap", _config_path()))
 | 
			
		||||
    for name, path in configuration_paths:
 | 
			
		||||
        platform = spack.platforms.host().name
 | 
			
		||||
@@ -137,7 +140,7 @@ def _bootstrap_config_scopes():
 | 
			
		||||
    return config_scopes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _add_compilers_if_missing():
 | 
			
		||||
def _add_compilers_if_missing() -> None:
 | 
			
		||||
    arch = spack.spec.ArchSpec.frontend_arch()
 | 
			
		||||
    if not spack.compilers.compilers_for_arch(arch):
 | 
			
		||||
        new_compilers = spack.compilers.find_new_compilers()
 | 
			
		||||
@@ -146,7 +149,7 @@ def _add_compilers_if_missing():
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextlib.contextmanager
 | 
			
		||||
def _ensure_bootstrap_configuration():
 | 
			
		||||
def _ensure_bootstrap_configuration() -> Generator:
 | 
			
		||||
    bootstrap_store_path = store_path()
 | 
			
		||||
    user_configuration = _read_and_sanitize_configuration()
 | 
			
		||||
    with spack.environment.no_active_environment():
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@
 | 
			
		||||
import os.path
 | 
			
		||||
import sys
 | 
			
		||||
import uuid
 | 
			
		||||
from typing import Callable, List, Optional
 | 
			
		||||
from typing import Any, Callable, Dict, List, Optional, Tuple
 | 
			
		||||
 | 
			
		||||
from llnl.util import tty
 | 
			
		||||
from llnl.util.lang import GroupedExceptionHandler
 | 
			
		||||
@@ -66,6 +66,9 @@
 | 
			
		||||
_bootstrap_methods = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ConfigDictionary = Dict[str, Any]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bootstrapper(bootstrapper_type: str):
 | 
			
		||||
    """Decorator to register classes implementing bootstrapping
 | 
			
		||||
    methods.
 | 
			
		||||
@@ -86,7 +89,7 @@ class Bootstrapper:
 | 
			
		||||
 | 
			
		||||
    config_scope_name = ""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, conf):
 | 
			
		||||
    def __init__(self, conf: ConfigDictionary) -> None:
 | 
			
		||||
        self.conf = conf
 | 
			
		||||
        self.name = conf["name"]
 | 
			
		||||
        self.metadata_dir = spack.util.path.canonicalize_path(conf["metadata"])
 | 
			
		||||
@@ -100,7 +103,7 @@ def __init__(self, conf):
 | 
			
		||||
        self.url = url
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def mirror_scope(self):
 | 
			
		||||
    def mirror_scope(self) -> spack.config.InternalConfigScope:
 | 
			
		||||
        """Mirror scope to be pushed onto the bootstrapping configuration when using
 | 
			
		||||
        this bootstrapper.
 | 
			
		||||
        """
 | 
			
		||||
@@ -121,7 +124,7 @@ def try_import(self, module: str, abstract_spec_str: str) -> bool:
 | 
			
		||||
        """
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def try_search_path(self, executables: List[str], abstract_spec_str: str) -> bool:
 | 
			
		||||
    def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bool:
 | 
			
		||||
        """Try to search some executables in the prefix of specs satisfying the abstract
 | 
			
		||||
        spec passed as argument.
 | 
			
		||||
 | 
			
		||||
@@ -139,13 +142,15 @@ def try_search_path(self, executables: List[str], abstract_spec_str: str) -> boo
 | 
			
		||||
class BuildcacheBootstrapper(Bootstrapper):
 | 
			
		||||
    """Install the software needed during bootstrapping from a buildcache."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, conf):
 | 
			
		||||
    def __init__(self, conf) -> None:
 | 
			
		||||
        super().__init__(conf)
 | 
			
		||||
        self.last_search = None
 | 
			
		||||
        self.last_search: Optional[ConfigDictionary] = None
 | 
			
		||||
        self.config_scope_name = f"bootstrap_buildcache-{uuid.uuid4()}"
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _spec_and_platform(abstract_spec_str):
 | 
			
		||||
    def _spec_and_platform(
 | 
			
		||||
        abstract_spec_str: str,
 | 
			
		||||
    ) -> Tuple[spack.spec.Spec, spack.platforms.Platform]:
 | 
			
		||||
        """Return the spec object and platform we need to use when
 | 
			
		||||
        querying the buildcache.
 | 
			
		||||
 | 
			
		||||
@@ -158,7 +163,7 @@ def _spec_and_platform(abstract_spec_str):
 | 
			
		||||
        bincache_platform = spack.platforms.real_host()
 | 
			
		||||
        return abstract_spec, bincache_platform
 | 
			
		||||
 | 
			
		||||
    def _read_metadata(self, package_name):
 | 
			
		||||
    def _read_metadata(self, package_name: str) -> Any:
 | 
			
		||||
        """Return metadata about the given package."""
 | 
			
		||||
        json_filename = f"{package_name}.json"
 | 
			
		||||
        json_dir = self.metadata_dir
 | 
			
		||||
@@ -167,7 +172,13 @@ def _read_metadata(self, package_name):
 | 
			
		||||
            data = json.load(stream)
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    def _install_by_hash(self, pkg_hash, pkg_sha256, index, bincache_platform):
 | 
			
		||||
    def _install_by_hash(
 | 
			
		||||
        self,
 | 
			
		||||
        pkg_hash: str,
 | 
			
		||||
        pkg_sha256: str,
 | 
			
		||||
        index: List[spack.spec.Spec],
 | 
			
		||||
        bincache_platform: spack.platforms.Platform,
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        index_spec = next(x for x in index if x.dag_hash() == pkg_hash)
 | 
			
		||||
        # Reconstruct the compiler that we need to use for bootstrapping
 | 
			
		||||
        compiler_entry = {
 | 
			
		||||
@@ -192,7 +203,13 @@ def _install_by_hash(self, pkg_hash, pkg_sha256, index, bincache_platform):
 | 
			
		||||
                        match, allow_root=True, unsigned=True, force=True, sha256=pkg_sha256
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
    def _install_and_test(self, abstract_spec, bincache_platform, bincache_data, test_fn):
 | 
			
		||||
    def _install_and_test(
 | 
			
		||||
        self,
 | 
			
		||||
        abstract_spec: spack.spec.Spec,
 | 
			
		||||
        bincache_platform: spack.platforms.Platform,
 | 
			
		||||
        bincache_data,
 | 
			
		||||
        test_fn,
 | 
			
		||||
    ) -> bool:
 | 
			
		||||
        # Ensure we see only the buildcache being used to bootstrap
 | 
			
		||||
        with spack.config.override(self.mirror_scope):
 | 
			
		||||
            # This index is currently needed to get the compiler used to build some
 | 
			
		||||
@@ -217,13 +234,14 @@ def _install_and_test(self, abstract_spec, bincache_platform, bincache_data, tes
 | 
			
		||||
                for _, pkg_hash, pkg_sha256 in item["binaries"]:
 | 
			
		||||
                    self._install_by_hash(pkg_hash, pkg_sha256, index, bincache_platform)
 | 
			
		||||
 | 
			
		||||
                info = {}
 | 
			
		||||
                info: ConfigDictionary = {}
 | 
			
		||||
                if test_fn(query_spec=abstract_spec, query_info=info):
 | 
			
		||||
                    self.last_search = info
 | 
			
		||||
                    return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def try_import(self, module, abstract_spec_str):
 | 
			
		||||
    def try_import(self, module: str, abstract_spec_str: str) -> bool:
 | 
			
		||||
        info: ConfigDictionary
 | 
			
		||||
        test_fn, info = functools.partial(_try_import_from_store, module), {}
 | 
			
		||||
        if test_fn(query_spec=abstract_spec_str, query_info=info):
 | 
			
		||||
            return True
 | 
			
		||||
@@ -235,7 +253,8 @@ def try_import(self, module, abstract_spec_str):
 | 
			
		||||
        data = self._read_metadata(module)
 | 
			
		||||
        return self._install_and_test(abstract_spec, bincache_platform, data, test_fn)
 | 
			
		||||
 | 
			
		||||
    def try_search_path(self, executables, abstract_spec_str):
 | 
			
		||||
    def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bool:
 | 
			
		||||
        info: ConfigDictionary
 | 
			
		||||
        test_fn, info = functools.partial(_executables_in_store, executables), {}
 | 
			
		||||
        if test_fn(query_spec=abstract_spec_str, query_info=info):
 | 
			
		||||
            self.last_search = info
 | 
			
		||||
@@ -251,13 +270,13 @@ def try_search_path(self, executables, abstract_spec_str):
 | 
			
		||||
class SourceBootstrapper(Bootstrapper):
 | 
			
		||||
    """Install the software needed during bootstrapping from sources."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, conf):
 | 
			
		||||
    def __init__(self, conf) -> None:
 | 
			
		||||
        super().__init__(conf)
 | 
			
		||||
        self.last_search = None
 | 
			
		||||
        self.last_search: Optional[ConfigDictionary] = None
 | 
			
		||||
        self.config_scope_name = f"bootstrap_source-{uuid.uuid4()}"
 | 
			
		||||
 | 
			
		||||
    def try_import(self, module, abstract_spec_str):
 | 
			
		||||
        info = {}
 | 
			
		||||
    def try_import(self, module: str, abstract_spec_str: str) -> bool:
 | 
			
		||||
        info: ConfigDictionary = {}
 | 
			
		||||
        if _try_import_from_store(module, abstract_spec_str, query_info=info):
 | 
			
		||||
            self.last_search = info
 | 
			
		||||
            return True
 | 
			
		||||
@@ -293,8 +312,8 @@ def try_import(self, module, abstract_spec_str):
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def try_search_path(self, executables, abstract_spec_str):
 | 
			
		||||
        info = {}
 | 
			
		||||
    def try_search_path(self, executables: Tuple[str], abstract_spec_str: str) -> bool:
 | 
			
		||||
        info: ConfigDictionary = {}
 | 
			
		||||
        if _executables_in_store(executables, abstract_spec_str, query_info=info):
 | 
			
		||||
            self.last_search = info
 | 
			
		||||
            return True
 | 
			
		||||
@@ -323,13 +342,13 @@ def try_search_path(self, executables, abstract_spec_str):
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_bootstrapper(conf):
 | 
			
		||||
def create_bootstrapper(conf: ConfigDictionary):
 | 
			
		||||
    """Return a bootstrap object built according to the configuration argument"""
 | 
			
		||||
    btype = conf["type"]
 | 
			
		||||
    return _bootstrap_methods[btype](conf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def source_is_enabled_or_raise(conf):
 | 
			
		||||
def source_is_enabled_or_raise(conf: ConfigDictionary):
 | 
			
		||||
    """Raise ValueError if the source is not enabled for bootstrapping"""
 | 
			
		||||
    trusted, name = spack.config.get("bootstrap:trusted"), conf["name"]
 | 
			
		||||
    if not trusted.get(name, False):
 | 
			
		||||
@@ -454,7 +473,7 @@ def ensure_executables_in_path_or_raise(
 | 
			
		||||
    raise RuntimeError(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _add_externals_if_missing():
 | 
			
		||||
def _add_externals_if_missing() -> None:
 | 
			
		||||
    search_list = [
 | 
			
		||||
        # clingo
 | 
			
		||||
        spack.repo.path.get_pkg_class("cmake"),
 | 
			
		||||
@@ -468,41 +487,41 @@ def _add_externals_if_missing():
 | 
			
		||||
    spack.detection.update_configuration(detected_packages, scope="bootstrap")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clingo_root_spec():
 | 
			
		||||
def clingo_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap clingo"""
 | 
			
		||||
    return _root_spec("clingo-bootstrap@spack+python")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_clingo_importable_or_raise():
 | 
			
		||||
def ensure_clingo_importable_or_raise() -> None:
 | 
			
		||||
    """Ensure that the clingo module is available for import."""
 | 
			
		||||
    ensure_module_importable_or_raise(module="clingo", abstract_spec=clingo_root_spec())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gnupg_root_spec():
 | 
			
		||||
def gnupg_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap GnuPG"""
 | 
			
		||||
    return _root_spec("gnupg@2.3:")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_gpg_in_path_or_raise():
 | 
			
		||||
def ensure_gpg_in_path_or_raise() -> None:
 | 
			
		||||
    """Ensure gpg or gpg2 are in the PATH or raise."""
 | 
			
		||||
    return ensure_executables_in_path_or_raise(
 | 
			
		||||
        executables=["gpg2", "gpg"], abstract_spec=gnupg_root_spec()
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def patchelf_root_spec():
 | 
			
		||||
def patchelf_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap patchelf"""
 | 
			
		||||
    # 0.13.1 is the last version not to require C++17.
 | 
			
		||||
    return _root_spec("patchelf@0.13.1:")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def verify_patchelf(patchelf):
 | 
			
		||||
def verify_patchelf(patchelf: "spack.util.executable.Executable") -> bool:
 | 
			
		||||
    """Older patchelf versions can produce broken binaries, so we
 | 
			
		||||
    verify the version here.
 | 
			
		||||
 | 
			
		||||
    Arguments:
 | 
			
		||||
 | 
			
		||||
        patchelf (spack.util.executable.Executable): patchelf executable
 | 
			
		||||
        patchelf: patchelf executable
 | 
			
		||||
    """
 | 
			
		||||
    out = patchelf("--version", output=str, error=os.devnull, fail_on_error=False).strip()
 | 
			
		||||
    if patchelf.returncode != 0:
 | 
			
		||||
@@ -517,7 +536,7 @@ def verify_patchelf(patchelf):
 | 
			
		||||
    return version >= spack.version.Version("0.13.1")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_patchelf_in_path_or_raise():
 | 
			
		||||
def ensure_patchelf_in_path_or_raise() -> None:
 | 
			
		||||
    """Ensure patchelf is in the PATH or raise."""
 | 
			
		||||
    # The old concretizer is not smart and we're doing its job: if the latest patchelf
 | 
			
		||||
    # does not concretize because the compiler doesn't support C++17, we try to
 | 
			
		||||
@@ -534,7 +553,7 @@ def ensure_patchelf_in_path_or_raise():
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_core_dependencies():
 | 
			
		||||
def ensure_core_dependencies() -> None:
 | 
			
		||||
    """Ensure the presence of all the core dependencies."""
 | 
			
		||||
    if sys.platform.lower() == "linux":
 | 
			
		||||
        ensure_patchelf_in_path_or_raise()
 | 
			
		||||
@@ -543,7 +562,7 @@ def ensure_core_dependencies():
 | 
			
		||||
    ensure_clingo_importable_or_raise()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def all_core_root_specs():
 | 
			
		||||
def all_core_root_specs() -> List[str]:
 | 
			
		||||
    """Return a list of all the core root specs that may be used to bootstrap Spack"""
 | 
			
		||||
    return [clingo_root_spec(), gnupg_root_spec(), patchelf_root_spec()]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@
 | 
			
		||||
import pathlib
 | 
			
		||||
import sys
 | 
			
		||||
import warnings
 | 
			
		||||
from typing import List
 | 
			
		||||
 | 
			
		||||
import archspec.cpu
 | 
			
		||||
 | 
			
		||||
@@ -27,7 +28,7 @@ class BootstrapEnvironment(spack.environment.Environment):
 | 
			
		||||
    """Environment to install dependencies of Spack for a given interpreter and architecture"""
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def spack_dev_requirements(cls):
 | 
			
		||||
    def spack_dev_requirements(cls) -> List[str]:
 | 
			
		||||
        """Spack development requirements"""
 | 
			
		||||
        return [
 | 
			
		||||
            isort_root_spec(),
 | 
			
		||||
@@ -38,7 +39,7 @@ def spack_dev_requirements(cls):
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def environment_root(cls):
 | 
			
		||||
    def environment_root(cls) -> pathlib.Path:
 | 
			
		||||
        """Environment root directory"""
 | 
			
		||||
        bootstrap_root_path = root_path()
 | 
			
		||||
        python_part = spec_for_current_python().replace("@", "")
 | 
			
		||||
@@ -52,12 +53,12 @@ def environment_root(cls):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def view_root(cls):
 | 
			
		||||
    def view_root(cls) -> pathlib.Path:
 | 
			
		||||
        """Location of the view"""
 | 
			
		||||
        return cls.environment_root().joinpath("view")
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def pythonpaths(cls):
 | 
			
		||||
    def pythonpaths(cls) -> List[str]:
 | 
			
		||||
        """Paths to be added to sys.path or PYTHONPATH"""
 | 
			
		||||
        python_dir_part = f"python{'.'.join(str(x) for x in sys.version_info[:2])}"
 | 
			
		||||
        glob_expr = str(cls.view_root().joinpath("**", python_dir_part, "**"))
 | 
			
		||||
@@ -68,21 +69,21 @@ def pythonpaths(cls):
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def bin_dirs(cls):
 | 
			
		||||
    def bin_dirs(cls) -> List[pathlib.Path]:
 | 
			
		||||
        """Paths to be added to PATH"""
 | 
			
		||||
        return [cls.view_root().joinpath("bin")]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def spack_yaml(cls):
 | 
			
		||||
    def spack_yaml(cls) -> pathlib.Path:
 | 
			
		||||
        """Environment spack.yaml file"""
 | 
			
		||||
        return cls.environment_root().joinpath("spack.yaml")
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
    def __init__(self) -> None:
 | 
			
		||||
        if not self.spack_yaml().exists():
 | 
			
		||||
            self._write_spack_yaml_file()
 | 
			
		||||
        super().__init__(self.environment_root())
 | 
			
		||||
 | 
			
		||||
    def update_installations(self):
 | 
			
		||||
    def update_installations(self) -> None:
 | 
			
		||||
        """Update the installations of this environment.
 | 
			
		||||
 | 
			
		||||
        The update is done using a depfile on Linux and macOS, and using the ``install_all``
 | 
			
		||||
@@ -103,7 +104,7 @@ def update_installations(self):
 | 
			
		||||
                self._install_with_depfile()
 | 
			
		||||
            self.write(regenerate=True)
 | 
			
		||||
 | 
			
		||||
    def update_syspath_and_environ(self):
 | 
			
		||||
    def update_syspath_and_environ(self) -> None:
 | 
			
		||||
        """Update ``sys.path`` and the PATH, PYTHONPATH environment variables to point to
 | 
			
		||||
        the environment view.
 | 
			
		||||
        """
 | 
			
		||||
@@ -119,7 +120,7 @@ def update_syspath_and_environ(self):
 | 
			
		||||
            + [str(x) for x in self.pythonpaths()]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _install_with_depfile(self):
 | 
			
		||||
    def _install_with_depfile(self) -> None:
 | 
			
		||||
        spackcmd = spack.util.executable.which("spack")
 | 
			
		||||
        spackcmd(
 | 
			
		||||
            "-e",
 | 
			
		||||
@@ -141,7 +142,7 @@ def _install_with_depfile(self):
 | 
			
		||||
            **kwargs,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _write_spack_yaml_file(self):
 | 
			
		||||
    def _write_spack_yaml_file(self) -> None:
 | 
			
		||||
        tty.msg(
 | 
			
		||||
            "[BOOTSTRAPPING] Spack has missing dependencies, creating a bootstrapping environment"
 | 
			
		||||
        )
 | 
			
		||||
@@ -159,32 +160,32 @@ def _write_spack_yaml_file(self):
 | 
			
		||||
        self.spack_yaml().write_text(template.render(context), encoding="utf-8")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def isort_root_spec():
 | 
			
		||||
def isort_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap isort"""
 | 
			
		||||
    return _root_spec("py-isort@4.3.5:")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mypy_root_spec():
 | 
			
		||||
def mypy_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap mypy"""
 | 
			
		||||
    return _root_spec("py-mypy@0.900:")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def black_root_spec():
 | 
			
		||||
def black_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap black"""
 | 
			
		||||
    return _root_spec("py-black@:23.1.0")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def flake8_root_spec():
 | 
			
		||||
def flake8_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap flake8"""
 | 
			
		||||
    return _root_spec("py-flake8")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def pytest_root_spec():
 | 
			
		||||
def pytest_root_spec() -> str:
 | 
			
		||||
    """Return the root spec used to bootstrap flake8"""
 | 
			
		||||
    return _root_spec("py-pytest")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_environment_dependencies():
 | 
			
		||||
def ensure_environment_dependencies() -> None:
 | 
			
		||||
    """Ensure Spack dependencies from the bootstrap environment are installed and ready to use"""
 | 
			
		||||
    with BootstrapEnvironment() as env:
 | 
			
		||||
        env.update_installations()
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
"""Query the status of bootstrapping on this machine"""
 | 
			
		||||
import platform
 | 
			
		||||
from typing import List, Optional, Sequence, Tuple, Union
 | 
			
		||||
 | 
			
		||||
import spack.util.executable
 | 
			
		||||
 | 
			
		||||
@@ -19,8 +20,12 @@
 | 
			
		||||
    pytest_root_spec,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
ExecutablesType = Union[str, Sequence[str]]
 | 
			
		||||
RequiredResponseType = Tuple[bool, Optional[str]]
 | 
			
		||||
SpecLike = Union["spack.spec.Spec", str]
 | 
			
		||||
 | 
			
		||||
def _required_system_executable(exes, msg):
 | 
			
		||||
 | 
			
		||||
def _required_system_executable(exes: ExecutablesType, msg: str) -> RequiredResponseType:
 | 
			
		||||
    """Search for an executable is the system path only."""
 | 
			
		||||
    if isinstance(exes, str):
 | 
			
		||||
        exes = (exes,)
 | 
			
		||||
@@ -29,7 +34,9 @@ def _required_system_executable(exes, msg):
 | 
			
		||||
    return False, msg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _required_executable(exes, query_spec, msg):
 | 
			
		||||
def _required_executable(
 | 
			
		||||
    exes: ExecutablesType, query_spec: SpecLike, msg: str
 | 
			
		||||
) -> RequiredResponseType:
 | 
			
		||||
    """Search for an executable in the system path or in the bootstrap store."""
 | 
			
		||||
    if isinstance(exes, str):
 | 
			
		||||
        exes = (exes,)
 | 
			
		||||
@@ -38,7 +45,7 @@ def _required_executable(exes, query_spec, msg):
 | 
			
		||||
    return False, msg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _required_python_module(module, query_spec, msg):
 | 
			
		||||
def _required_python_module(module: str, query_spec: SpecLike, msg: str) -> RequiredResponseType:
 | 
			
		||||
    """Check if a Python module is available in the current interpreter or
 | 
			
		||||
    if it can be loaded from the bootstrap store
 | 
			
		||||
    """
 | 
			
		||||
@@ -47,7 +54,7 @@ def _required_python_module(module, query_spec, msg):
 | 
			
		||||
    return False, msg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _missing(name, purpose, system_only=True):
 | 
			
		||||
def _missing(name: str, purpose: str, system_only: bool = True) -> str:
 | 
			
		||||
    """Message to be printed if an executable is not found"""
 | 
			
		||||
    msg = '[{2}] MISSING "{0}": {1}'
 | 
			
		||||
    if not system_only:
 | 
			
		||||
@@ -55,7 +62,7 @@ def _missing(name, purpose, system_only=True):
 | 
			
		||||
    return msg.format(name, purpose, "@*y{{-}}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _core_requirements():
 | 
			
		||||
def _core_requirements() -> List[RequiredResponseType]:
 | 
			
		||||
    _core_system_exes = {
 | 
			
		||||
        "make": _missing("make", "required to build software from sources"),
 | 
			
		||||
        "patch": _missing("patch", "required to patch source code before building"),
 | 
			
		||||
@@ -80,7 +87,7 @@ def _core_requirements():
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _buildcache_requirements():
 | 
			
		||||
def _buildcache_requirements() -> List[RequiredResponseType]:
 | 
			
		||||
    _buildcache_exes = {
 | 
			
		||||
        "file": _missing("file", "required to analyze files for buildcaches"),
 | 
			
		||||
        ("gpg2", "gpg"): _missing("gpg2", "required to sign/verify buildcaches", False),
 | 
			
		||||
@@ -103,7 +110,7 @@ def _buildcache_requirements():
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _optional_requirements():
 | 
			
		||||
def _optional_requirements() -> List[RequiredResponseType]:
 | 
			
		||||
    _optional_exes = {
 | 
			
		||||
        "zstd": _missing("zstd", "required to compress/decompress code archives"),
 | 
			
		||||
        "svn": _missing("svn", "required to manage subversion repositories"),
 | 
			
		||||
@@ -114,7 +121,7 @@ def _optional_requirements():
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _development_requirements():
 | 
			
		||||
def _development_requirements() -> List[RequiredResponseType]:
 | 
			
		||||
    # Ensure we trigger environment modifications if we have an environment
 | 
			
		||||
    if BootstrapEnvironment.spack_yaml().exists():
 | 
			
		||||
        with BootstrapEnvironment() as env:
 | 
			
		||||
@@ -139,7 +146,7 @@ def _development_requirements():
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def status_message(section):
 | 
			
		||||
def status_message(section) -> Tuple[str, bool]:
 | 
			
		||||
    """Return a status message to be printed to screen that refers to the
 | 
			
		||||
    section passed as argument and a bool which is True if there are missing
 | 
			
		||||
    dependencies.
 | 
			
		||||
@@ -161,7 +168,7 @@ def status_message(section):
 | 
			
		||||
    with ensure_bootstrap_configuration():
 | 
			
		||||
        missing_software = False
 | 
			
		||||
        for found, err_msg in required_software():
 | 
			
		||||
            if not found:
 | 
			
		||||
            if not found and err_msg:
 | 
			
		||||
                missing_software = True
 | 
			
		||||
                msg += "\n  " + err_msg
 | 
			
		||||
        msg += "\n"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user