Remove spack.compilers Python modules

Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
This commit is contained in:
Massimiliano Culpo 2024-09-24 18:12:42 +02:00
parent f59c120e0a
commit 5b7caba4a6
No known key found for this signature in database
GPG Key ID: 3E52BB992233066C
18 changed files with 0 additions and 3581 deletions

View File

@ -1,856 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import contextlib
import hashlib
import itertools
import json
import os
import platform
import re
import shutil
import sys
import tempfile
from typing import Dict, List, Optional, Sequence
import llnl.path
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.filesystem import path_contains_subdirectory, paths_containing_libs
import spack.caches
import spack.error
import spack.schema.environment
import spack.spec
import spack.util.executable
import spack.util.libc
import spack.util.module_cmd
import spack.version
from spack.util.environment import filter_system_paths
from spack.util.file_cache import FileCache
__all__ = ["Compiler"]
PATH_INSTANCE_VARS = ["cc", "cxx", "f77", "fc"]
FLAG_INSTANCE_VARS = ["cflags", "cppflags", "cxxflags", "fflags"]
@llnl.util.lang.memoized
def _get_compiler_version_output(compiler_path, version_arg, ignore_errors=()) -> str:
"""Invokes the compiler at a given path passing a single
version argument and returns the output.
Args:
compiler_path (path): path of the compiler to be invoked
version_arg (str): the argument used to extract version information
"""
compiler = spack.util.executable.Executable(compiler_path)
compiler_invocation_args = {
"output": str,
"error": str,
"ignore_errors": ignore_errors,
"timeout": 120,
"fail_on_error": True,
}
if version_arg:
output = compiler(version_arg, **compiler_invocation_args)
else:
output = compiler(**compiler_invocation_args)
return output
def get_compiler_version_output(compiler_path, *args, **kwargs) -> str:
"""Wrapper for _get_compiler_version_output()."""
# This ensures that we memoize compiler output by *absolute path*,
# not just executable name. If we don't do this, and the path changes
# (e.g., during testing), we can get incorrect results.
if not os.path.isabs(compiler_path):
compiler_path = spack.util.executable.which_string(compiler_path, required=True)
return _get_compiler_version_output(compiler_path, *args, **kwargs)
def tokenize_flags(flags_values, propagate=False):
"""Given a compiler flag specification as a string, this returns a list
where the entries are the flags. For compiler options which set values
using the syntax "-flag value", this function groups flags and their
values together. Any token not preceded by a "-" is considered the
value of a prior flag."""
tokens = flags_values.split()
if not tokens:
return []
flag = tokens[0]
flags_with_propagation = []
for token in tokens[1:]:
if not token.startswith("-"):
flag += " " + token
else:
flags_with_propagation.append((flag, propagate))
flag = token
flags_with_propagation.append((flag, propagate))
return flags_with_propagation
#: regex for parsing linker lines
_LINKER_LINE = re.compile(r"^( *|.*[/\\])" r"(link|ld|([^/\\]+-)?ld|collect2)" r"[^/\\]*( |$)")
#: components of linker lines to ignore
_LINKER_LINE_IGNORE = re.compile(r"(collect2 version|^[A-Za-z0-9_]+=|/ldfe )")
#: regex to match linker search paths
_LINK_DIR_ARG = re.compile(r"^-L(.:)?(?P<dir>[/\\].*)")
#: regex to match linker library path arguments
_LIBPATH_ARG = re.compile(r"^[-/](LIBPATH|libpath):(?P<dir>.*)")
def _parse_link_paths(string):
"""Parse implicit link paths from compiler debug output.
This gives the compiler runtime library paths that we need to add to
the RPATH of generated binaries and libraries. It allows us to
ensure, e.g., that codes load the right libstdc++ for their compiler.
"""
lib_search_paths = False
raw_link_dirs = []
for line in string.splitlines():
if lib_search_paths:
if line.startswith("\t"):
raw_link_dirs.append(line[1:])
continue
else:
lib_search_paths = False
elif line.startswith("Library search paths:"):
lib_search_paths = True
if not _LINKER_LINE.match(line):
continue
if _LINKER_LINE_IGNORE.match(line):
continue
tty.debug(f"implicit link dirs: link line: {line}")
next_arg = False
for arg in line.split():
if arg in ("-L", "-Y"):
next_arg = True
continue
if next_arg:
raw_link_dirs.append(arg)
next_arg = False
continue
link_dir_arg = _LINK_DIR_ARG.match(arg)
if link_dir_arg:
link_dir = link_dir_arg.group("dir")
raw_link_dirs.append(link_dir)
link_dir_arg = _LIBPATH_ARG.match(arg)
if link_dir_arg:
link_dir = link_dir_arg.group("dir")
raw_link_dirs.append(link_dir)
implicit_link_dirs = list()
visited = set()
for link_dir in raw_link_dirs:
normalized_path = os.path.abspath(link_dir)
if normalized_path not in visited:
implicit_link_dirs.append(normalized_path)
visited.add(normalized_path)
tty.debug(f"implicit link dirs: result: {', '.join(implicit_link_dirs)}")
return implicit_link_dirs
@llnl.path.system_path_filter
def _parse_non_system_link_dirs(string: str) -> List[str]:
"""Parses link paths out of compiler debug output.
Args:
string: compiler debug output as a string
Returns:
Implicit link paths parsed from the compiler output
"""
link_dirs = _parse_link_paths(string)
# Remove directories that do not exist. Some versions of the Cray compiler
# report nonexistent directories
link_dirs = [d for d in link_dirs if os.path.isdir(d)]
# Return set of directories containing needed compiler libs, minus
# system paths. Note that 'filter_system_paths' only checks for an
# exact match, while 'in_system_subdirectory' checks if a path contains
# a system directory as a subdirectory
link_dirs = filter_system_paths(link_dirs)
return list(p for p in link_dirs if not in_system_subdirectory(p))
def in_system_subdirectory(path):
system_dirs = [
"/lib/",
"/lib64/",
"/usr/lib/",
"/usr/lib64/",
"/usr/local/lib/",
"/usr/local/lib64/",
]
return any(path_contains_subdirectory(path, x) for x in system_dirs)
class Compiler:
"""This class encapsulates a Spack "compiler", which includes C,
C++, and Fortran compilers. Subclasses should implement
support for specific compilers, their possible names, arguments,
and how to identify the particular type of compiler."""
# Optional prefix regexes for searching for this type of compiler.
# Prefixes are sometimes used for toolchains
prefixes: List[str] = []
# Optional suffix regexes for searching for this type of compiler.
# Suffixes are used by some frameworks, e.g. macports uses an '-mp-X.Y'
# version suffix for gcc.
suffixes = [r"-.*"]
#: Compiler argument that produces version information
version_argument = "-dumpversion"
#: Return values to ignore when invoking the compiler to get its version
ignore_version_errors: Sequence[int] = ()
#: Regex used to extract version from compiler's output
version_regex = "(.*)"
# These libraries are anticipated to be required by all executables built
# by any compiler
_all_compiler_rpath_libraries = ["libc", "libc++", "libstdc++"]
#: Platform matcher for Platform objects supported by compiler
is_supported_on_platform = lambda x: True
# Default flags used by a compiler to set an rpath
@property
def cc_rpath_arg(self):
return "-Wl,-rpath,"
@property
def cxx_rpath_arg(self):
return "-Wl,-rpath,"
@property
def f77_rpath_arg(self):
return "-Wl,-rpath,"
@property
def fc_rpath_arg(self):
return "-Wl,-rpath,"
@property
def linker_arg(self):
"""Flag that need to be used to pass an argument to the linker."""
return "-Wl,"
@property
def disable_new_dtags(self):
if platform.system() == "Darwin":
return ""
return "--disable-new-dtags"
@property
def enable_new_dtags(self):
if platform.system() == "Darwin":
return ""
return "--enable-new-dtags"
@property
def debug_flags(self):
return ["-g"]
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3"]
def __init__(
self,
cspec,
operating_system,
target,
paths,
modules: Optional[List[str]] = None,
alias=None,
environment=None,
extra_rpaths=None,
enable_implicit_rpaths=None,
**kwargs,
):
self.spec = cspec
self.operating_system = str(operating_system)
self.target = target
self.modules = modules or []
self.alias = alias
self.environment = environment or {}
self.extra_rpaths = extra_rpaths or []
self.enable_implicit_rpaths = enable_implicit_rpaths
self.cache = COMPILER_CACHE
self.cc = paths[0]
self.cxx = paths[1]
self.f77 = None
self.fc = None
if len(paths) > 2:
self.f77 = paths[2]
if len(paths) == 3:
self.fc = self.f77
else:
self.fc = paths[3]
# Unfortunately have to make sure these params are accepted
# in the same order they are returned by sorted(flags)
# in compilers/__init__.py
self.flags = spack.spec.FlagMap(self.spec)
for flag in self.flags.valid_compiler_flags():
value = kwargs.get(flag, None)
if value is not None:
values_with_propagation = tokenize_flags(value, False)
for value, propagation in values_with_propagation:
self.flags.add_flag(flag, value, propagation)
# caching value for compiler reported version
# used for version checks for API, e.g. C++11 flag
self._real_version = None
def __eq__(self, other):
return (
self.cc == other.cc
and self.cxx == other.cxx
and self.fc == other.fc
and self.f77 == other.f77
and self.spec == other.spec
and self.operating_system == other.operating_system
and self.target == other.target
and self.flags == other.flags
and self.modules == other.modules
and self.environment == other.environment
and self.extra_rpaths == other.extra_rpaths
and self.enable_implicit_rpaths == other.enable_implicit_rpaths
)
def __hash__(self):
return hash(
(
self.cc,
self.cxx,
self.fc,
self.f77,
self.spec,
self.operating_system,
self.target,
str(self.flags),
str(self.modules),
str(self.environment),
str(self.extra_rpaths),
self.enable_implicit_rpaths,
)
)
def verify_executables(self):
"""Raise an error if any of the compiler executables is not valid.
This method confirms that for all of the compilers (cc, cxx, f77, fc)
that have paths, those paths exist and are executable by the current
user.
Raises a CompilerAccessError if any of the non-null paths for the
compiler are not accessible.
"""
def accessible_exe(exe):
# compilers may contain executable names (on Cray or user edited)
if not os.path.isabs(exe):
exe = spack.util.executable.which_string(exe)
if not exe:
return False
return os.path.isfile(exe) and os.access(exe, os.X_OK)
# setup environment before verifying in case we have executable names
# instead of absolute paths
with self.compiler_environment():
missing = [
cmp
for cmp in (self.cc, self.cxx, self.f77, self.fc)
if cmp and not accessible_exe(cmp)
]
if missing:
raise CompilerAccessError(self, missing)
@property
def version(self):
return self.spec.version
@property
def real_version(self):
"""Executable reported compiler version used for API-determinations
E.g. C++11 flag checks.
"""
real_version_str = self.cache.get(self).real_version
if not real_version_str or real_version_str == "unknown":
return self.version
return spack.version.StandardVersion.from_string(real_version_str)
def implicit_rpaths(self) -> List[str]:
if self.enable_implicit_rpaths is False:
return []
output = self.compiler_verbose_output
if not output:
return []
link_dirs = _parse_non_system_link_dirs(output)
all_required_libs = list(self.required_libs) + Compiler._all_compiler_rpath_libraries
return list(paths_containing_libs(link_dirs, all_required_libs))
@property
def default_dynamic_linker(self) -> Optional[str]:
"""Determine default dynamic linker from compiler link line"""
output = self.compiler_verbose_output
if not output:
return None
return spack.util.libc.parse_dynamic_linker(output)
@property
def default_libc(self) -> Optional["spack.spec.Spec"]:
"""Determine libc targeted by the compiler from link line"""
# technically this should be testing the target platform of the compiler, but we don't have
# that, so stick to host platform for now.
if sys.platform in ("darwin", "win32"):
return None
dynamic_linker = self.default_dynamic_linker
if not dynamic_linker:
return None
return spack.util.libc.libc_from_dynamic_linker(dynamic_linker)
@property
def required_libs(self):
"""For executables created with this compiler, the compiler libraries
that would be generally required to run it.
"""
# By default every compiler returns the empty list
return []
@property
def compiler_verbose_output(self) -> Optional[str]:
"""Verbose output from compiling a dummy C source file. Output is cached."""
return self.cache.get(self).c_compiler_output
def _compile_dummy_c_source(self) -> Optional[str]:
if self.cc:
cc = self.cc
ext = "c"
else:
cc = self.cxx
ext = "cc"
if not cc or not self.verbose_flag:
return None
try:
tmpdir = tempfile.mkdtemp(prefix="spack-implicit-link-info")
fout = os.path.join(tmpdir, "output")
fin = os.path.join(tmpdir, f"main.{ext}")
with open(fin, "w", encoding="utf-8") as csource:
csource.write(
"int main(int argc, char* argv[]) { (void)argc; (void)argv; return 0; }\n"
)
cc_exe = spack.util.executable.Executable(cc)
for flag_type in ["cflags" if cc == self.cc else "cxxflags", "cppflags", "ldflags"]:
cc_exe.add_default_arg(*self.flags.get(flag_type, []))
with self.compiler_environment():
return cc_exe(self.verbose_flag, fin, "-o", fout, output=str, error=str)
except spack.util.executable.ProcessError as pe:
tty.debug("ProcessError: Command exited with non-zero status: " + pe.long_message)
return None
finally:
shutil.rmtree(tmpdir, ignore_errors=True)
@property
def verbose_flag(self) -> Optional[str]:
"""
This property should be overridden in the compiler subclass if a
verbose flag is available.
If it is not overridden, it is assumed to not be supported.
"""
# This property should be overridden in the compiler subclass if
# OpenMP is supported by that compiler
@property
def openmp_flag(self):
# If it is not overridden, assume it is not supported and warn the user
raise UnsupportedCompilerFlag(self, "OpenMP", "openmp_flag")
# This property should be overridden in the compiler subclass if
# C++98 is not the default standard for that compiler
@property
def cxx98_flag(self):
return ""
# This property should be overridden in the compiler subclass if
# C++11 is supported by that compiler
@property
def cxx11_flag(self):
# If it is not overridden, assume it is not supported and warn the user
raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag")
# This property should be overridden in the compiler subclass if
# C++14 is supported by that compiler
@property
def cxx14_flag(self):
# If it is not overridden, assume it is not supported and warn the user
raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag")
# This property should be overridden in the compiler subclass if
# C++17 is supported by that compiler
@property
def cxx17_flag(self):
# If it is not overridden, assume it is not supported and warn the user
raise UnsupportedCompilerFlag(self, "the C++17 standard", "cxx17_flag")
# This property should be overridden in the compiler subclass if
# C99 is supported by that compiler
@property
def c99_flag(self):
# If it is not overridden, assume it is not supported and warn the user
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag")
# This property should be overridden in the compiler subclass if
# C11 is supported by that compiler
@property
def c11_flag(self):
# If it is not overridden, assume it is not supported and warn the user
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag")
@property
def cc_pic_flag(self):
"""Returns the flag used by the C compiler to produce
Position Independent Code (PIC)."""
return "-fPIC"
@property
def cxx_pic_flag(self):
"""Returns the flag used by the C++ compiler to produce
Position Independent Code (PIC)."""
return "-fPIC"
@property
def f77_pic_flag(self):
"""Returns the flag used by the F77 compiler to produce
Position Independent Code (PIC)."""
return "-fPIC"
@property
def fc_pic_flag(self):
"""Returns the flag used by the FC compiler to produce
Position Independent Code (PIC)."""
return "-fPIC"
# Note: This is not a class method. The class methods are used to detect
# compilers on PATH based systems, and do not set up the run environment of
# the compiler. This method can be called on `module` based systems as well
def get_real_version(self) -> str:
"""Query the compiler for its version.
This is the "real" compiler version, regardless of what is in the
compilers.yaml file, which the user can change to name their compiler.
Use the runtime environment of the compiler (modules and environment
modifications) to enable the compiler to run properly on any platform.
"""
cc = spack.util.executable.Executable(self.cc)
try:
with self.compiler_environment():
output = cc(
self.version_argument,
output=str,
error=str,
ignore_errors=tuple(self.ignore_version_errors),
)
return self.extract_version_from_output(output)
except spack.util.executable.ProcessError:
return "unknown"
@property
def prefix(self):
"""Query the compiler for its install prefix. This is the install
path as reported by the compiler. Note that paths for cc, cxx, etc
are not enough to find the install prefix of the compiler, since
the can be symlinks, wrappers, or filenames instead of absolute paths."""
raise NotImplementedError("prefix is not implemented for this compiler")
#
# Compiler classes have methods for querying the version of
# specific compiler executables. This is used when discovering compilers.
#
# Compiler *instances* are just data objects, and can only be
# constructed from an actual set of executables.
#
@classmethod
def default_version(cls, cc):
"""Override just this to override all compiler version functions."""
output = get_compiler_version_output(
cc, cls.version_argument, tuple(cls.ignore_version_errors)
)
return cls.extract_version_from_output(output)
@classmethod
@llnl.util.lang.memoized
def extract_version_from_output(cls, output: str) -> str:
"""Extracts the version from compiler's output."""
match = re.search(cls.version_regex, output)
return match.group(1) if match else "unknown"
@classmethod
def cc_version(cls, cc):
return cls.default_version(cc)
@classmethod
def search_regexps(cls, language):
# Compile all the regular expressions used for files beforehand.
# This searches for any combination of <prefix><name><suffix>
# defined for the compiler
compiler_names = getattr(cls, "{0}_names".format(language))
prefixes = [""] + cls.prefixes
suffixes = [""]
if sys.platform == "win32":
ext = r"\.(?:exe|bat)"
cls_suf = [suf + ext for suf in cls.suffixes]
ext_suf = [ext]
suffixes = suffixes + cls.suffixes + cls_suf + ext_suf
else:
suffixes = suffixes + cls.suffixes
regexp_fmt = r"^({0}){1}({2})$"
return [
re.compile(regexp_fmt.format(prefix, re.escape(name), suffix))
for prefix, name, suffix in itertools.product(prefixes, compiler_names, suffixes)
]
def setup_custom_environment(self, pkg, env):
"""Set any environment variables necessary to use the compiler."""
pass
def __repr__(self):
"""Return a string representation of the compiler toolchain."""
return self.__str__()
def __str__(self):
"""Return a string representation of the compiler toolchain."""
return "%s(%s)" % (
self.name,
"\n ".join(
(
str(s)
for s in (
self.cc,
self.cxx,
self.f77,
self.fc,
self.modules,
str(self.operating_system),
)
)
),
)
@contextlib.contextmanager
def compiler_environment(self):
# Avoid modifying os.environ if possible.
if not self.modules and not self.environment:
yield
return
# store environment to replace later
backup_env = os.environ.copy()
try:
# load modules and set env variables
for module in self.modules:
spack.util.module_cmd.load_module(module)
# apply other compiler environment changes
spack.schema.environment.parse(self.environment).apply_modifications()
yield
finally:
# Restore environment regardless of whether inner code succeeded
os.environ.clear()
os.environ.update(backup_env)
def to_dict(self):
flags_dict = {fname: " ".join(fvals) for fname, fvals in self.flags.items()}
flags_dict.update(
{attr: getattr(self, attr, None) for attr in FLAG_INSTANCE_VARS if hasattr(self, attr)}
)
result = {
"spec": str(self.spec),
"paths": {attr: getattr(self, attr, None) for attr in PATH_INSTANCE_VARS},
"flags": flags_dict,
"operating_system": str(self.operating_system),
"target": str(self.target),
"modules": self.modules or [],
"environment": self.environment or {},
"extra_rpaths": self.extra_rpaths or [],
}
if self.enable_implicit_rpaths is not None:
result["implicit_rpaths"] = self.enable_implicit_rpaths
if self.alias:
result["alias"] = self.alias
return result
class CompilerAccessError(spack.error.SpackError):
def __init__(self, compiler, paths):
msg = "Compiler '%s' has executables that are missing" % compiler.spec
msg += " or are not executable: %s" % paths
super().__init__(msg)
class InvalidCompilerError(spack.error.SpackError):
def __init__(self):
super().__init__("Compiler has no executables.")
class UnsupportedCompilerFlag(spack.error.SpackError):
def __init__(self, compiler, feature, flag_name, ver_string=None):
super().__init__(
"{0} ({1}) does not support {2} (as compiler.{3}).".format(
compiler.name, ver_string if ver_string else compiler.version, feature, flag_name
),
"If you think it should, please edit the compiler.{0} subclass to".format(
compiler.name
)
+ " implement the {0} property and submit a pull request or issue.".format(flag_name),
)
class CompilerCacheEntry:
"""Deserialized cache entry for a compiler"""
__slots__ = ("c_compiler_output", "real_version")
def __init__(self, c_compiler_output: Optional[str], real_version: str):
self.c_compiler_output = c_compiler_output
self.real_version = real_version
@property
def empty(self) -> bool:
"""Sometimes the compiler is temporarily broken, preventing us from getting output. The
call site determines if that is a problem."""
return self.c_compiler_output is None
@classmethod
def from_dict(cls, data: Dict[str, Optional[str]]):
if not isinstance(data, dict):
raise ValueError(f"Invalid {cls.__name__} data")
c_compiler_output = data.get("c_compiler_output")
real_version = data.get("real_version")
if not isinstance(real_version, str) or not isinstance(
c_compiler_output, (str, type(None))
):
raise ValueError(f"Invalid {cls.__name__} data")
return cls(c_compiler_output, real_version)
class CompilerCache:
"""Base class for compiler output cache. Default implementation does not cache anything."""
def value(self, compiler: Compiler) -> Dict[str, Optional[str]]:
return {
"c_compiler_output": compiler._compile_dummy_c_source(),
"real_version": compiler.get_real_version(),
}
def get(self, compiler: Compiler) -> CompilerCacheEntry:
return CompilerCacheEntry.from_dict(self.value(compiler))
class FileCompilerCache(CompilerCache):
"""Cache for compiler output, which is used to determine implicit link paths, the default libc
version, and the compiler version."""
name = os.path.join("compilers", "compilers.json")
def __init__(self, cache: "FileCache") -> None:
self.cache = cache
self.cache.init_entry(self.name)
self._data: Dict[str, Dict[str, Optional[str]]] = {}
def _get_entry(self, key: str, *, allow_empty: bool) -> Optional[CompilerCacheEntry]:
try:
entry = CompilerCacheEntry.from_dict(self._data[key])
return entry if allow_empty or not entry.empty else None
except ValueError:
del self._data[key]
except KeyError:
pass
return None
def get(self, compiler: Compiler) -> CompilerCacheEntry:
# Cache hit
try:
with self.cache.read_transaction(self.name) as f:
assert f is not None
self._data = json.loads(f.read())
assert isinstance(self._data, dict)
except (json.JSONDecodeError, AssertionError):
self._data = {}
key = self._key(compiler)
value = self._get_entry(key, allow_empty=False)
if value is not None:
return value
# Cache miss
with self.cache.write_transaction(self.name) as (old, new):
try:
assert old is not None
self._data = json.loads(old.read())
assert isinstance(self._data, dict)
except (json.JSONDecodeError, AssertionError):
self._data = {}
# Use cache entry that may have been created by another process in the meantime.
entry = self._get_entry(key, allow_empty=True)
# Finally compute the cache entry
if entry is None:
self._data[key] = self.value(compiler)
entry = CompilerCacheEntry.from_dict(self._data[key])
new.write(json.dumps(self._data, separators=(",", ":")))
return entry
def _key(self, compiler: Compiler) -> str:
as_bytes = json.dumps(compiler.to_dict(), separators=(",", ":")).encode("utf-8")
return hashlib.sha256(as_bytes).hexdigest()
def _make_compiler_cache():
return FileCompilerCache(spack.caches.MISC_CACHE)
COMPILER_CACHE: CompilerCache = llnl.util.lang.Singleton(_make_compiler_cache) # type: ignore

