Make Spec.compiler behavior stricter

Now the adaptor will raise if the Spec has no C, C++,
or Fortran compiler.
This commit is contained in:
Massimiliano Culpo 2024-11-17 14:23:20 +01:00
parent 54bca16130
commit 040b827dad
No known key found for this signature in database
GPG Key ID: 3E52BB992233066C
7 changed files with 25 additions and 35 deletions

View File

@ -72,7 +72,7 @@ def index_by(objects, *funcs):
if isinstance(f, str): if isinstance(f, str):
f = lambda x: getattr(x, funcs[0]) f = lambda x: getattr(x, funcs[0])
elif isinstance(f, tuple): elif isinstance(f, tuple):
f = lambda x: tuple(getattr(x, p) for p in funcs[0]) f = lambda x: tuple(getattr(x, p, None) for p in funcs[0])
result = {} result = {}
for o in objects: for o in objects:

View File

@ -375,8 +375,13 @@ def iter_groups(specs, indent, all_headers):
index = index_by(specs, ("architecture", "compiler")) index = index_by(specs, ("architecture", "compiler"))
ispace = indent * " " ispace = indent * " "
def _key(item):
if item is None:
return ""
return str(item)
# Traverse the index and print out each package # Traverse the index and print out each package
for i, (architecture, compiler) in enumerate(sorted(index)): for i, (architecture, compiler) in enumerate(sorted(index, key=_key)):
if i > 0: if i > 0:
print() print()
@ -434,7 +439,6 @@ def display_specs(specs, args=None, **kwargs):
""" """
# FIXME (compiler as nodes): remove the "show full compiler" arguments, and its use
def get_arg(name, default=None): def get_arg(name, default=None):
"""Prefer kwargs, then args, then default.""" """Prefer kwargs, then args, then default."""
if name in kwargs: if name in kwargs:
@ -449,7 +453,6 @@ def get_arg(name, default=None):
hashes = get_arg("long", False) hashes = get_arg("long", False)
namespaces = get_arg("namespaces", False) namespaces = get_arg("namespaces", False)
flags = get_arg("show_flags", False) flags = get_arg("show_flags", False)
full_compiler = get_arg("show_full_compiler", False)
variants = get_arg("variants", False) variants = get_arg("variants", False)
groups = get_arg("groups", True) groups = get_arg("groups", True)
all_headers = get_arg("all_headers", False) all_headers = get_arg("all_headers", False)
@ -471,10 +474,7 @@ def get_arg(name, default=None):
if format_string is None: if format_string is None:
nfmt = "{fullname}" if namespaces else "{name}" nfmt = "{fullname}" if namespaces else "{name}"
ffmt = "" ffmt = ""
if full_compiler or flags: if flags:
ffmt += "{%compiler.name}"
if full_compiler:
ffmt += "{@compiler.version}"
ffmt += " {compiler_flags}" ffmt += " {compiler_flags}"
vfmt = "{variants}" if variants else "" vfmt = "{variants}" if variants else ""
format_string = nfmt + "{@version}" + ffmt + vfmt format_string = nfmt + "{@version}" + ffmt + vfmt

View File

@ -518,8 +518,6 @@ def config_prefer_upstream(args):
for spec in pref_specs: for spec in pref_specs:
# Collect all the upstream compilers and versions for this package. # Collect all the upstream compilers and versions for this package.
pkg = pkgs.get(spec.name, {"version": []}) pkg = pkgs.get(spec.name, {"version": []})
all = pkgs.get("all", {"compiler": []})
pkgs["all"] = all
pkgs[spec.name] = pkg pkgs[spec.name] = pkg
# We have no existing variant if this is our first added version. # We have no existing variant if this is our first added version.
@ -529,10 +527,6 @@ def config_prefer_upstream(args):
if version not in pkg["version"]: if version not in pkg["version"]:
pkg["version"].append(version) pkg["version"].append(version)
compiler = str(spec.compiler)
if compiler not in all["compiler"]:
all["compiler"].append(compiler)
# Get and list all the variants that differ from the default. # Get and list all the variants that differ from the default.
variants = [] variants = []
for var_name, variant in spec.variants.items(): for var_name, variant in spec.variants.items():

View File

@ -98,7 +98,7 @@ def setup_parser(subparser):
"--show-full-compiler", "--show-full-compiler",
action="store_true", action="store_true",
dest="show_full_compiler", dest="show_full_compiler",
help="(DEPRECATED) show full compiler specs", help="(DEPRECATED) show full compiler specs. Currently it's a no-op",
) )
implicit_explicit = subparser.add_mutually_exclusive_group() implicit_explicit = subparser.add_mutually_exclusive_group()
implicit_explicit.add_argument( implicit_explicit.add_argument(
@ -278,7 +278,6 @@ def root_decorator(spec, string):
# these enforce details in the root specs to show what the user asked for # these enforce details in the root specs to show what the user asked for
namespaces=True, namespaces=True,
show_flags=True, show_flags=True,
show_full_compiler=True,
decorator=root_decorator, decorator=root_decorator,
variants=True, variants=True,
) )
@ -301,7 +300,6 @@ def root_decorator(spec, string):
decorator=lambda s, f: color.colorize("@*{%s}" % f), decorator=lambda s, f: color.colorize("@*{%s}" % f),
namespace=True, namespace=True,
show_flags=True, show_flags=True,
show_full_compiler=True,
variants=True, variants=True,
) )
print() print()

