Source code for spack.externals_config
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Helpers to build an ExternalSpecsParser from Spack configuration."""
import itertools
from typing import TYPE_CHECKING, Any, Dict, Set
import spack.compilers.config
import spack.compilers.libraries
import spack.platforms
import spack.repo
import spack.spec
import spack.util.libc
from spack.externals import (
ExternalSpecsParser,
complete_architecture,
complete_variants_and_architecture,
extract_dicts_from_configuration,
)
if TYPE_CHECKING:
import spack.context
def _normalize_packages_yaml(packages_yaml: Dict[str, Any], *, repo: spack.repo.RepoPath) -> None:
for pkg_name in list(packages_yaml.keys()):
is_virtual = repo.is_virtual(pkg_name)
if pkg_name == "all" or not is_virtual:
continue
# Remove the virtual entry from the normalized configuration
data = packages_yaml.pop(pkg_name)
is_buildable = data.get("buildable", True)
if not is_buildable:
for provider in repo.providers_for(pkg_name):
entry = packages_yaml.setdefault(provider.name, {})
entry["buildable"] = False
externals = data.get("externals", [])
def keyfn(x):
return spack.spec.Spec(x["spec"]).name
for provider, specs in itertools.groupby(externals, key=keyfn):
entry = packages_yaml.setdefault(provider, {})
entry.setdefault("externals", []).extend(specs)
[docs]
def external_config_with_implicit_externals(
context: "spack.context.SpackContext",
) -> Dict[str, Any]:
"""Return packages.yaml augmented with implicit libc externals on Linux.
Normalizes the configuration so that virtual-package keys are replaced by
their concrete providers, then adds any libc specs detected from configured
compilers when running on a libc-compatibility platform.
Args:
context: resources to read the ``packages`` section, the package repositories and the
cached compiler output from.
"""
configuration, repo = context.config, context.repo
packages_yaml = configuration.deepcopy_as_builtin("packages", line_info=True)
_normalize_packages_yaml(packages_yaml, repo=repo)
# Add externals for libc from compilers on Linux
if not spack.platforms.using_libc_compatibility():
return packages_yaml
for libc in sorted(all_libcs(context)):
entry = {"spec": f"{libc}", "prefix": libc.external_path}
packages_yaml.setdefault(libc.name, {}).setdefault("externals", []).append(entry)
return packages_yaml
[docs]
def all_libcs(context: "spack.context.SpackContext") -> Set[spack.spec.Spec]:
"""Return a set of all libc specs targeted by any configured compiler. If none, fall back to
libc determined from the current Python process if dynamically linked.
"""
cache = spack.compilers.libraries.FileCompilerCache(context.misc_cache)
libcs = set()
for c in spack.compilers.config.all_compilers_from(context.config, repo=context.repo):
candidate = spack.compilers.libraries.CompilerPropertyDetector(
c, repo=context.repo, cache=cache
).default_libc()
if candidate is not None:
libcs.add(candidate)
if libcs:
return libcs
libc = spack.util.libc.libc_from_current_python_process()
return {libc} if libc else set()
[docs]
def create_external_parser(
packages_with_externals: Any, *, context: "spack.context.SpackContext"
) -> ExternalSpecsParser:
"""Get externals from a pre-processed packages.yaml (with implicit externals).
Args:
packages_with_externals: pre-processed packages.yaml configuration.
context: resources to read the completion mode and the package repositories from.
"""
external_dicts = extract_dicts_from_configuration(packages_with_externals)
completion_mode = context.config.get("concretizer:externals:completion")
if completion_mode == "default_variants":
complete_fn = complete_variants_and_architecture
elif completion_mode == "architecture_only":
complete_fn = complete_architecture
else:
raise ValueError(
f"Unknown value for concretizer:externals:completion: {completion_mode!r}"
)
return ExternalSpecsParser(external_dicts, complete_node=complete_fn, repo=context.repo)