View File

@ -1,119 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import re
import llnl.util.lang
from spack.compiler import Compiler
from spack.version import ver
class Aocc(Compiler):
version_argument = "--version"
@property
def debug_flags(self):
return [
"-gcodeview",
"-gdwarf-2",
"-gdwarf-3",
"-gdwarf-4",
"-gdwarf-5",
"-gline-tables-only",
"-gmodules",
"-g",
]
@property
def opt_flags(self):
return ["-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os", "-Oz", "-Og", "-O", "-O4"]
@property
def link_paths(self):
link_paths = {
"cc": os.path.join("aocc", "clang"),
"cxx": os.path.join("aocc", "clang++"),
"f77": os.path.join("aocc", "flang"),
"fc": os.path.join("aocc", "flang"),
}
return link_paths
@property
def verbose_flag(self):
return "-v"
@property
def openmp_flag(self):
return "-fopenmp"
@property
def cxx11_flag(self):
return "-std=c++11"
@property
def cxx14_flag(self):
return "-std=c++14"
@property
def cxx17_flag(self):
return "-std=c++17"
@property
def c99_flag(self):
return "-std=c99"
@property
def c11_flag(self):
return "-std=c11"
@property
def cc_pic_flag(self):
return "-fPIC"
@property
def cxx_pic_flag(self):
return "-fPIC"
@property
def f77_pic_flag(self):
return "-fPIC"
@property
def fc_pic_flag(self):
return "-fPIC"
required_libs = ["libclang"]
@classmethod
@llnl.util.lang.memoized
def extract_version_from_output(cls, output):
match = re.search(r"AOCC_(\d+)[._](\d+)[._](\d+)", output)
if match:
return ".".join(match.groups())
return "unknown"
@property
def stdcxx_libs(self):
return ("-lstdc++",)
@property
def cflags(self):
return self._handle_default_flag_addtions()
@property
def cxxflags(self):
return self._handle_default_flag_addtions()
@property
def fflags(self):
return self._handle_default_flag_addtions()
def _handle_default_flag_addtions(self):
# This is a known issue for AOCC 3.0 see:
# https://developer.amd.com/wp-content/resources/AOCC-3.0-Install-Guide.pdf
if self.version.satisfies(ver("3.0.0")):
return "-Wno-unused-command-line-argument " "-mllvm -eliminate-similar-expr=false"

View File