View File

@ -30,7 +30,6 @@
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.lang import classproperty, memoized from llnl.util.lang import classproperty, memoized
import spack.compilers.config
import spack.config import spack.config
import spack.dependency import spack.dependency
import spack.deptypes as dt import spack.deptypes as dt
@ -1813,17 +1812,14 @@ def _resource_stage(self, resource):
return resource_stage_folder return resource_stage_folder
def do_test(self, dirty=False, externals=False): def do_test(self, dirty=False, externals=False):
if self.test_requires_compiler: if self.test_requires_compiler and not any(
compilers = spack.compilers.config.compilers_for_spec( lang in self.spec for lang in ("c", "cxx", "fortran")
self.spec.compiler, arch_spec=self.spec.architecture ):
tty.error(
f"Skipping tests for package {self.spec}, since a compiler is required, "
f"but not available"
) )
if not compilers: return
tty.error(
"Skipping tests for package %s\n"
% self.spec.format("{name}-{version}-{hash:7}")
+ "Package test requires missing compiler %s" % self.spec.compiler
)
return
kwargs = { kwargs = {
"dirty": dirty, "dirty": dirty,

View File

@ -77,7 +77,6 @@ def __enter__(self):
"packages": [], "packages": [],
} }
spec_record["properties"].append(Property("architecture", input_spec.architecture)) spec_record["properties"].append(Property("architecture", input_spec.architecture))
spec_record["properties"].append(Property("compiler", input_spec.compiler))
self.init_spec_record(input_spec, spec_record) self.init_spec_record(input_spec, spec_record)
self.specs.append(spec_record) self.specs.append(spec_record)

View File

@ -630,7 +630,7 @@ def __contains__(self, string):
return string in str(self) or string in self.target return string in str(self) or string in self.target
def complete_with_defaults(self) -> None: def complete_with_defaults(self) -> None:
default_architecture = spack.spec.ArchSpec.default_arch() default_architecture = ArchSpec.default_arch()
if not self.platform: if not self.platform:
self.platform = default_architecture.platform self.platform = default_architecture.platform
@ -703,7 +703,8 @@ def factory(self, instance, owner):
deps = instance.dependencies(virtuals=language) deps = instance.dependencies(virtuals=language)
if deps: if deps:
return CompilerSpec(deps[0]) return CompilerSpec(deps[0])
return CompilerSpec(Spec())
raise AttributeError(f"{instance} has no C, C++, or Fortran compiler")
@lang.lazy_lexicographic_ordering @lang.lazy_lexicographic_ordering
@ -1435,13 +1436,13 @@ def tree(
class SpecAnnotations: class SpecAnnotations:
def __init__(self) -> None: def __init__(self) -> None:
self.original_spec_format = SPECFILE_FORMAT_VERSION self.original_spec_format = SPECFILE_FORMAT_VERSION
self.compiler_node_attribute: Optional["spack.spec.Spec"] = None self.compiler_node_attribute: Optional["Spec"] = None
def with_spec_format(self, spec_format: int) -> "SpecAnnotations": def with_spec_format(self, spec_format: int) -> "SpecAnnotations":
self.original_spec_format = spec_format self.original_spec_format = spec_format
return self return self
def with_compiler(self, compiler: "spack.spec.Spec") -> "SpecAnnotations": def with_compiler(self, compiler: "Spec") -> "SpecAnnotations":
self.compiler_node_attribute = compiler self.compiler_node_attribute = compiler
return self return self
@ -3009,10 +3010,9 @@ def validate_or_raise(self):
if spec.name and not spack.repo.PATH.is_virtual(spec.name): if spec.name and not spack.repo.PATH.is_virtual(spec.name):
spack.repo.PATH.get_pkg_class(spec.fullname) spack.repo.PATH.get_pkg_class(spec.fullname)
# validate compiler in addition to the package name. # FIXME: atm allow '%' on abstract specs only if they depend on C, C++, or Fortran
if spec.dependencies(deptype="build"): if spec.dependencies(deptype="build"):
pkg_cls = spack.repo.PATH.get_pkg_class(spec.fullname) pkg_cls = spack.repo.PATH.get_pkg_class(spec.fullname)
# FIXME (compiler as nodes): raise if we use %gcc on pkgs that do not depend on C
pkg_dependencies = pkg_cls.dependency_names() pkg_dependencies = pkg_cls.dependency_names()
if not any(x in pkg_dependencies for x in ("c", "cxx", "fortran")): if not any(x in pkg_dependencies for x in ("c", "cxx", "fortran")):
raise UnsupportedCompilerError( raise UnsupportedCompilerError(
@ -3975,6 +3975,9 @@ def format_attribute(match_object: Match) -> str:
try: try:
current = getattr(current, part) current = getattr(current, part)
except AttributeError: except AttributeError:
if part == "compiler":
return "none"
raise SpecFormatStringError( raise SpecFormatStringError(
f"Attempted to format attribute {attribute}. " f"Attempted to format attribute {attribute}. "
f"Spec {'.'.join(parts[:idx])} has no attribute {part}" f"Spec {'.'.join(parts[:idx])} has no attribute {part}"