Overhaul the spack compiler command

This reverts commit 2c47dddbc1.

Now, `spack compiler` writes by default in packages.yaml. Entries
in old `compilers.yaml` are converted to external specs as a way to
support legacy configuration.

Since this operation is expensive, an environment variable can be
used to enforce the deprecation of `compiler.yaml`.

The --mixed-toolchain option has been deprecated, since it stops to
make sense once compiler are treated as nodes.
This commit is contained in:
Massimiliano Culpo 2024-11-11 12:42:41 +01:00
parent da02a4a606
commit 285926cb69
No known key found for this signature in database
GPG Key ID: 3E52BB992233066C
5 changed files with 337 additions and 726 deletions

View File

@ -4,6 +4,7 @@
import argparse import argparse
import sys import sys
import warnings
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.util.lang import index_by from llnl.util.lang import index_by
@ -34,13 +35,13 @@ def setup_parser(subparser):
"--mixed-toolchain", "--mixed-toolchain",
action="store_true", action="store_true",
default=sys.platform == "darwin", default=sys.platform == "darwin",
help="Allow mixed toolchains (for example: clang, clang++, gfortran)", help="(DEPRECATED) Allow mixed toolchains (for example: clang, clang++, gfortran)",
) )
mixed_toolchain_group.add_argument( mixed_toolchain_group.add_argument(
"--no-mixed-toolchain", "--no-mixed-toolchain",
action="store_false", action="store_false",
dest="mixed_toolchain", dest="mixed_toolchain",
help="Do not allow mixed toolchains (for example: clang, clang++, gfortran)", help="(DEPRECATED) Do not allow mixed toolchains (for example: clang, clang++, gfortran)",
) )
find_parser.add_argument("add_paths", nargs=argparse.REMAINDER) find_parser.add_argument("add_paths", nargs=argparse.REMAINDER)
find_parser.add_argument( find_parser.add_argument(
@ -79,19 +80,22 @@ def compiler_find(args):
"""Search either $PATH or a list of paths OR MODULES for compilers and """Search either $PATH or a list of paths OR MODULES for compilers and
add them to Spack's configuration. add them to Spack's configuration.
""" """
if args.mixed_toolchain:
warnings.warn(
"The '--mixed-toolchain' option has been deprecated in Spack v0.23, and currently "
"has no effect. The option will be removed in Spack v0.25"
)
paths = args.add_paths or None paths = args.add_paths or None
new_compilers = spack.compilers.find_compilers( new_compilers = spack.compilers.find_compilers(
path_hints=paths, path_hints=paths, scope=args.scope, max_workers=args.jobs
scope=args.scope,
mixed_toolchain=args.mixed_toolchain,
max_workers=args.jobs,
) )
if new_compilers: if new_compilers:
n = len(new_compilers) n = len(new_compilers)
s = "s" if n > 1 else "" s = "s" if n > 1 else ""
filename = spack.config.CONFIG.get_config_filename(args.scope, "compilers") filename = spack.config.CONFIG.get_config_filename(args.scope, "packages")
tty.msg(f"Added {n:d} new compiler{s} to {filename}") tty.msg(f"Added {n:d} new compiler{s} to {filename}")
compiler_strs = sorted(f"{c.spec.name}@{c.spec.version}" for c in new_compilers) compiler_strs = sorted(f"{spec.name}@{spec.versions}" for spec in new_compilers)
colify(reversed(compiler_strs), indent=4) colify(reversed(compiler_strs), indent=4)
else: else:
tty.msg("Found no new compilers") tty.msg("Found no new compilers")
@ -100,52 +104,69 @@ def compiler_find(args):
def compiler_remove(args): def compiler_remove(args):
compiler_spec = spack.spec.CompilerSpec(args.compiler_spec) remover = spack.compilers.CompilerRemover(spack.config.CONFIG)
candidate_compilers = spack.compilers.compilers_for_spec(compiler_spec, scope=args.scope) candidates = remover.mark_compilers(match=args.compiler_spec, scope=args.scope)
if not candidates:
tty.die(f"No compiler matches '{args.compiler_spec}'")
if not candidate_compilers: compiler_strs = reversed(sorted(f"{spec.name}@{spec.versions}" for spec in candidates))
tty.die("No compilers match spec %s" % compiler_spec)
if not args.all and len(candidate_compilers) > 1: if not args.all and len(candidates) > 1:
tty.error(f"Multiple compilers match spec {compiler_spec}. Choose one:") tty.error(f"multiple compilers match the spec '{args.compiler_spec}':")
colify(reversed(sorted([c.spec.display_str for c in candidate_compilers])), indent=4) print()
tty.msg("Or, use `spack compiler remove -a` to remove all of them.") colify(compiler_strs, indent=4)
print()
print(
"Either use a stricter spec to select only one, or use `spack compiler remove -a`"
" to remove all of them."
)
sys.exit(1) sys.exit(1)
for current_compiler in candidate_compilers: remover.flush()
spack.compilers.remove_compiler_from_config(current_compiler.spec, scope=args.scope) tty.msg("The following compilers have been removed:")
tty.msg(f"{current_compiler.spec.display_str} has been removed") print()
colify(compiler_strs, indent=4)
print()
def compiler_info(args): def compiler_info(args):
"""Print info about all compilers matching a spec.""" """Print info about all compilers matching a spec."""
cspec = spack.spec.CompilerSpec(args.compiler_spec) query = spack.spec.Spec(args.compiler_spec)
compilers = spack.compilers.compilers_for_spec(cspec, scope=args.scope) all_compilers = spack.compilers.all_compilers(scope=args.scope, init_config=False)
compilers = [x for x in all_compilers if x.satisfies(query)]
if not compilers: if not compilers:
tty.die("No compilers match spec %s" % cspec) tty.die(f"No compilers match spec {query.cformat()}")
else: else:
for c in compilers: for c in compilers:
print(c.spec.display_str + ":") print(f"{c.cformat()}:")
print("\tpaths:") print(f" prefix: {c.external_path}")
for cpath in ["cc", "cxx", "f77", "fc"]: extra_attributes = getattr(c, "extra_attributes", {})
print("\t\t%s = %s" % (cpath, getattr(c, cpath, None))) if "compilers" in extra_attributes:
if c.flags: print(" compilers:")
print("\tflags:") for language, exe in extra_attributes.get("compilers", {}).items():
for flag, flag_value in c.flags.items(): print(f" {language}: {exe}")
print("\t\t%s = %s" % (flag, flag_value)) if "flags" in extra_attributes:
if len(c.environment) != 0: print(" flags:")
if len(c.environment.get("set", {})) != 0: for flag, flag_value in extra_attributes["flags"].items():
print("\tenvironment:") print(f" {flag} = {flag_value}")
print("\t set:") # FIXME (compiler as nodes): recover this printing
for key, value in c.environment["set"].items(): # if "environment" in extra_attributes:
print("\t %s = %s" % (key, value)) # if len(c.environment.get("set", {})) != 0:
if c.extra_rpaths: # print("\tenvironment:")
print("\tExtra rpaths:") # print("\t set:")
for extra_rpath in c.extra_rpaths: # for key, value in c.environment["set"].items():
print("\t\t%s" % extra_rpath) # print("\t %s = %s" % (key, value))
print("\tmodules = %s" % c.modules) if "extra_rpaths" in extra_attributes:
print("\toperating system = %s" % c.operating_system) print(" extra rpaths:")
for extra_rpath in extra_attributes["extra_rpaths"]:
print(f" {extra_rpath}")
if getattr(c, "external_modules", []):
print(" modules: ")
for module in c.external_modules:
print(f" {module}")
print()
def compiler_list(args): def compiler_list(args):
@ -162,7 +183,7 @@ def compiler_list(args):
tty.msg(msg) tty.msg(msg)
return return
index = index_by(compilers, lambda c: (c.spec.name, c.operating_system, c.target)) index = index_by(compilers, spack.compilers.name_os_target)
tty.msg("Available compilers") tty.msg("Available compilers")
@ -181,10 +202,10 @@ def compiler_list(args):
name, os, target = key name, os, target = key
os_str = os os_str = os
if target: if target:
os_str += "-%s" % target os_str += f"-{target}"
cname = "%s{%s} %s" % (spack.spec.COMPILER_COLOR, name, os_str) cname = f"{spack.spec.COMPILER_COLOR}{{{name}}} {os_str}"
tty.hline(colorize(cname), char="-") tty.hline(colorize(cname), char="-")
colify(reversed(sorted(c.spec.display_str for c in compilers))) colify(reversed(sorted(c.format("{name}@{version}") for c in compilers)))
def compiler(parser, args): def compiler(parser, args):

File diff suppressed because it is too large Load Diff

View File

@ -718,6 +718,9 @@ def print_section(self, section: str, blame: bool = False, *, scope=None) -> Non
raise spack.error.ConfigError(f"cannot read '{section}' configuration") from e raise spack.error.ConfigError(f"cannot read '{section}' configuration") from e
ConfigurationType = Union[Configuration, lang.Singleton]
@contextlib.contextmanager @contextlib.contextmanager
def override( def override(
path_or_scope: Union[ConfigScope, str], value: Optional[Any] = None path_or_scope: Union[ConfigScope, str], value: Optional[Any] = None

View File

@ -222,13 +222,13 @@ def read(path, apply_updates):
compilers = list() compilers = list()
if "compilers" in json_data: if "compilers" in json_data:
compilers.extend(compiler_from_entry(x, path) for x in json_data["compilers"]) compilers.extend(compiler_from_entry(x, path) for x in json_data["compilers"])
tty.debug("{0}: {1} compilers read from manifest".format(path, str(len(compilers)))) tty.debug(f"{path}: {str(len(compilers))} compilers read from manifest")
# Filter out the compilers that already appear in the configuration # Filter out the compilers that already appear in the configuration
compilers = spack.compilers.select_new_compilers(compilers) compilers = spack.compilers.select_new_compilers(compilers)
if apply_updates and compilers: if apply_updates and compilers:
for compiler in compilers: for compiler in compilers:
try: try:
spack.compilers.add_compilers_to_config([compiler]) spack.compilers.add_compiler_to_config(compiler)
except Exception: except Exception:
warnings.warn( warnings.warn(
f"Could not add compiler {str(compiler.spec)}: " f"Could not add compiler {str(compiler.spec)}: "

View File

@ -1375,8 +1375,7 @@ def compiler(self):
"""Get the spack.compiler.Compiler object used to build this package""" """Get the spack.compiler.Compiler object used to build this package"""
if not self.spec.concrete: if not self.spec.concrete:
raise ValueError("Can only get a compiler for a concrete package.") raise ValueError("Can only get a compiler for a concrete package.")
raise NotImplementedError("Wrapper to old API still to be implemented")
return spack.compilers.compiler_for_spec(self.spec.compiler, self.spec.architecture)
def url_version(self, version): def url_version(self, version):
""" """