@ -1,115 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import re
import llnl.util.lang
import spack.compiler
import spack.compilers.clang
from spack.version import Version
class AppleClang(spack.compilers.clang.Clang):
openmp_flag = "-Xpreprocessor -fopenmp"
@classmethod
@llnl.util.lang.memoized
def extract_version_from_output(cls, output):
ver = "unknown"
match = re.search(
# Apple's LLVM compiler has its own versions, so suffix them.
r"^Apple (?:LLVM|clang) version ([^ )]+)",
output,
# Multi-line, since 'Apple clang' may not be on the first line
# in particular, when run as gcc, it seems to output
# "Configured with: --prefix=..." as the first line
re.M,
)
if match:
ver = match.group(match.lastindex)
return ver
# C++ flags based on CMake Modules/Compiler/AppleClang-CXX.cmake
@property
def cxx11_flag(self):
# Spack's AppleClang detection only valid from Xcode >= 4.6
if self.real_version < Version("4.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++11 standard", "cxx11_flag", "Xcode < 4.0"
)
return "-std=c++11"
@property
def cxx14_flag(self):
if self.real_version < Version("5.1"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++14 standard", "cxx14_flag", "Xcode < 5.1"
)
elif self.real_version < Version("6.1"):
return "-std=c++1y"
return "-std=c++14"
@property
def cxx17_flag(self):
if self.real_version < Version("6.1"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++17 standard", "cxx17_flag", "Xcode < 6.1"
)
elif self.real_version < Version("10.0"):
return "-std=c++1z"
return "-std=c++17"
@property
def cxx20_flag(self):
if self.real_version < Version("10.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++20 standard", "cxx20_flag", "Xcode < 10.0"
)
elif self.real_version < Version("13.0"):
return "-std=c++2a"
return "-std=c++20"
@property
def cxx23_flag(self):
if self.real_version < Version("13.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++23 standard", "cxx23_flag", "Xcode < 13.0"
)
return "-std=c++2b"
# C flags based on CMake Modules/Compiler/AppleClang-C.cmake
@property
def c99_flag(self):
if self.real_version < Version("4.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C99 standard", "c99_flag", "< 4.0"
)
return "-std=c99"
@property
def c11_flag(self):
if self.real_version < Version("4.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C11 standard", "c11_flag", "< 4.0"
)
return "-std=c11"
@property
def c17_flag(self):
if self.real_version < Version("11.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C17 standard", "c17_flag", "< 11.0"
)
return "-std=c17"
@property
def c23_flag(self):
if self.real_version < Version("11.0.3"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C23 standard", "c23_flag", "< 11.0.3"
)
return "-std=c2x"

View File

@ -1,79 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import spack.compiler
class Arm(spack.compiler.Compiler):
# Named wrapper links within lib/spack/env
link_paths = {
"cc": os.path.join("arm", "armclang"),
"cxx": os.path.join("arm", "armclang++"),
"f77": os.path.join("arm", "armflang"),
"fc": os.path.join("arm", "armflang"),
}
# The ``--version`` option seems to be the most consistent one for
# arm compilers. Output looks like this:
#
# $ arm<c/f>lang --version
# Arm C/C++/Fortran Compiler version 19.0 (build number 73) (based on LLVM 7.0.2)
# Target: aarch64--linux-gnu
# Thread model: posix
# InstalledDir:
# /opt/arm/arm-hpc-compiler-19.0_Generic-AArch64_RHEL-7_aarch64-linux/bin
version_argument = "--version"
version_regex = r"Arm C\/C\+\+\/Fortran Compiler version ([\d\.]+) "
@property
def verbose_flag(self):
return "-v"
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast"]
@property
def openmp_flag(self):
return "-fopenmp"
@property
def cxx11_flag(self):
return "-std=c++11"
@property
def cxx14_flag(self):
return "-std=c++14"
@property
def cxx17_flag(self):
return "-std=c++1z"
@property
def c99_flag(self):
return "-std=c99"
@property
def c11_flag(self):
return "-std=c11"
@property
def cc_pic_flag(self):
return "-fPIC"
@property
def cxx_pic_flag(self):
return "-fPIC"
@property
def f77_pic_flag(self):
return "-fPIC"
@property
def fc_pic_flag(self):
return "-fPIC"
required_libs = ["libclang", "libflang"]

View File

@ -1,127 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import Version
class Cce(Compiler):
"""Cray compiler environment compiler."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# For old cray compilers on module based systems we replace
# ``version_argument`` with the old value. Cannot be a property
# as the new value is used in classmethods for path-based detection
if not self.is_clang_based:
self.version_argument = "-V"
# MacPorts builds gcc versions with prefixes and -mp-X.Y suffixes.
suffixes = [r"-mp-\d\.\d"]
@property
def link_paths(self):
if any("PrgEnv-cray" in m for m in self.modules):
# Old module-based interface to cray compilers
return {
"cc": os.path.join("cce", "cc"),
"cxx": os.path.join("case-insensitive", "CC"),
"f77": os.path.join("cce", "ftn"),
"fc": os.path.join("cce", "ftn"),
}
return {
"cc": os.path.join("cce", "craycc"),
"cxx": os.path.join("cce", "case-insensitive", "crayCC"),
"f77": os.path.join("cce", "crayftn"),
"fc": os.path.join("cce", "crayftn"),
}
@property
def is_clang_based(self):
version = self._real_version or self.version
return version >= Version("9.0") and "classic" not in str(version)
version_argument = "--version"
version_regex = r"[Cc]ray (?:clang|C :|C\+\+ :|Fortran :) [Vv]ersion.*?(\d+(\.\d+)+)"
@property
def verbose_flag(self):
return "-v"
@property
def debug_flags(self):
return ["-g", "-G0", "-G1", "-G2", "-Gfast"]
@property
def openmp_flag(self):
if self.is_clang_based:
return "-fopenmp"
return "-h omp"
@property
def cxx11_flag(self):
if self.is_clang_based:
return "-std=c++11"
return "-h std=c++11"
@property
def cxx14_flag(self):
if self.is_clang_based:
return "-std=c++14"
return "-h std=c++14"
@property
def cxx17_flag(self):
if self.is_clang_based:
return "-std=c++17"
@property
def c99_flag(self):
if self.is_clang_based:
return "-std=c99"
elif self.real_version >= Version("8.4"):
return "-h std=c99,noconform,gnu"
elif self.real_version >= Version("8.1"):
return "-h c99,noconform,gnu"
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 8.1")
@property
def c11_flag(self):
if self.is_clang_based:
return "-std=c11"
elif self.real_version >= Version("8.5"):
return "-h std=c11,noconform,gnu"
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 8.5")
@property
def cc_pic_flag(self):
if self.is_clang_based:
return "-fPIC"
return "-h PIC"
@property
def cxx_pic_flag(self):
if self.is_clang_based:
return "-fPIC"
return "-h PIC"
@property
def f77_pic_flag(self):
if self.is_clang_based:
return "-fPIC"
return "-h PIC"
@property
def fc_pic_flag(self):
if self.is_clang_based:
return "-fPIC"
return "-h PIC"
@property
def stdcxx_libs(self):
# Cray compiler wrappers link to the standard C++ library
# without additional flags.
return ()

View File

@ -1,191 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import re
import llnl.util.lang
from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import Version
#: compiler symlink mappings for mixed f77 compilers
f77_mapping = [
("gfortran", os.path.join("clang", "gfortran")),
("xlf_r", os.path.join("xl_r", "xlf_r")),
("xlf", os.path.join("xl", "xlf")),
("ifort", os.path.join("intel", "ifort")),
]
#: compiler symlink mappings for mixed f90/fc compilers
fc_mapping = [
("gfortran", os.path.join("clang", "gfortran")),
("xlf90_r", os.path.join("xl_r", "xlf90_r")),
("xlf90", os.path.join("xl", "xlf90")),
("ifort", os.path.join("intel", "ifort")),
]
class Clang(Compiler):
version_argument = "--version"
@property
def debug_flags(self):
return [
"-gcodeview",
"-gdwarf-2",
"-gdwarf-3",
"-gdwarf-4",
"-gdwarf-5",
"-gline-tables-only",
"-gmodules",
"-g",
]
@property
def opt_flags(self):
return ["-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os", "-Oz", "-Og", "-O", "-O4"]
# Clang has support for using different fortran compilers with the
# clang executable.
@property
def link_paths(self):
# clang links are always the same
link_paths = {
"cc": os.path.join("clang", "clang"),
"cxx": os.path.join("clang", "clang++"),
}
# fortran links need to look at the actual compiler names from
# compilers.yaml to figure out which named symlink to use
for compiler_name, link_path in f77_mapping:
if self.f77 and compiler_name in self.f77:
link_paths["f77"] = link_path
break
else:
link_paths["f77"] = os.path.join("clang", "flang")
for compiler_name, link_path in fc_mapping:
if self.fc and compiler_name in self.fc:
link_paths["fc"] = link_path
break
else:
link_paths["fc"] = os.path.join("clang", "flang")
return link_paths
@property
def verbose_flag(self):
return "-v"
openmp_flag = "-fopenmp"
# C++ flags based on CMake Modules/Compiler/Clang.cmake
@property
def cxx11_flag(self):
if self.real_version < Version("3.3"):
raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 3.3")
return "-std=c++11"
@property
def cxx14_flag(self):
if self.real_version < Version("3.4"):
raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 3.5")
elif self.real_version < Version("3.5"):
return "-std=c++1y"
return "-std=c++14"
@property
def cxx17_flag(self):
if self.real_version < Version("3.5"):
raise UnsupportedCompilerFlag(self, "the C++17 standard", "cxx17_flag", "< 3.5")
elif self.real_version < Version("5.0"):
return "-std=c++1z"
return "-std=c++17"
@property
def cxx20_flag(self):
if self.real_version < Version("5.0"):
raise UnsupportedCompilerFlag(self, "the C++20 standard", "cxx20_flag", "< 5.0")
elif self.real_version < Version("11.0"):
return "-std=c++2a"
else:
return "-std=c++20"
@property
def cxx23_flag(self):
if self.real_version < Version("12.0"):
raise UnsupportedCompilerFlag(self, "the C++23 standard", "cxx23_flag", "< 12.0")
elif self.real_version < Version("17.0"):
return "-std=c++2b"
else:
return "-std=c++23"
@property
def c99_flag(self):
return "-std=c99"
@property
def c11_flag(self):
if self.real_version < Version("3.0"):
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 3.0")
if self.real_version < Version("3.1"):
return "-std=c1x"
return "-std=c11"
@property
def c17_flag(self):
if self.real_version < Version("6.0"):
raise UnsupportedCompilerFlag(self, "the C17 standard", "c17_flag", "< 6.0")
return "-std=c17"
@property
def c23_flag(self):
if self.real_version < Version("9.0"):
raise UnsupportedCompilerFlag(self, "the C23 standard", "c23_flag", "< 9.0")
elif self.real_version < Version("18.0"):
return "-std=c2x"
else:
return "-std=c23"
@property
def cc_pic_flag(self):
return "-fPIC"
@property
def cxx_pic_flag(self):
return "-fPIC"
@property
def f77_pic_flag(self):
return "-fPIC"
@property
def fc_pic_flag(self):
return "-fPIC"
required_libs = ["libclang"]
@classmethod
@llnl.util.lang.memoized
def extract_version_from_output(cls, output):
ver = "unknown"
if ("Apple" in output) or ("AMD" in output):
return ver
match = re.search(
# Normal clang compiler versions are left as-is
r"(?:clang|flang-new) version ([^ )\n]+)-svn[~.\w\d-]*|"
# Don't include hyphenated patch numbers in the version
# (see https://github.com/spack/spack/pull/14365 for details)
r"(?:clang|flang-new) version ([^ )\n]+?)-[~.\w\d-]*|"
r"(?:clang|flang-new) version ([^ )\n]+)",
output,
)
if match:
ver = match.group(match.lastindex)
return ver

View File

@ -1,78 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import spack.compiler
class Fj(spack.compiler.Compiler):
# Named wrapper links within build_env_path
link_paths = {
"cc": os.path.join("fj", "fcc"),
"cxx": os.path.join("fj", "case-insensitive", "FCC"),
"f77": os.path.join("fj", "frt"),
"fc": os.path.join("fj", "frt"),
}
version_argument = "--version"
version_regex = r"\((?:FCC|FRT)\) ([a-z\d.]+)"
required_libs = ["libfj90i", "libfj90f", "libfjsrcinfo"]
@property
def verbose_flag(self):
return "-v"
@property
def debug_flags(self):
return "-g"
@property
def opt_flags(self):
return ["-O0", "-O1", "-O2", "-O3", "-Ofast"]
@property
def openmp_flag(self):
return "-Kopenmp"
@property
def cxx98_flag(self):
return "-std=c++98"
@property
def cxx11_flag(self):
return "-std=c++11"
@property
def cxx14_flag(self):
return "-std=c++14"
@property
def cxx17_flag(self):
return "-std=c++17"
@property
def c99_flag(self):
return "-std=c99"
@property
def c11_flag(self):
return "-std=c11"
@property
def cc_pic_flag(self):
return "-KPIC"
@property
def cxx_pic_flag(self):
return "-KPIC"
@property
def f77_pic_flag(self):
return "-KPIC"
@property
def fc_pic_flag(self):
return "-KPIC"

View File

@ -1,190 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from llnl.util.filesystem import ancestor
import spack.compiler
import spack.compilers.apple_clang as apple_clang
import spack.util.executable
from spack.version import Version
class Gcc(spack.compiler.Compiler):
# MacPorts builds gcc versions with prefixes and -mp-X or -mp-X.Y suffixes.
# Homebrew and Linuxbrew may build gcc with -X, -X.Y suffixes.
# Old compatibility versions may contain XY suffixes.
suffixes = [r"-mp-\d+(?:\.\d+)?", r"-\d+(?:\.\d+)?", r"\d\d"]
# Named wrapper links within build_env_path
link_paths = {
"cc": os.path.join("gcc", "gcc"),
"cxx": os.path.join("gcc", "g++"),
"f77": os.path.join("gcc", "gfortran"),
"fc": os.path.join("gcc", "gfortran"),
}
@property
def verbose_flag(self):
return "-v"
@property
def debug_flags(self):
return ["-g", "-gstabs+", "-gstabs", "-gxcoff+", "-gxcoff", "-gvms"]
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3", "-Os", "-Ofast", "-Og"]
@property
def openmp_flag(self):
return "-fopenmp"
@property
def cxx98_flag(self):
if self.real_version < Version("6.0"):
return ""
else:
return "-std=c++98"
@property
def cxx11_flag(self):
if self.real_version < Version("4.3"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++11 standard", "cxx11_flag", " < 4.3"
)
elif self.real_version < Version("4.7"):
return "-std=c++0x"
else:
return "-std=c++11"
@property
def cxx14_flag(self):
if self.real_version < Version("4.8"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++14 standard", "cxx14_flag", "< 4.8"
)
elif self.real_version < Version("4.9"):
return "-std=c++1y"
else:
return "-std=c++14"
@property
def cxx17_flag(self):
if self.real_version < Version("5.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++17 standard", "cxx17_flag", "< 5.0"
)
elif self.real_version < Version("6.0"):
return "-std=c++1z"
else:
return "-std=c++17"
@property
def cxx20_flag(self):
if self.real_version < Version("8.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++20 standard", "cxx20_flag", "< 8.0"
)
elif self.real_version < Version("11.0"):
return "-std=c++2a"
else:
return "-std=c++20"
@property
def cxx23_flag(self):
if self.real_version < Version("11.0"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++23 standard", "cxx23_flag", "< 11.0"
)
elif self.real_version < Version("14.0"):
return "-std=c++2b"
else:
return "-std=c++23"
@property
def c99_flag(self):
if self.real_version < Version("4.5"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C99 standard", "c99_flag", "< 4.5"
)
return "-std=c99"
@property
def c11_flag(self):
if self.real_version < Version("4.7"):
raise spack.compiler.UnsupportedCompilerFlag(
self, "the C11 standard", "c11_flag", "< 4.7"
)
return "-std=c11"
@property
def cc_pic_flag(self):
return "-fPIC"
@property
def cxx_pic_flag(self):
return "-fPIC"
@property
def f77_pic_flag(self):
return "-fPIC"
@property
def fc_pic_flag(self):
return "-fPIC"
required_libs = ["libgcc", "libgfortran"]
@classmethod
def default_version(cls, cc):
"""Older versions of gcc use the ``-dumpversion`` option.
Output looks like this::
4.4.7
In GCC 7, this option was changed to only return the major
version of the compiler::
7
A new ``-dumpfullversion`` option was added that gives us
what we want::
7.2.0
"""
# Apple's gcc is actually apple clang, so skip it. Returning
# "unknown" ensures this compiler is not detected by default.
# Users can add it manually to compilers.yaml at their own risk.
if apple_clang.AppleClang.default_version(cc) != "unknown":
return "unknown"
version = super(Gcc, cls).default_version(cc)
if Version(version) >= Version("7"):
output = spack.compiler.get_compiler_version_output(cc, "-dumpfullversion")
version = cls.extract_version_from_output(output)
return version
@property
def stdcxx_libs(self):
return ("-lstdc++",)
@property
def prefix(self):
# GCC reports its install prefix when running ``-print-search-dirs``
# on the first line ``install: <prefix>``.
cc = spack.util.executable.Executable(self.cc)
with self.compiler_environment():
gcc_output = cc("-print-search-dirs", output=str, error=str)
for line in gcc_output.splitlines():
if line.startswith("install:"):
gcc_prefix = line.split(":")[1].strip()
# Go from <prefix>/lib/gcc/<triplet>/<version>/ to <prefix>
return ancestor(gcc_prefix, 4)
raise RuntimeError(
"could not find install prefix of GCC from output:\n\t{}".format(gcc_output)
)

View File

@ -1,130 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import sys
from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import Version
class Intel(Compiler):
# Named wrapper links within build_env_path
link_paths = {
"cc": os.path.join("intel", "icc"),
"cxx": os.path.join("intel", "icpc"),
"f77": os.path.join("intel", "ifort"),
"fc": os.path.join("intel", "ifort"),
}
if sys.platform == "win32":
version_argument = "/QV"
else:
version_argument = "--version"
if sys.platform == "win32":
version_regex = r"([1-9][0-9]*\.[0-9]*\.[0-9]*)"
else:
version_regex = r"\((?:IFORT|ICC)\) ([^ ]+)"
@property
def verbose_flag(self):
return "-v"
required_libs = ["libirc", "libifcore", "libifcoremt", "libirng"]
@property
def debug_flags(self):
return ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"]
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"]
@property
def openmp_flag(self):
if self.real_version < Version("16.0"):
return "-openmp"
else:
return "-qopenmp"
@property
def cxx11_flag(self):
if self.real_version < Version("11.1"):
raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 11.1")
elif self.real_version < Version("13"):
return "-std=c++0x"
else:
return "-std=c++11"
@property
def cxx14_flag(self):
# Adapted from CMake's Intel-CXX rules.
if self.real_version < Version("15"):
raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 15")
elif self.real_version < Version("15.0.2"):
return "-std=c++1y"
else:
return "-std=c++14"
@property
def cxx17_flag(self):
# https://www.intel.com/content/www/us/en/developer/articles/news/c17-features-supported-by-c-compiler.html
if self.real_version < Version("19"):
raise UnsupportedCompilerFlag(self, "the C++17 standard", "cxx17_flag", "< 19")
else:
return "-std=c++17"
@property
def c99_flag(self):
if self.real_version < Version("12"):
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 12")
else:
return "-std=c99"
@property
def c11_flag(self):
if self.real_version < Version("16"):
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 16")
else:
return "-std=c1x"
@property
def c18_flag(self):
# c18 supported since oneapi 2022, which is classic version 2021.5.0
if self.real_version < Version("21.5.0"):
raise UnsupportedCompilerFlag(self, "the C18 standard", "c18_flag", "< 21.5.0")
else:
return "-std=c18"
@property
def cc_pic_flag(self):
return "-fPIC"
@property
def cxx_pic_flag(self):
return "-fPIC"
@property
def f77_pic_flag(self):
return "-fPIC"
@property
def fc_pic_flag(self):
return "-fPIC"
@property
def stdcxx_libs(self):
return ("-cxxlib",)
def setup_custom_environment(self, pkg, env):
# Edge cases for Intel's oneAPI compilers when using the legacy classic compilers:
# Always pass flags to disable deprecation warnings, since these warnings can
# confuse tools that parse the output of compiler commands (e.g. version checks).
if self.real_version >= Version("2021") and self.real_version < Version("2024"):
env.append_flags("SPACK_ALWAYS_CFLAGS", "-diag-disable=10441")
env.append_flags("SPACK_ALWAYS_CXXFLAGS", "-diag-disable=10441")
if self.real_version >= Version("2021") and self.real_version < Version("2025"):
env.append_flags("SPACK_ALWAYS_FFLAGS", "-diag-disable=10448")

View File

@ -1,393 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import re
import subprocess
import sys
import tempfile
from typing import Dict
import archspec.cpu
import spack.compiler
import spack.operating_systems.windows_os
import spack.platforms
import spack.util.executable
from spack.compiler import Compiler
from spack.error import SpackError
from spack.version import Version, VersionRange
FC_PATH: Dict[str, str] = dict()
class CmdCall:
"""Compose a call to `cmd` for an ordered series of cmd commands/scripts"""
def __init__(self, *cmds):
if not cmds:
raise RuntimeError(
"""Attempting to run commands from CMD without specifying commands.
Please add commands to be run."""
)
self._cmds = cmds
def __call__(self):
out = subprocess.check_output(self.cmd_line, stderr=subprocess.STDOUT) # novermin
return out.decode("utf-16le", errors="replace") # novermin
@property
def cmd_line(self):
base_call = "cmd /u /c "
commands = " && ".join([x.command_str() for x in self._cmds])
# If multiple commands are being invoked by a single subshell
# they must be encapsulated by a double quote. Always double
# quote to be sure of proper handling
# cmd will properly resolve nested double quotes as needed
#
# `set`` writes out the active env to the subshell stdout,
# and in this context we are always trying to obtain env
# state so it should always be appended
return base_call + f'"{commands} && set"'
class VarsInvocation:
def __init__(self, script):
self._script = script
def command_str(self):
return f'"{self._script}"'
@property
def script(self):
return self._script
class VCVarsInvocation(VarsInvocation):
def __init__(self, script, arch, msvc_version):
super(VCVarsInvocation, self).__init__(script)
self._arch = arch
self._msvc_version = msvc_version
@property
def sdk_ver(self):
"""Accessor for Windows SDK version property
Note: This property may not be set by
the calling context and as such this property will
return an empty string
This property will ONLY be set if the SDK package
is a dependency somewhere in the Spack DAG of the package
for which we are constructing an MSVC compiler env.
Otherwise this property should be unset to allow the VCVARS
script to use its internal heuristics to determine appropriate
SDK version
"""
if getattr(self, "_sdk_ver", None):
return self._sdk_ver + ".0"
return ""
@sdk_ver.setter
def sdk_ver(self, val):
self._sdk_ver = val
@property
def arch(self):
return self._arch
@property
def vcvars_ver(self):
return f"-vcvars_ver={self._msvc_version}"
def command_str(self):
script = super(VCVarsInvocation, self).command_str()
return f"{script} {self.arch} {self.sdk_ver} {self.vcvars_ver}"
def get_valid_fortran_pth():
"""Assign maximum available fortran compiler version"""
# TODO (johnwparent): validate compatibility w/ try compiler
# functionality when added
sort_fn = lambda fc_ver: Version(fc_ver)
sort_fc_ver = sorted(list(FC_PATH.keys()), key=sort_fn)
return FC_PATH[sort_fc_ver[-1]] if sort_fc_ver else None
class Msvc(Compiler):
# Named wrapper links within build_env_path
# Due to the challenges of supporting compiler wrappers
# in Windows, we leave these blank, and dynamically compute
# based on proper versions of MSVC from there
# pending acceptance of #28117 for full support using
# compiler wrappers
link_paths = {"cc": "", "cxx": "", "f77": "", "fc": ""}
#: Compiler argument that produces version information
version_argument = ""
# For getting ifx's version, call it with version_argument
# and ignore the error code
ignore_version_errors = [1]
#: Regex used to extract version from compiler's output
version_regex = r"([1-9][0-9]*\.[0-9]*\.[0-9]*)"
# The MSVC compiler class overrides this to prevent instances
# of erroneous matching on executable names that cannot be msvc
# compilers
suffixes = []
is_supported_on_platform = lambda x: isinstance(x, spack.platforms.Windows)
def __init__(self, *args, **kwargs):
# This positional argument "paths" is later parsed and process by the base class
# via the call to `super` later in this method
paths = args[3]
latest_fc = get_valid_fortran_pth()
new_pth = [pth if pth else latest_fc for pth in paths[2:]]
paths[2:] = new_pth
# Initialize, deferring to base class but then adding the vcvarsallfile
# file based on compiler executable path.
super().__init__(*args, **kwargs)
# To use the MSVC compilers, VCVARS must be invoked
# VCVARS is located at a fixed location, referencable
# idiomatically by the following relative path from the
# compiler.
# Spack first finds the compilers via VSWHERE
# and stores their path, but their respective VCVARS
# file must be invoked before useage.
env_cmds = []
compiler_root = os.path.join(os.path.dirname(self.cc), "../../../../../..")
vcvars_script_path = os.path.join(compiler_root, "Auxiliary", "Build", "vcvars64.bat")
# get current platform architecture and format for vcvars argument
arch = spack.platforms.real_host().default.lower()
arch = arch.replace("-", "_")
if str(archspec.cpu.host().family) == "x86_64":
arch = "amd64"
self.vcvars_call = VCVarsInvocation(vcvars_script_path, arch, self.msvc_version)
env_cmds.append(self.vcvars_call)
# Below is a check for a valid fortran path
# paths has c, cxx, fc, and f77 paths in that order
# paths[2] refers to the fc path and is a generic check
# for a fortran compiler
if paths[2]:
def get_oneapi_root(pth: str):
"""From within a prefix known to be a oneAPI path
determine the oneAPI root path from arbitrary point
under root
Args:
pth: path prefixed within oneAPI root
"""
if not pth:
return ""
while os.path.basename(pth) and os.path.basename(pth) != "oneAPI":
pth = os.path.dirname(pth)
return pth
# If this found, it sets all the vars
oneapi_root = get_oneapi_root(self.fc)
if not oneapi_root:
raise RuntimeError(f"Non-oneAPI Fortran compiler {self.fc} assigned to MSVC")
oneapi_root_setvars = os.path.join(oneapi_root, "setvars.bat")
# some oneAPI exes return a version more precise than their
# install paths specify, so we determine path from
# the install path rather than the fc executable itself
numver = r"\d+\.\d+(?:\.\d+)?"
pattern = f"((?:{numver})|(?:latest))"
version_from_path = re.search(pattern, self.fc).group(1)
oneapi_version_setvars = os.path.join(
oneapi_root, "compiler", version_from_path, "env", "vars.bat"
)
# order matters here, the specific version env must be invoked first,
# otherwise it will be ignored if the root setvars sets up the oneapi
# env first
env_cmds.extend(
[VarsInvocation(oneapi_version_setvars), VarsInvocation(oneapi_root_setvars)]
)
self.msvc_compiler_environment = CmdCall(*env_cmds)
@property
def cxx11_flag(self):
return "/std:c++11"
@property
def cxx14_flag(self):
return "/std:c++14"
@property
def cxx17_flag(self):
return "/std:c++17"
@property
def cxx20_flag(self):
return "/std:c++20"
@property
def c11_flag(self):
return "/std:c11"
@property
def c17_flag(self):
return "/std:c17"
@property
def msvc_version(self):
"""This is the VCToolset version *NOT* the actual version of the cl compiler
For CL version, query `Msvc.cl_version`"""
return Version(re.search(Msvc.version_regex, self.cc).group(1))
@property
def short_msvc_version(self):
"""This is the shorthand VCToolset version of form
MSVC<short-ver>
"""
return "MSVC" + self.vc_toolset_ver
@property
def vc_toolset_ver(self):
"""
The toolset version is the version of the combined set of cl and link
This typically relates directly to VS version i.e. VS 2022 is v143
VS 19 is v142, etc.
This value is defined by the first three digits of the major + minor
version of the VS toolset (143 for 14.3x.bbbbb). Traditionally the
minor version has remained a static two digit number for a VS release
series, however, as of VS22, this is no longer true, both
14.4x.bbbbb and 14.3x.bbbbb are considered valid VS22 VC toolset
versions due to a change in toolset minor version sentiment.
This is *NOT* the full version, for that see
Msvc.msvc_version or MSVC.platform_toolset_ver for the
raw platform toolset version
"""
ver = self.msvc_version[:2].joined.string[:3]
return ver
@property
def platform_toolset_ver(self):
"""
This is the platform toolset version of current MSVC compiler
i.e. 142. The platform toolset is the targeted MSVC library/compiler
versions by compilation (this is different from the VC Toolset)
This is different from the VC toolset version as established
by `short_msvc_version`, but typically are represented by the same
three digit value
"""
# Typically VS toolset version and platform toolset versions match
# VS22 introduces the first divergence of VS toolset version
# (144 for "recent" releases) and platform toolset version (143)
# so it needs additional handling until MS releases v144
# (assuming v144 is also for VS22)
# or adds better support for detection
# TODO: (johnwparent) Update this logic for the next platform toolset
# or VC toolset version update
toolset_ver = self.vc_toolset_ver
vs22_toolset = Version(toolset_ver) > Version("142")
return toolset_ver if not vs22_toolset else "143"
@property
def visual_studio_version(self):
"""The four digit Visual Studio version (i.e. 2019 or 2022)
Note: This differs from the msvc version or toolset version as
those properties track the compiler and build tools version
respectively, whereas this tracks the VS release associated
with a given MSVC compiler.
"""
return re.search(r"[0-9]{4}", self.cc).group(0)
def _compiler_version(self, compiler):
"""Returns version object for given compiler"""
# ignore_errors below is true here due to ifx's
# non zero return code if it is not provided
# and input file
return Version(
re.search(
Msvc.version_regex,
spack.compiler.get_compiler_version_output(
compiler, version_arg=None, ignore_errors=True
),
).group(1)
)
@property
def cl_version(self):
"""Cl toolset version"""
return self._compiler_version(self.cc)
@property
def ifx_version(self):
"""Ifx compiler version associated with this version of MSVC"""
return self._compiler_version(self.fc)
@property
def vs_root(self):
# The MSVC install root is located at a fix level above the compiler
# and is referenceable idiomatically via the pattern below
# this should be consistent accross versions
return os.path.abspath(os.path.join(self.cc, "../../../../../../../.."))
def setup_custom_environment(self, pkg, env):
"""Set environment variables for MSVC using the
Microsoft-provided script."""
# Set the build environment variables for spack. Just using
# subprocess.call() doesn't work since that operates in its own
# environment which is destroyed (along with the adjusted variables)
# once the process terminates. So go the long way around: examine
# output, sort into dictionary, use that to make the build
# environment.
# vcvars can target specific sdk versions, force it to pick up concretized sdk
# version, if needed by spec
if pkg.name != "win-sdk" and "win-sdk" in pkg.spec:
self.vcvars_call.sdk_ver = pkg.spec["win-sdk"].version.string
out = self.msvc_compiler_environment()
int_env = dict(
(key, value)
for key, _, value in (line.partition("=") for line in out.splitlines())
if key and value
)
for env_var in int_env:
if os.pathsep not in int_env[env_var]:
env.set(env_var, int_env[env_var])
else:
env.set_path(env_var, int_env[env_var].split(os.pathsep))
# certain versions of ifx (2021.3.0:2023.1.0) do not play well with env:TMP
# that has a "." character in the path
# Work around by pointing tmp to the stage for the duration of the build
if self.fc and Version(self.fc_version(self.fc)).satisfies(
VersionRange("2021.3.0", "2023.1.0")
):
new_tmp = tempfile.mkdtemp(dir=pkg.stage.path)
env.set("TMP", new_tmp)
env.set("CC", self.cc)
env.set("CXX", self.cxx)
env.set("FC", self.fc)
env.set("F77", self.f77)
@classmethod
def fc_version(cls, fc):
if not sys.platform == "win32":
return "unknown"
fc_ver = cls.default_version(fc)
FC_PATH[fc_ver] = fc
try:
sps = spack.operating_systems.windows_os.WindowsOs().compiler_search_paths
except AttributeError:
raise SpackError(
"Windows compiler search paths not established, "
"please report this behavior to github.com/spack/spack"
)
clp = spack.util.executable.which_string("cl", path=sps)
return cls.default_version(clp) if clp else fc_ver

View File

@ -1,111 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import re
import llnl.util.lang
import spack.compiler
class Nag(spack.compiler.Compiler):
# Named wrapper links within build_env_path
# Use default wrappers for C and C++, in case provided in compilers.yaml
link_paths = {
"cc": "cc",
"cxx": "c++",
"f77": os.path.join("nag", "nagfor"),
"fc": os.path.join("nag", "nagfor"),
}
version_argument = "-V"
@classmethod
@llnl.util.lang.memoized
def extract_version_from_output(cls, output):
match = re.search(r"NAG Fortran Compiler Release (\d+).(\d+)\(.*\) Build (\d+)", output)
if match:
return ".".join(match.groups())
@property
def verbose_flag(self):
# NAG does not support a flag that would enable verbose output and
# compilation/linking at the same time (with either '-#' or '-dryrun'
# the compiler only prints the commands but does not run them).
# Therefore, the only thing we can do is to pass the '-v' argument to
# the underlying GCC. In order to get verbose output from the latter
# at both compile and linking stages, we need to call NAG with two
# additional flags: '-Wc,-v' and '-Wl,-v'. However, we return only
# '-Wl,-v' for the following reasons:
# 1) the interface of this method does not support multiple flags in
# the return value and, at least currently, verbose output at the
# linking stage has a higher priority for us;
# 2) NAG is usually mixed with GCC compiler, which also accepts
# '-Wl,-v' and produces meaningful result with it: '-v' is passed
# to the linker and the latter produces verbose output for the
# linking stage ('-Wc,-v', however, would break the compilation
# with a message from GCC that the flag is not recognized).
#
# This way, we at least enable the implicit rpath detection, which is
# based on compilation of a C file (see method
# spack.compiler._compile_dummy_c_source): in the case of a mixed
# NAG/GCC toolchain, the flag will be passed to g++ (e.g.
# 'g++ -Wl,-v ./main.c'), otherwise, the flag will be passed to nagfor
# (e.g. 'nagfor -Wl,-v ./main.c' - note that nagfor recognizes '.c'
# extension and treats the file accordingly). The list of detected
# rpaths will contain only GCC-related directories and rpaths to
# NAG-related directories are injected by nagfor anyway.
return "-Wl,-v"
@property
def openmp_flag(self):
return "-openmp"
@property
def debug_flags(self):
return ["-g", "-gline", "-g90"]
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"]
@property
def cxx11_flag(self):
# NAG does not have a C++ compiler
# However, it can be mixed with a compiler that does support it
return "-std=c++11"
@property
def f77_pic_flag(self):
return "-PIC"
@property
def fc_pic_flag(self):
return "-PIC"
# Unlike other compilers, the NAG compiler passes options to GCC, which
# then passes them to the linker. Therefore, we need to doubly wrap the
# options with '-Wl,-Wl,,'
@property
def f77_rpath_arg(self):
return "-Wl,-Wl,,-rpath,,"
@property
def fc_rpath_arg(self):
return "-Wl,-Wl,,-rpath,,"
@property
def linker_arg(self):
return "-Wl,-Wl,,"
@property
def disable_new_dtags(self):
# Disable RPATH/RUNPATH forcing for NAG/GCC mixed toolchains:
return ""
@property
def enable_new_dtags(self):
# Disable RPATH/RUNPATH forcing for NAG/GCC mixed toolchains:
return ""

View File

@ -1,78 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from spack.compiler import Compiler
class Nvhpc(Compiler):
# Named wrapper links within build_env_path
link_paths = {
"cc": os.path.join("nvhpc", "nvc"),
"cxx": os.path.join("nvhpc", "nvc++"),
"f77": os.path.join("nvhpc", "nvfortran"),
"fc": os.path.join("nvhpc", "nvfortran"),
}
version_argument = "--version"
version_regex = r"nv[^ ]* (?:[^ ]+ Dev-r)?([0-9.]+)(?:-[0-9]+)?"
@property
def verbose_flag(self):
return "-v"
@property
def debug_flags(self):
return ["-g", "-gopt"]
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"]
@property
def openmp_flag(self):
return "-mp"
@property
def cc_pic_flag(self):
return "-fpic"
@property
def cxx_pic_flag(self):
return "-fpic"
@property
def f77_pic_flag(self):
return "-fpic"
@property
def fc_pic_flag(self):
return "-fpic"
@property
def c99_flag(self):
return "-c99"
@property
def c11_flag(self):
return "-c11"
@property
def cxx11_flag(self):
return "--c++11"
@property
def cxx14_flag(self):
return "--c++14"
@property
def cxx17_flag(self):
return "--c++17"
@property
def stdcxx_libs(self):
return ("-c++libs",)
required_libs = ["libnvc", "libnvf"]

View File

@ -1,171 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from os.path import dirname, join
from llnl.util import tty
from llnl.util.filesystem import ancestor
import spack.util.executable
from spack.compiler import Compiler
from spack.version import Version
class Oneapi(Compiler):
# Named wrapper links within build_env_path
link_paths = {
"cc": os.path.join("oneapi", "icx"),
"cxx": os.path.join("oneapi", "icpx"),
"f77": os.path.join("oneapi", "ifx"),
"fc": os.path.join("oneapi", "ifx"),
}
version_argument = "--version"
version_regex = r"(?:(?:oneAPI DPC\+\+(?:\/C\+\+)? Compiler)|(?:\(IFORT\))|(?:\(IFX\))) (\S+)"
@property
def verbose_flag(self):
return "-v"
required_libs = [
"libirc",
"libifcore",
"libifcoremt",
"libirng",
"libsvml",
"libintlc",
"libimf",
"libsycl",
"libOpenCL",
]
@property
def debug_flags(self):
return ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"]
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"]
@property
def openmp_flag(self):
return "-fiopenmp"
# There may be some additional options here for offload, e.g. :
# -fopenmp-simd Emit OpenMP code only for SIMD-based constructs.
# -fopenmp-targets=<value>
# -fopenmp-version=<value>
# -fopenmp Parse OpenMP pragmas and generate parallel code.
# -qno-openmp Disable OpenMP support
# -qopenmp-link=<value> Choose whether to link with the static or
# dynamic OpenMP libraries. Default is dynamic.
# -qopenmp-simd Emit OpenMP code only for SIMD-based constructs.
# -qopenmp-stubs enables the user to compile OpenMP programs in
# sequential mode. The OpenMP directives are
# ignored and a stub OpenMP library is linked.
# -qopenmp-threadprivate=<value>
# -qopenmp Parse OpenMP pragmas and generate parallel code.
# -static-openmp Use the static host OpenMP runtime while
# linking.
# -Xopenmp-target=<triple> <arg>
# -Xopenmp-target <arg> Pass <arg> to the target offloading toolchain.
# Source: icx --help output
@property
def cxx11_flag(self):
return "-std=c++11"
@property
def cxx14_flag(self):
return "-std=c++14"
@property
def cxx17_flag(self):
return "-std=c++17"
@property
def cxx20_flag(self):
return "-std=c++20"
@property
def c99_flag(self):
return "-std=c99"
@property
def c11_flag(self):
return "-std=c1x"
@property
def cc_pic_flag(self):
return "-fPIC"
@property
def cxx_pic_flag(self):
return "-fPIC"
@property
def f77_pic_flag(self):
return "-fPIC"
@property
def fc_pic_flag(self):
return "-fPIC"
@property
def stdcxx_libs(self):
return ("-cxxlib",)
@property
def prefix(self):
# OneAPI reports its install prefix when running ``--version``
# on the line ``InstalledDir: <prefix>/bin/compiler``.
cc = spack.util.executable.Executable(self.cc)
with self.compiler_environment():
oneapi_output = cc("--version", output=str, error=str)
for line in oneapi_output.splitlines():
if line.startswith("InstalledDir:"):
oneapi_prefix = line.split(":")[1].strip()
# Go from <prefix>/bin/compiler to <prefix>
return ancestor(oneapi_prefix, 2)
raise RuntimeError(
"could not find install prefix of OneAPI from output:\n\t{}".format(oneapi_output)
)
def setup_custom_environment(self, pkg, env):
# workaround bug in icpx driver where it requires sycl-post-link is on the PATH
# It is located in the same directory as the driver. Error message:
# clang++: error: unable to execute command:
# Executable "sycl-post-link" doesn't exist!
# also ensures that shared objects and libraries required by the compiler,
# e.g. libonnx, can be found succesfully
# due to a fix, this is no longer required for OneAPI versions >= 2024.2
if self.cxx and pkg.spec.satisfies("%oneapi@:2024.1"):
env.prepend_path("PATH", dirname(self.cxx))
env.prepend_path("LD_LIBRARY_PATH", join(dirname(dirname(self.cxx)), "lib"))
# Edge cases for Intel's oneAPI compilers when using the legacy classic compilers:
# Always pass flags to disable deprecation warnings, since these warnings can
# confuse tools that parse the output of compiler commands (e.g. version checks).
# This is really only needed for Fortran, since oneapi@ should be using either
# icx+icpx+ifx or icx+icpx+ifort. But to be on the safe side (some users may
# want to try to swap icpx against icpc, for example), and since the Intel LLVM
# compilers accept these diag-disable flags, we apply them for all compilers.
if self.real_version >= Version("2021") and self.real_version < Version("2024"):
env.append_flags("SPACK_ALWAYS_CFLAGS", "-diag-disable=10441")
env.append_flags("SPACK_ALWAYS_CXXFLAGS", "-diag-disable=10441")
if self.real_version >= Version("2021") and self.real_version < Version("2025"):
env.append_flags("SPACK_ALWAYS_FFLAGS", "-diag-disable=10448")
# 2024 release bumped the libsycl version because of an ABI
# change, 2024 compilers are required. You will see this
# error:
#
# /usr/bin/ld: warning: libsycl.so.7, needed by ...., not found
if pkg.spec.satisfies("%oneapi@:2023"):
for c in ["dnn"]:
if pkg.spec.satisfies(f"^intel-oneapi-{c}@2024:"):
tty.warn(f"intel-oneapi-{c}@2024 SYCL APIs requires %oneapi@2024:")

View File

@ -1,53 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import re
import llnl.util.lang
import spack.compilers.clang
class Rocmcc(spack.compilers.clang.Clang):
@property
def link_paths(self):
link_paths = {
"cc": "rocmcc/amdclang",
"cxx": "rocmcc/amdclang++",
"f77": "rocmcc/amdflang",
"fc": "rocmcc/amdflang",
}
return link_paths
@property
def cxx11_flag(self):
return "-std=c++11"
@property
def cxx14_flag(self):
return "-std=c++14"
@property
def cxx17_flag(self):
return "-std=c++17"
@property
def c99_flag(self):
return "-std=c99"
@property
def c11_flag(self):
return "-std=c11"
@classmethod
@llnl.util.lang.memoized
def extract_version_from_output(cls, output):
match = re.search(r"llvm-project roc-(\d+)[._](\d+)[._](\d+)", output)
if match:
return ".".join(match.groups())
@property
def stdcxx_libs(self):
return ("-lstdc++",)

View File

@ -1,92 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
from spack.compiler import Compiler, UnsupportedCompilerFlag
from spack.version import Version
class Xl(Compiler):
# Named wrapper links within build_env_path
link_paths = {
"cc": os.path.join("xl", "xlc"),
"cxx": os.path.join("xl", "xlc++"),
"f77": os.path.join("xl", "xlf"),
"fc": os.path.join("xl", "xlf90"),
}
version_argument = "-qversion"
version_regex = r"([0-9]?[0-9]\.[0-9])"
@property
def verbose_flag(self):
return "-V"
@property
def debug_flags(self):
return ["-g", "-g0", "-g1", "-g2", "-g8", "-g9"]
@property
def opt_flags(self):
return ["-O", "-O0", "-O1", "-O2", "-O3", "-O4", "-O5", "-Ofast"]
@property
def openmp_flag(self):
return "-qsmp=omp"
@property
def cxx11_flag(self):
if self.real_version < Version("13.1"):
raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 13.1")
else:
return "-qlanglvl=extended0x"
@property
def c99_flag(self):
if self.real_version >= Version("13.1.1"):
return "-std=gnu99"
if self.real_version >= Version("10.1"):
return "-qlanglvl=extc99"
raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", "< 10.1")
@property
def c11_flag(self):
if self.real_version >= Version("13.1.2"):
return "-std=gnu11"
if self.real_version >= Version("12.1"):
return "-qlanglvl=extc1x"
raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", "< 12.1")
@property
def cxx14_flag(self):
# .real_version does not have the "y.z" component of "w.x.y.z", which
# is required to distinguish whether support is available
if self.version >= Version("16.1.1.8"):
return "-std=c++14"
raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 16.1.1.8")
@property
def cc_pic_flag(self):
return "-qpic"
@property
def cxx_pic_flag(self):
return "-qpic"
@property
def f77_pic_flag(self):
return "-qpic"
@property
def fc_pic_flag(self):
return "-qpic"
@property
def fflags(self):
# The -qzerosize flag is effective only for the Fortran 77
# compilers and allows the use of zero size objects.
# For Fortran 90 and beyond, it is set by default and has not impact.
# Its use has no negative side effects.
return "-qzerosize"

View File

@ -1,17 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import spack.compilers.xl
class XlR(spack.compilers.xl.Xl):
# Named wrapper links within build_env_path
link_paths = {
"cc": os.path.join("xl_r", "xlc_r"),
"cxx": os.path.join("xl_r", "xlc++_r"),
"f77": os.path.join("xl_r", "xlf_r"),
"fc": os.path.join("xl_r", "xlf90_r"),
}

View File

@ -1,3 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

View File

@ -1,778 +0,0 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Test basic behavior of compilers in Spack"""
import json
import os
from copy import copy
from typing import Optional
import pytest
import llnl.util.filesystem as fs
import spack.compiler
import spack.compilers
import spack.config
import spack.spec
import spack.util.module_cmd
from spack.compiler import Compiler
from spack.util.executable import Executable
from spack.util.file_cache import FileCache
# FIXME (compiler as nodes): revisit this test
# def test_multiple_conflicting_compiler_definitions(mutable_config):
# compiler_def = {
# "compiler": {
# "flags": {},
# "modules": [],
# "paths": {"cc": "cc", "cxx": "cxx", "f77": "null", "fc": "null"},
# "extra_rpaths": [],
# "operating_system": "test",
# "target": "test",
# "environment": {},
# "spec": "clang@0.0.0",
# }
# }
#
# compiler_config = [compiler_def, compiler_def]
# compiler_config[0]["compiler"]["paths"]["f77"] = "f77"
# mutable_config.update_config("compilers", compiler_config)
#
# arch_spec = spack.spec.ArchSpec(("test", "test", "test"))
# cmp = spack.compilers.compiler_for_spec("clang@=0.0.0", arch_spec)
# assert cmp.f77 == "f77"
def test_compiler_flags_from_config_are_grouped():
compiler_entry = {
"spec": "intel@17.0.2",
"operating_system": "foo-os",
"paths": {"cc": "cc-path", "cxx": "cxx-path", "fc": None, "f77": None},
"flags": {"cflags": "-O0 -foo-flag foo-val"},
"modules": None,
}
compiler = spack.compilers.compiler_from_dict(compiler_entry)
assert any(x == "-foo-flag foo-val" for x in compiler.flags["cflags"])
# Test behavior of flags and UnsupportedCompilerFlag.
# Utility function to test most flags.
default_compiler_entry = {
"spec": "apple-clang@2.0.0",
"operating_system": "foo-os",
"paths": {"cc": "cc-path", "cxx": "cxx-path", "fc": "fc-path", "f77": "f77-path"},
"flags": {},
"modules": None,
}
# Fake up a mock compiler where everything is defaulted.
class MockCompiler(Compiler):
def __init__(self):
super().__init__(
cspec="badcompiler@1.0.0",
operating_system=default_compiler_entry["operating_system"],
target=None,
paths=[
default_compiler_entry["paths"]["cc"],
default_compiler_entry["paths"]["cxx"],
default_compiler_entry["paths"]["fc"],
default_compiler_entry["paths"]["f77"],
],
environment={},
)
@property
def name(self):
return "mockcompiler"
@property
def version(self):
return "1.0.0"
_verbose_flag = "--verbose"
@property
def verbose_flag(self):
return self._verbose_flag
required_libs = ["libgfortran"]
@pytest.mark.not_on_windows("Not supported on Windows (yet)")
def test_implicit_rpaths(dirs_with_libfiles, monkeypatch):
lib_to_dirs, all_dirs = dirs_with_libfiles
monkeypatch.setattr(
MockCompiler,
"_compile_dummy_c_source",
lambda self: "ld " + " ".join(f"-L{d}" for d in all_dirs),
)
retrieved_rpaths = MockCompiler().implicit_rpaths()
assert set(retrieved_rpaths) == set(lib_to_dirs["libstdc++"] + lib_to_dirs["libgfortran"])
without_flag_output = "ld -L/path/to/first/lib -L/path/to/second/lib64"
with_flag_output = "ld -L/path/to/first/with/flag/lib -L/path/to/second/lib64"
def call_compiler(exe, *args, **kwargs):
# This method can replace Executable.__call__ to emulate a compiler that
# changes libraries depending on a flag.
if "--correct-flag" in exe.exe:
return with_flag_output
return without_flag_output
@pytest.mark.not_on_windows("Not supported on Windows (yet)")
@pytest.mark.parametrize(
"exe,flagname",
[
("cxx", "cxxflags"),
("cxx", "cppflags"),
("cxx", "ldflags"),
("cc", "cflags"),
("cc", "cppflags"),
],
)
@pytest.mark.enable_compiler_execution
def test_compile_dummy_c_source_adds_flags(monkeypatch, exe, flagname):
# create fake compiler that emits mock verbose output
compiler = MockCompiler()
monkeypatch.setattr(Executable, "__call__", call_compiler)
if exe == "cxx":
compiler.cc = None
compiler.fc = None
compiler.f77 = None
elif exe == "cc":
compiler.cxx = None
compiler.fc = None
compiler.f77 = None
else:
assert False
# Test without flags
assert compiler._compile_dummy_c_source() == without_flag_output
if flagname:
# set flags and test
compiler.flags = {flagname: ["--correct-flag"]}
assert compiler._compile_dummy_c_source() == with_flag_output
@pytest.mark.enable_compiler_execution
def test_compile_dummy_c_source_no_path():
compiler = MockCompiler()
compiler.cc = None
compiler.cxx = None
assert compiler._compile_dummy_c_source() is None
@pytest.mark.enable_compiler_execution
def test_compile_dummy_c_source_no_verbose_flag():
compiler = MockCompiler()
compiler._verbose_flag = None
assert compiler._compile_dummy_c_source() is None
@pytest.mark.not_on_windows("Not supported on Windows (yet)")
@pytest.mark.enable_compiler_execution
def test_compile_dummy_c_source_load_env(working_env, monkeypatch, tmpdir):
gcc = str(tmpdir.join("gcc"))
with open(gcc, "w", encoding="utf-8") as f:
f.write(
f"""#!/bin/sh
if [ "$ENV_SET" = "1" ] && [ "$MODULE_LOADED" = "1" ]; then
printf '{without_flag_output}'
fi
"""
)
fs.set_executable(gcc)
# Set module load to turn compiler on
def module(*args):
if args[0] == "show":
return ""
elif args[0] == "load":
os.environ["MODULE_LOADED"] = "1"
monkeypatch.setattr(spack.util.module_cmd, "module", module)
compiler = MockCompiler()
compiler.cc = gcc
compiler.environment = {"set": {"ENV_SET": "1"}}
compiler.modules = ["turn_on"]
assert compiler._compile_dummy_c_source() == without_flag_output
# Get the desired flag from the specified compiler spec.
def flag_value(flag, spec):
compiler = None
if spec is None:
compiler = MockCompiler()
else:
compiler_entry = copy(default_compiler_entry)
compiler_entry["spec"] = spec
compiler = spack.compilers.compiler_from_dict(compiler_entry)
return getattr(compiler, flag)
# Utility function to verify that the expected exception is thrown for
# an unsupported flag.
def unsupported_flag_test(flag, spec=None):
caught_exception = None
try:
flag_value(flag, spec)
except spack.compiler.UnsupportedCompilerFlag:
caught_exception = True
assert caught_exception and "Expected exception not thrown."
# Verify the expected flag value for the give compiler spec.
def supported_flag_test(flag, flag_value_ref, spec=None):
assert flag_value(flag, spec) == flag_value_ref
# Tests for UnsupportedCompilerFlag exceptions from default
# implementations of flags.
def test_default_flags():
supported_flag_test("cc_rpath_arg", "-Wl,-rpath,")
supported_flag_test("cxx_rpath_arg", "-Wl,-rpath,")
supported_flag_test("f77_rpath_arg", "-Wl,-rpath,")
supported_flag_test("fc_rpath_arg", "-Wl,-rpath,")
supported_flag_test("linker_arg", "-Wl,")
unsupported_flag_test("openmp_flag")
unsupported_flag_test("cxx11_flag")
unsupported_flag_test("cxx14_flag")
unsupported_flag_test("cxx17_flag")
supported_flag_test("cxx98_flag", "")
unsupported_flag_test("c99_flag")
unsupported_flag_test("c11_flag")
supported_flag_test("cc_pic_flag", "-fPIC")
supported_flag_test("cxx_pic_flag", "-fPIC")
supported_flag_test("f77_pic_flag", "-fPIC")
supported_flag_test("fc_pic_flag", "-fPIC")
supported_flag_test("debug_flags", ["-g"])
supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3"])
# Verify behavior of particular compiler definitions.
def test_arm_flags():
supported_flag_test("openmp_flag", "-fopenmp", "arm@1.0")
supported_flag_test("cxx11_flag", "-std=c++11", "arm@1.0")
supported_flag_test("cxx14_flag", "-std=c++14", "arm@1.0")
supported_flag_test("cxx17_flag", "-std=c++1z", "arm@1.0")
supported_flag_test("c99_flag", "-std=c99", "arm@1.0")
supported_flag_test("c11_flag", "-std=c11", "arm@1.0")
supported_flag_test("cc_pic_flag", "-fPIC", "arm@1.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "arm@1.0")
supported_flag_test("f77_pic_flag", "-fPIC", "arm@1.0")
supported_flag_test("fc_pic_flag", "-fPIC", "arm@1.0")
supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast"], "arm@1.0")
def test_cce_flags():
supported_flag_test("version_argument", "--version", "cce@9.0.1")
supported_flag_test("version_argument", "-V", "cce@9.0.1-classic")
supported_flag_test("openmp_flag", "-fopenmp", "cce@9.0.1")
supported_flag_test("openmp_flag", "-h omp", "cce@9.0.1-classic")
supported_flag_test("openmp_flag", "-h omp", "cce@1.0")
supported_flag_test("cxx11_flag", "-std=c++11", "cce@9.0.1")
supported_flag_test("cxx11_flag", "-h std=c++11", "cce@9.0.1-classic")
supported_flag_test("cxx11_flag", "-h std=c++11", "cce@1.0")
unsupported_flag_test("c99_flag", "cce@8.0")
supported_flag_test("c99_flag", "-std=c99", "cce@9.0.1")
supported_flag_test("c99_flag", "-h c99,noconform,gnu", "cce@8.1")
supported_flag_test("c99_flag", "-h std=c99,noconform,gnu", "cce@8.4")
unsupported_flag_test("c11_flag", "cce@8.4")
supported_flag_test("c11_flag", "-std=c11", "cce@9.0.1")
supported_flag_test("c11_flag", "-h std=c11,noconform,gnu", "cce@8.5")
supported_flag_test("cc_pic_flag", "-h PIC", "cce@1.0")
supported_flag_test("cxx_pic_flag", "-h PIC", "cce@1.0")
supported_flag_test("f77_pic_flag", "-h PIC", "cce@1.0")
supported_flag_test("fc_pic_flag", "-h PIC", "cce@1.0")
supported_flag_test("cc_pic_flag", "-fPIC", "cce@9.1.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "cce@9.1.0")
supported_flag_test("f77_pic_flag", "-fPIC", "cce@9.1.0")
supported_flag_test("fc_pic_flag", "-fPIC", "cce@9.1.0")
supported_flag_test("stdcxx_libs", (), "cce@1.0")
supported_flag_test("debug_flags", ["-g", "-G0", "-G1", "-G2", "-Gfast"], "cce@1.0")
def test_apple_clang_flags():
supported_flag_test("openmp_flag", "-Xpreprocessor -fopenmp", "apple-clang@2.0.0")
unsupported_flag_test("cxx11_flag", "apple-clang@2.0.0")
supported_flag_test("cxx11_flag", "-std=c++11", "apple-clang@4.0.0")
unsupported_flag_test("cxx14_flag", "apple-clang@5.0.0")
supported_flag_test("cxx14_flag", "-std=c++1y", "apple-clang@5.1.0")
supported_flag_test("cxx14_flag", "-std=c++14", "apple-clang@6.1.0")
unsupported_flag_test("cxx17_flag", "apple-clang@6.0.0")
supported_flag_test("cxx17_flag", "-std=c++1z", "apple-clang@6.1.0")
supported_flag_test("c99_flag", "-std=c99", "apple-clang@6.1.0")
unsupported_flag_test("c11_flag", "apple-clang@3.0.0")
supported_flag_test("c11_flag", "-std=c11", "apple-clang@6.1.0")
supported_flag_test("cc_pic_flag", "-fPIC", "apple-clang@2.0.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "apple-clang@2.0.0")
supported_flag_test("f77_pic_flag", "-fPIC", "apple-clang@2.0.0")
supported_flag_test("fc_pic_flag", "-fPIC", "apple-clang@2.0.0")
def test_clang_flags():
supported_flag_test("version_argument", "--version", "clang@foo.bar")
supported_flag_test("openmp_flag", "-fopenmp", "clang@3.3")
unsupported_flag_test("cxx11_flag", "clang@3.2")
supported_flag_test("cxx11_flag", "-std=c++11", "clang@3.3")
unsupported_flag_test("cxx14_flag", "clang@3.3")
supported_flag_test("cxx14_flag", "-std=c++1y", "clang@3.4")
supported_flag_test("cxx14_flag", "-std=c++14", "clang@3.5")
unsupported_flag_test("cxx17_flag", "clang@3.4")
supported_flag_test("cxx17_flag", "-std=c++1z", "clang@3.5")
supported_flag_test("cxx17_flag", "-std=c++17", "clang@5.0")
unsupported_flag_test("cxx20_flag", "clang@4.0")
supported_flag_test("cxx20_flag", "-std=c++2a", "clang@5.0")
supported_flag_test("cxx20_flag", "-std=c++20", "clang@11.0")
unsupported_flag_test("cxx23_flag", "clang@11.0")
supported_flag_test("cxx23_flag", "-std=c++2b", "clang@12.0")
supported_flag_test("cxx23_flag", "-std=c++23", "clang@17.0")
supported_flag_test("c99_flag", "-std=c99", "clang@3.3")
unsupported_flag_test("c11_flag", "clang@2.0")
supported_flag_test("c11_flag", "-std=c11", "clang@6.1.0")
unsupported_flag_test("c23_flag", "clang@8.0")
supported_flag_test("c23_flag", "-std=c2x", "clang@9.0")
supported_flag_test("c23_flag", "-std=c23", "clang@18.0")
supported_flag_test("cc_pic_flag", "-fPIC", "clang@3.3")
supported_flag_test("cxx_pic_flag", "-fPIC", "clang@3.3")
supported_flag_test("f77_pic_flag", "-fPIC", "clang@3.3")
supported_flag_test("fc_pic_flag", "-fPIC", "clang@3.3")
supported_flag_test(
"debug_flags",
[
"-gcodeview",
"-gdwarf-2",
"-gdwarf-3",
"-gdwarf-4",
"-gdwarf-5",
"-gline-tables-only",
"-gmodules",
"-g",
],
"clang@3.3",
)
supported_flag_test(
"opt_flags",
["-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os", "-Oz", "-Og", "-O", "-O4"],
"clang@3.3",
)
def test_aocc_flags():
supported_flag_test(
"debug_flags",
[
"-gcodeview",
"-gdwarf-2",
"-gdwarf-3",
"-gdwarf-4",
"-gdwarf-5",
"-gline-tables-only",
"-gmodules",
"-g",
],
"aocc@2.2.0",
)
supported_flag_test(
"opt_flags",
["-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os", "-Oz", "-Og", "-O", "-O4"],
"aocc@2.2.0",
)
supported_flag_test("stdcxx_libs", ("-lstdc++",), "aocc@2.2.0")
supported_flag_test("openmp_flag", "-fopenmp", "aocc@2.2.0")
supported_flag_test("cxx11_flag", "-std=c++11", "aocc@2.2.0")
supported_flag_test("cxx14_flag", "-std=c++14", "aocc@2.2.0")
supported_flag_test("cxx17_flag", "-std=c++17", "aocc@2.2.0")
supported_flag_test("c99_flag", "-std=c99", "aocc@2.2.0")
supported_flag_test("c11_flag", "-std=c11", "aocc@2.2.0")
supported_flag_test("cc_pic_flag", "-fPIC", "aocc@2.2.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "aocc@2.2.0")
supported_flag_test("f77_pic_flag", "-fPIC", "aocc@2.2.0")
supported_flag_test("fc_pic_flag", "-fPIC", "aocc@2.2.0")
supported_flag_test("version_argument", "--version", "aocc@2.2.0")
flg = "-Wno-unused-command-line-argument -mllvm -eliminate-similar-expr=false"
supported_flag_test("cflags", flg, "aocc@3.0.0")
supported_flag_test("cxxflags", flg, "aocc@3.0.0")
supported_flag_test("fflags", flg, "aocc@3.0.0")
def test_fj_flags():
supported_flag_test("openmp_flag", "-Kopenmp", "fj@4.0.0")
supported_flag_test("cxx98_flag", "-std=c++98", "fj@4.0.0")
supported_flag_test("cxx11_flag", "-std=c++11", "fj@4.0.0")
supported_flag_test("cxx14_flag", "-std=c++14", "fj@4.0.0")
supported_flag_test("cxx17_flag", "-std=c++17", "fj@4.0.0")
supported_flag_test("c99_flag", "-std=c99", "fj@4.0.0")
supported_flag_test("c11_flag", "-std=c11", "fj@4.0.0")
supported_flag_test("cc_pic_flag", "-KPIC", "fj@4.0.0")
supported_flag_test("cxx_pic_flag", "-KPIC", "fj@4.0.0")
supported_flag_test("f77_pic_flag", "-KPIC", "fj@4.0.0")
supported_flag_test("fc_pic_flag", "-KPIC", "fj@4.0.0")
supported_flag_test("opt_flags", ["-O0", "-O1", "-O2", "-O3", "-Ofast"], "fj@4.0.0")
supported_flag_test("debug_flags", "-g", "fj@4.0.0")
def test_gcc_flags():
supported_flag_test("openmp_flag", "-fopenmp", "gcc@4.1")
supported_flag_test("cxx98_flag", "", "gcc@5.2")
supported_flag_test("cxx98_flag", "-std=c++98", "gcc@6.0")
unsupported_flag_test("cxx11_flag", "gcc@4.2")
supported_flag_test("cxx11_flag", "-std=c++0x", "gcc@4.3")
supported_flag_test("cxx11_flag", "-std=c++11", "gcc@4.7")
unsupported_flag_test("cxx14_flag", "gcc@4.7")
supported_flag_test("cxx14_flag", "-std=c++1y", "gcc@4.8")
supported_flag_test("cxx14_flag", "-std=c++14", "gcc@4.9")
supported_flag_test("cxx14_flag", "-std=c++14", "gcc@6.0")
unsupported_flag_test("cxx17_flag", "gcc@4.9")
supported_flag_test("cxx17_flag", "-std=c++1z", "gcc@5.0")
supported_flag_test("cxx17_flag", "-std=c++17", "gcc@6.0")
unsupported_flag_test("c99_flag", "gcc@4.4")
supported_flag_test("c99_flag", "-std=c99", "gcc@4.5")
unsupported_flag_test("c11_flag", "gcc@4.6")
supported_flag_test("c11_flag", "-std=c11", "gcc@4.7")
supported_flag_test("cc_pic_flag", "-fPIC", "gcc@4.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "gcc@4.0")
supported_flag_test("f77_pic_flag", "-fPIC", "gcc@4.0")
supported_flag_test("fc_pic_flag", "-fPIC", "gcc@4.0")
supported_flag_test("stdcxx_libs", ("-lstdc++",), "gcc@4.1")
supported_flag_test(
"debug_flags", ["-g", "-gstabs+", "-gstabs", "-gxcoff+", "-gxcoff", "-gvms"], "gcc@4.0"
)
supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Os", "-Ofast", "-Og"], "gcc@4.0"
)
def test_intel_flags():
supported_flag_test("openmp_flag", "-openmp", "intel@=15.0")
supported_flag_test("openmp_flag", "-qopenmp", "intel@=16.0")
unsupported_flag_test("cxx11_flag", "intel@=11.0")
supported_flag_test("cxx11_flag", "-std=c++0x", "intel@=12.0")
supported_flag_test("cxx11_flag", "-std=c++11", "intel@=13")
unsupported_flag_test("cxx14_flag", "intel@=14.0")
supported_flag_test("cxx14_flag", "-std=c++1y", "intel@=15.0")
supported_flag_test("cxx14_flag", "-std=c++14", "intel@=15.0.2")
unsupported_flag_test("cxx17_flag", "intel@=18")
supported_flag_test("cxx17_flag", "-std=c++17", "intel@=19.0")
unsupported_flag_test("c99_flag", "intel@=11.0")
supported_flag_test("c99_flag", "-std=c99", "intel@=12.0")
unsupported_flag_test("c11_flag", "intel@=15.0")
supported_flag_test("c18_flag", "-std=c18", "intel@=21.5.0")
unsupported_flag_test("c18_flag", "intel@=21.4.0")
supported_flag_test("c11_flag", "-std=c1x", "intel@=16.0")
supported_flag_test("cc_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("f77_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("fc_pic_flag", "-fPIC", "intel@=1.0")
supported_flag_test("stdcxx_libs", ("-cxxlib",), "intel@=1.0")
supported_flag_test("debug_flags", ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"], "intel@=1.0")
supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"], "intel@=1.0"
)
def test_oneapi_flags():
supported_flag_test("openmp_flag", "-fiopenmp", "oneapi@=2020.8.0.0827")
supported_flag_test("cxx11_flag", "-std=c++11", "oneapi@=2020.8.0.0827")
supported_flag_test("cxx14_flag", "-std=c++14", "oneapi@=2020.8.0.0827")
supported_flag_test("c99_flag", "-std=c99", "oneapi@=2020.8.0.0827")
supported_flag_test("c11_flag", "-std=c1x", "oneapi@=2020.8.0.0827")
supported_flag_test("cc_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("cxx_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("f77_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("fc_pic_flag", "-fPIC", "oneapi@=2020.8.0.0827")
supported_flag_test("stdcxx_libs", ("-cxxlib",), "oneapi@=2020.8.0.0827")
supported_flag_test(
"debug_flags", ["-debug", "-g", "-g0", "-g1", "-g2", "-g3"], "oneapi@=2020.8.0.0827"
)
supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-Ofast", "-Os"], "oneapi@=2020.8.0.0827"
)
def test_nag_flags():
supported_flag_test("openmp_flag", "-openmp", "nag@=1.0")
supported_flag_test("cxx11_flag", "-std=c++11", "nag@=1.0")
supported_flag_test("cc_pic_flag", "-fPIC", "nag@=1.0")
supported_flag_test("cxx_pic_flag", "-fPIC", "nag@=1.0")
supported_flag_test("f77_pic_flag", "-PIC", "nag@=1.0")
supported_flag_test("fc_pic_flag", "-PIC", "nag@=1.0")
supported_flag_test("cc_rpath_arg", "-Wl,-rpath,", "nag@=1.0")
supported_flag_test("cxx_rpath_arg", "-Wl,-rpath,", "nag@=1.0")
supported_flag_test("f77_rpath_arg", "-Wl,-Wl,,-rpath,,", "nag@=1.0")
supported_flag_test("fc_rpath_arg", "-Wl,-Wl,,-rpath,,", "nag@=1.0")
supported_flag_test("linker_arg", "-Wl,-Wl,,", "nag@=1.0")
supported_flag_test("debug_flags", ["-g", "-gline", "-g90"], "nag@=1.0")
supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "nag@=1.0")
def test_nvhpc_flags():
supported_flag_test("openmp_flag", "-mp", "nvhpc@=20.9")
supported_flag_test("cxx11_flag", "--c++11", "nvhpc@=20.9")
supported_flag_test("cxx14_flag", "--c++14", "nvhpc@=20.9")
supported_flag_test("cxx17_flag", "--c++17", "nvhpc@=20.9")
supported_flag_test("c99_flag", "-c99", "nvhpc@=20.9")
supported_flag_test("c11_flag", "-c11", "nvhpc@=20.9")
supported_flag_test("cc_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("cxx_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("f77_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("fc_pic_flag", "-fpic", "nvhpc@=20.9")
supported_flag_test("debug_flags", ["-g", "-gopt"], "nvhpc@=20.9")
supported_flag_test("opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4"], "nvhpc@=20.9")
supported_flag_test("stdcxx_libs", ("-c++libs",), "nvhpc@=20.9")
def test_xl_flags():
supported_flag_test("openmp_flag", "-qsmp=omp", "xl@=1.0")
unsupported_flag_test("cxx11_flag", "xl@=13.0")
supported_flag_test("cxx11_flag", "-qlanglvl=extended0x", "xl@=13.1")
unsupported_flag_test("c99_flag", "xl@=10.0")
supported_flag_test("c99_flag", "-qlanglvl=extc99", "xl@=10.1")
supported_flag_test("c99_flag", "-std=gnu99", "xl@=13.1.1")
unsupported_flag_test("c11_flag", "xl@=12.0")
supported_flag_test("c11_flag", "-qlanglvl=extc1x", "xl@=12.1")
supported_flag_test("c11_flag", "-std=gnu11", "xl@=13.1.2")
supported_flag_test("cc_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("cxx_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("f77_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("fc_pic_flag", "-qpic", "xl@=1.0")
supported_flag_test("fflags", "-qzerosize", "xl@=1.0")
supported_flag_test("debug_flags", ["-g", "-g0", "-g1", "-g2", "-g8", "-g9"], "xl@=1.0")
supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4", "-O5", "-Ofast"], "xl@=1.0"
)
def test_xl_r_flags():
supported_flag_test("openmp_flag", "-qsmp=omp", "xl_r@=1.0")
unsupported_flag_test("cxx11_flag", "xl_r@=13.0")
supported_flag_test("cxx11_flag", "-qlanglvl=extended0x", "xl_r@=13.1")
unsupported_flag_test("c99_flag", "xl_r@=10.0")
supported_flag_test("c99_flag", "-qlanglvl=extc99", "xl_r@=10.1")
supported_flag_test("c99_flag", "-std=gnu99", "xl_r@=13.1.1")
unsupported_flag_test("c11_flag", "xl_r@=12.0")
supported_flag_test("c11_flag", "-qlanglvl=extc1x", "xl_r@=12.1")
supported_flag_test("c11_flag", "-std=gnu11", "xl_r@=13.1.2")
supported_flag_test("cc_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("cxx_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("f77_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("fc_pic_flag", "-qpic", "xl_r@=1.0")
supported_flag_test("fflags", "-qzerosize", "xl_r@=1.0")
supported_flag_test("debug_flags", ["-g", "-g0", "-g1", "-g2", "-g8", "-g9"], "xl@=1.0")
supported_flag_test(
"opt_flags", ["-O", "-O0", "-O1", "-O2", "-O3", "-O4", "-O5", "-Ofast"], "xl@=1.0"
)
# FIXME (compiler as nodes): revisit this test
# @pytest.mark.regression("14798,13733")
# def test_raising_if_compiler_target_is_over_specific(config):
# # Compiler entry with an overly specific target
# compilers = [
# {
# "compiler": {
# "spec": "gcc@9.0.1",
# "paths": {
# "cc": "/usr/bin/gcc-9",
# "cxx": "/usr/bin/g++-9",
# "f77": "/usr/bin/gfortran-9",
# "fc": "/usr/bin/gfortran-9",
# },
# "flags": {},
# "operating_system": "ubuntu18.04",
# "target": "haswell",
# "modules": [],
# "environment": {},
# "extra_rpaths": [],
# }
# }
# ]
# arch_spec = spack.spec.ArchSpec(("linux", "ubuntu18.04", "haswell"))
# with spack.config.override("compilers", compilers):
# cfg = spack.compilers.get_compiler_config(config)
# with pytest.raises(ValueError):
# spack.compilers.get_compilers(cfg, spack.spec.CompilerSpec("gcc@9.0.1"), arch_spec)
# FIXME (compiler as nodes): revisit this test
# @pytest.mark.regression("42679")
# def test_get_compilers(config):
# """Tests that we can select compilers whose versions differ only for a suffix."""
# common = {
# "flags": {},
# "operating_system": "ubuntu23.10",
# "target": "x86_64",
# "modules": [],
# "environment": {},
# "extra_rpaths": [],
# }
# with_suffix = {
# "spec": "gcc@13.2.0-suffix",
# "paths": {
# "cc": "/usr/bin/gcc-13.2.0-suffix",
# "cxx": "/usr/bin/g++-13.2.0-suffix",
# "f77": "/usr/bin/gfortran-13.2.0-suffix",
# "fc": "/usr/bin/gfortran-13.2.0-suffix",
# },
# **common,
# }
# without_suffix = {
# "spec": "gcc@13.2.0",
# "paths": {
# "cc": "/usr/bin/gcc-13.2.0",
# "cxx": "/usr/bin/g++-13.2.0",
# "f77": "/usr/bin/gfortran-13.2.0",
# "fc": "/usr/bin/gfortran-13.2.0",
# },
# **common,
# }
#
# compilers = [{"compiler": without_suffix}, {"compiler": with_suffix}]
#
# assert spack.compilers.get_compilers(
# compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0-suffix")
# ) == [spack.compilers._compiler_from_config_entry(with_suffix)]
#
# assert spack.compilers.get_compilers(
# compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0")
# ) == [spack.compilers._compiler_from_config_entry(without_suffix)]
@pytest.mark.enable_compiler_verification
def test_compiler_executable_verification_raises(tmpdir):
compiler = MockCompiler()
compiler.cc = "/this/path/does/not/exist"
with pytest.raises(spack.compiler.CompilerAccessError):
compiler.verify_executables()
@pytest.mark.enable_compiler_verification
def test_compiler_executable_verification_success(tmpdir):
def prepare_executable(name):
real = str(tmpdir.join("cc").ensure())
fs.set_executable(real)
setattr(compiler, name, real)
# setup mock compiler with real paths
compiler = MockCompiler()
for name in ("cc", "cxx", "f77", "fc"):
prepare_executable(name)
# testing that this doesn't raise an error because the paths exist and
# are executable
compiler.verify_executables()
# Test that null entries don't fail
compiler.cc = None
compiler.verify_executables()
@pytest.mark.parametrize(
"compilers_extra_attributes,expected_length",
[
# If we detect a C compiler we expect the result to be valid
({"c": "/usr/bin/clang-12", "cxx": "/usr/bin/clang-12"}, 1),
# If we detect only a C++ compiler we expect the result to be discarded
({"cxx": "/usr/bin/clang-12"}, 0),
],
)
def test_detection_requires_c_compiler(compilers_extra_attributes, expected_length):
"""Tests that compilers automatically added to the configuration have
at least a C compiler.
"""
packages_yaml = {
"llvm": {
"externals": [
{
"spec": "clang@12.0.0",
"prefix": "/usr",
"extra_attributes": {"compilers": compilers_extra_attributes},
}
]
}
}
result = spack.compilers.CompilerFactory.from_packages_yaml(packages_yaml)
assert len(result) == expected_length
def test_compiler_environment(working_env):
"""Test whether environment modifications from compilers are applied in compiler_environment"""
os.environ.pop("TEST", None)
compiler = Compiler(
"gcc@=13.2.0",
operating_system="ubuntu20.04",
target="x86_64",
paths=["/test/bin/gcc", "/test/bin/g++"],
environment={"set": {"TEST": "yes"}},
)
with compiler.compiler_environment():
assert os.environ["TEST"] == "yes"
class MockCompilerWithoutExecutables(MockCompiler):
def __init__(self):
super().__init__()
self._compile_dummy_c_source_count = 0
self._get_real_version_count = 0
def _compile_dummy_c_source(self) -> Optional[str]:
self._compile_dummy_c_source_count += 1
return "gcc helloworld.c -o helloworld"
def get_real_version(self) -> str:
self._get_real_version_count += 1
return "1.0.0"
def test_compiler_output_caching(tmp_path):
"""Test that compiler output is cached on the filesystem."""
# The first call should trigger the cache to updated.
a = MockCompilerWithoutExecutables()
cache = spack.compiler.FileCompilerCache(FileCache(str(tmp_path)))
assert cache.get(a).c_compiler_output == "gcc helloworld.c -o helloworld"
assert cache.get(a).real_version == "1.0.0"
assert a._compile_dummy_c_source_count == 1
assert a._get_real_version_count == 1
# The second call on an equivalent but distinct object should not trigger compiler calls.
b = MockCompilerWithoutExecutables()
cache = spack.compiler.FileCompilerCache(FileCache(str(tmp_path)))
assert cache.get(b).c_compiler_output == "gcc helloworld.c -o helloworld"
assert cache.get(b).real_version == "1.0.0"
assert b._compile_dummy_c_source_count == 0
assert b._get_real_version_count == 0
# Cache schema change should be handled gracefully.
with open(cache.cache.cache_path(cache.name), "w", encoding="utf-8") as f:
for k in cache._data:
cache._data[k] = "corrupted entry"
f.write(json.dumps(cache._data))
c = MockCompilerWithoutExecutables()
cache = spack.compiler.FileCompilerCache(FileCache(str(tmp_path)))
assert cache.get(c).c_compiler_output == "gcc helloworld.c -o helloworld"
assert cache.get(c).real_version == "1.0.0"
# Cache corruption should be handled gracefully.
with open(cache.cache.cache_path(cache.name), "w", encoding="utf-8") as f:
f.write("corrupted cache")
d = MockCompilerWithoutExecutables()
cache = spack.compiler.FileCompilerCache(FileCache(str(tmp_path)))
assert cache.get(d).c_compiler_output == "gcc helloworld.c -o helloworld"
assert cache.get(d).real_version == "1.0.0"