summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@gmail.com>2021-09-13 20:04:42 +0200
committerGitHub <noreply@github.com>2021-09-13 11:04:42 -0700
commite9f1cfdaaf0638d5f7bc26a2af7e757ec2f45e96 (patch)
tree870f94ce2f4424145fc911276aafb0c582febd7c
parent060582a21d3ebabf5d6b1eab829638e0865c8ba5 (diff)
downloadspack-e9f1cfdaaf0638d5f7bc26a2af7e757ec2f45e96.tar.gz
spack-e9f1cfdaaf0638d5f7bc26a2af7e757ec2f45e96.tar.bz2
spack-e9f1cfdaaf0638d5f7bc26a2af7e757ec2f45e96.tar.xz
spack-e9f1cfdaaf0638d5f7bc26a2af7e757ec2f45e96.zip
Avoid hidden circular dependencies in spack.architecture (#25873)
* Refactor platform etc. to avoid circular dependencies All the base classes in spack.architecture have been moved to the corresponding specialized subpackages, e.g. Platform is now defined within spack.platforms. This resolves a circular dependency where spack.architecture was both: - Defining the base classes for spack.platforms, etc. - Collecting derived classes from spack.platforms, etc. Now it dopes only the latter. * Move a few platform related functions to "spack.platforms" * Removed spack.architecture.sys_type() * Fixup for docs * Rename Python modules according to review
-rw-r--r--lib/spack/docs/developer_guide.rst2
-rw-r--r--lib/spack/spack/architecture.py457
-rw-r--r--lib/spack/spack/binary_distribution.py5
-rw-r--r--lib/spack/spack/compilers/__init__.py2
-rw-r--r--lib/spack/spack/concretize.py13
-rw-r--r--lib/spack/spack/main.py2
-rw-r--r--lib/spack/spack/operating_systems/__init__.py16
-rw-r--r--lib/spack/spack/operating_systems/_operating_system.py36
-rw-r--r--lib/spack/spack/operating_systems/cray_backend.py3
-rw-r--r--lib/spack/spack/operating_systems/cray_frontend.py3
-rw-r--r--lib/spack/spack/operating_systems/linux_distro.py3
-rw-r--r--lib/spack/spack/operating_systems/mac_os.py3
-rw-r--r--lib/spack/spack/platforms/__init__.py40
-rw-r--r--lib/spack/spack/platforms/_platform.py126
-rw-r--r--lib/spack/spack/platforms/cray.py10
-rw-r--r--lib/spack/spack/platforms/darwin.py6
-rw-r--r--lib/spack/spack/platforms/linux.py6
-rw-r--r--lib/spack/spack/platforms/test.py14
-rw-r--r--lib/spack/spack/spec.py32
-rw-r--r--lib/spack/spack/target.py162
-rw-r--r--lib/spack/spack/test/architecture.py16
-rw-r--r--lib/spack/spack/test/concretize.py6
-rw-r--r--var/spack/repos/builtin/packages/pmix/package.py7
23 files changed, 492 insertions, 478 deletions
diff --git a/lib/spack/docs/developer_guide.rst b/lib/spack/docs/developer_guide.rst
index 71aac1fd93..cc77666846 100644
--- a/lib/spack/docs/developer_guide.rst
+++ b/lib/spack/docs/developer_guide.rst
@@ -211,7 +211,7 @@ Spec-related modules
yet.
:mod:`spack.architecture`
- :func:`architecture.sys_type <spack.architecture.sys_type>` is used
+ :func:`architecture.default_arch <spack.architecture.default_arch>` is used
to determine the host architecture while building.
.. warning::
diff --git a/lib/spack/spack/architecture.py b/lib/spack/spack/architecture.py
index c9217e0941..0ae99959bd 100644
--- a/lib/spack/spack/architecture.py
+++ b/lib/spack/spack/architecture.py
@@ -2,28 +2,24 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-"""
-This module contains all the elements that are required to create an
-architecture object. These include, the target processor, the operating system,
-and the architecture platform (i.e. cray, darwin, linux, etc) classes.
+"""Aggregate the target processor, the operating system and the target
+platform into an architecture object.
On a multiple architecture machine, the architecture spec field can be set to
build a package against any target and operating system that is present on the
platform. On Cray platforms or any other architecture that has different front
and back end environments, the operating system will determine the method of
-compiler
-detection.
+compiler detection.
There are two different types of compiler detection:
+
1. Through the $PATH env variable (front-end detection)
- 2. Through the tcl module system. (back-end detection)
+ 2. Through the module system. (back-end detection)
Depending on which operating system is specified, the compiler will be detected
using one of those methods.
-For platforms such as linux and darwin, the operating system is autodetected
-and the target is set to be x86_64.
+For platforms such as linux and darwin, the operating system is autodetected.
The command line syntax for specifying an architecture is as follows:
@@ -33,10 +29,8 @@ If the user wishes to use the defaults, either target or os can be left out of
the command line and Spack will concretize using the default. These defaults
are set in the 'platforms/' directory which contains the different subclasses
for platforms. If the machine has multiple architectures, the user can
-also enter front-end, or fe or back-end or be. These settings will concretize
-to their respective front-end and back-end targets and operating systems.
-Additional platforms can be added by creating a subclass of Platform
-and adding it inside the platform directory.
+also enter frontend, or fe or backend or be. These settings will concretize
+to their respective frontend and backend targets and operating systems.
Platforms are an abstract class that are extended by subclasses. If the user
wants to add a new type of platform (such as cray_xe), they can create a
@@ -47,334 +41,30 @@ arbitrarily set and can be changed though often there isn't much need unless a
new platform is added and the user wants that to be detected first.
Targets are created inside the platform subclasses. Most architecture
-(like linux, and darwin) will have only one target (x86_64) but in the case of
+(like linux, and darwin) will have only one target family (x86_64) but in the case of
Cray machines, there is both a frontend and backend processor. The user can
specify which targets are present on front-end and back-end architecture
-Depending on the platform, operating systems are either auto-detected or are
-set. The user can set the front-end and back-end operating setting by the class
+Depending on the platform, operating systems are either autodetected or are
+set. The user can set the frontend and backend operating setting by the class
attributes front_os and back_os. The operating system as described earlier,
will be responsible for compiler detection.
"""
import contextlib
-import functools
-import warnings
-
-import six
import archspec.cpu
import llnl.util.lang as lang
-import llnl.util.tty as tty
import spack.compiler
import spack.compilers
import spack.config
-import spack.error as serr
-import spack.paths
+import spack.operating_systems
+import spack.platforms
import spack.spec
-import spack.util.classes
-import spack.util.executable
+import spack.target
+import spack.util.spack_yaml as syaml
import spack.version
-from spack.util.spack_yaml import syaml_dict
-
-
-class NoPlatformError(serr.SpackError):
- def __init__(self):
- super(NoPlatformError, self).__init__(
- "Could not determine a platform for this machine.")
-
-
-def _ensure_other_is_target(method):
- """Decorator to be used in dunder methods taking a single argument to
- ensure that the argument is an instance of ``Target`` too.
- """
- @functools.wraps(method)
- def _impl(self, other):
- if isinstance(other, six.string_types):
- other = Target(other)
-
- if not isinstance(other, Target):
- return NotImplemented
-
- return method(self, other)
-
- return _impl
-
-
-class Target(object):
- def __init__(self, name, module_name=None):
- """Target models microarchitectures and their compatibility.
-
- Args:
- name (str or Microarchitecture):micro-architecture of the
- target
- module_name (str): optional module name to get access to the
- current target. This is typically used on machines
- like Cray (e.g. craype-compiler)
- """
- if not isinstance(name, archspec.cpu.Microarchitecture):
- name = archspec.cpu.TARGETS.get(
- name, archspec.cpu.generic_microarchitecture(name)
- )
- self.microarchitecture = name
- self.module_name = module_name
-
- @property
- def name(self):
- return self.microarchitecture.name
-
- @_ensure_other_is_target
- def __eq__(self, other):
- return self.microarchitecture == other.microarchitecture and \
- self.module_name == other.module_name
-
- def __ne__(self, other):
- # This method is necessary as long as we support Python 2. In Python 3
- # __ne__ defaults to the implementation below
- return not self == other
-
- @_ensure_other_is_target
- def __lt__(self, other):
- # TODO: In the future it would be convenient to say
- # TODO: `spec.architecture.target < other.architecture.target`
- # TODO: and change the semantic of the comparison operators
-
- # This is needed to sort deterministically specs in a list.
- # It doesn't implement a total ordering semantic.
- return self.microarchitecture.name < other.microarchitecture.name
-
- def __hash__(self):
- return hash((self.name, self.module_name))
-
- @staticmethod
- def from_dict_or_value(dict_or_value):
- # A string here represents a generic target (like x86_64 or ppc64) or
- # a custom micro-architecture
- if isinstance(dict_or_value, six.string_types):
- return Target(dict_or_value)
-
- # TODO: From a dict we actually retrieve much more information than
- # TODO: just the name. We can use that information to reconstruct an
- # TODO: "old" micro-architecture or check the current definition.
- target_info = dict_or_value
- return Target(target_info['name'])
-
- def to_dict_or_value(self):
- """Returns a dict or a value representing the current target.
-
- String values are used to keep backward compatibility with generic
- targets, like e.g. x86_64 or ppc64. More specific micro-architectures
- will return a dictionary which contains information on the name,
- features, vendor, generation and parents of the current target.
- """
- # Generic targets represent either an architecture
- # family (like x86_64) or a custom micro-architecture
- if self.microarchitecture.vendor == 'generic':
- return str(self)
-
- return syaml_dict(
- self.microarchitecture.to_dict(return_list_of_items=True)
- )
-
- def __repr__(self):
- cls_name = self.__class__.__name__
- fmt = cls_name + '({0}, {1})'
- return fmt.format(repr(self.microarchitecture),
- repr(self.module_name))
-
- def __str__(self):
- return str(self.microarchitecture)
-
- def __contains__(self, cpu_flag):
- return cpu_flag in self.microarchitecture
-
- def optimization_flags(self, compiler):
- """Returns the flags needed to optimize for this target using
- the compiler passed as argument.
-
- Args:
- compiler (spack.spec.CompilerSpec or spack.compiler.Compiler): object that
- contains both the name and the version of the compiler we want to use
- """
- # Mixed toolchains are not supported yet
- if isinstance(compiler, spack.compiler.Compiler):
- if spack.compilers.is_mixed_toolchain(compiler):
- msg = ('microarchitecture specific optimizations are not '
- 'supported yet on mixed compiler toolchains [check'
- ' {0.name}@{0.version} for further details]')
- warnings.warn(msg.format(compiler))
- return ''
-
- # Try to check if the current compiler comes with a version number or
- # has an unexpected suffix. If so, treat it as a compiler with a
- # custom spec.
- compiler_version = compiler.version
- version_number, suffix = archspec.cpu.version_components(
- compiler.version
- )
- if not version_number or suffix not in ('', 'apple'):
- # Try to deduce the underlying version of the compiler, regardless
- # of its name in compilers.yaml. Depending on where this function
- # is called we might get either a CompilerSpec or a fully fledged
- # compiler object.
- if isinstance(compiler, spack.spec.CompilerSpec):
- compiler = spack.compilers.compilers_for_spec(compiler).pop()
- try:
- compiler_version = compiler.real_version
- except spack.util.executable.ProcessError as e:
- # log this and just return compiler.version instead
- tty.debug(str(e))
-
- return self.microarchitecture.optimization_flags(
- compiler.name, str(compiler_version)
- )
-
-
-@lang.lazy_lexicographic_ordering
-class Platform(object):
- """ Abstract class that each type of Platform will subclass.
- Will return a instance of it once it is returned.
- """
-
- # Subclass sets number. Controls detection order
- priority = None # type: int
-
- #: binary formats used on this platform; used by relocation logic
- binary_formats = ['elf']
-
- front_end = None # type: str
- back_end = None # type: str
- default = None # type: str # The default back end target.
-
- front_os = None # type: str
- back_os = None # type: str
- default_os = None # type: str
-
- reserved_targets = ['default_target', 'frontend', 'fe', 'backend', 'be']
- reserved_oss = ['default_os', 'frontend', 'fe', 'backend', 'be']
-
- def __init__(self, name):
- self.targets = {}
- self.operating_sys = {}
- self.name = name
-
- def add_target(self, name, target):
- """Used by the platform specific subclass to list available targets.
- Raises an error if the platform specifies a name
- that is reserved by spack as an alias.
- """
- if name in Platform.reserved_targets:
- raise ValueError(
- "%s is a spack reserved alias "
- "and cannot be the name of a target" % name)
- self.targets[name] = target
-
- def target(self, name):
- """This is a getter method for the target dictionary
- that handles defaulting based on the values provided by default,
- front-end, and back-end. This can be overwritten
- by a subclass for which we want to provide further aliasing options.
- """
- # TODO: Check if we can avoid using strings here
- name = str(name)
- if name == 'default_target':
- name = self.default
- elif name == 'frontend' or name == 'fe':
- name = self.front_end
- elif name == 'backend' or name == 'be':
- name = self.back_end
-
- return self.targets.get(name, None)
-
- def add_operating_system(self, name, os_class):
- """ Add the operating_system class object into the
- platform.operating_sys dictionary
- """
- if name in Platform.reserved_oss:
- raise ValueError(
- "%s is a spack reserved alias "
- "and cannot be the name of an OS" % name)
- self.operating_sys[name] = os_class
-
- def operating_system(self, name):
- if name == 'default_os':
- name = self.default_os
- if name == 'frontend' or name == "fe":
- name = self.front_os
- if name == 'backend' or name == 'be':
- name = self.back_os
-
- return self.operating_sys.get(name, None)
-
- @classmethod
- def setup_platform_environment(cls, pkg, env):
- """ Subclass can override this method if it requires any
- platform-specific build environment modifications.
- """
-
- @classmethod
- def detect(cls):
- """ Subclass is responsible for implementing this method.
- Returns True if the Platform class detects that
- it is the current platform
- and False if it's not.
- """
- raise NotImplementedError()
-
- def __repr__(self):
- return self.__str__()
-
- def __str__(self):
- return self.name
-
- def _cmp_iter(self):
- yield self.name
- yield self.default
- yield self.front_end
- yield self.back_end
- yield self.default_os
- yield self.front_os
- yield self.back_os
-
- def targets():
- for t in sorted(self.targets.values()):
- yield t._cmp_iter
- yield targets
-
- def oses():
- for o in sorted(self.operating_sys.values()):
- yield o._cmp_iter
- yield oses
-
-
-@lang.lazy_lexicographic_ordering
-class OperatingSystem(object):
- """ Operating System will be like a class similar to platform extended
- by subclasses for the specifics. Operating System will contain the
- compiler finding logic. Instead of calling two separate methods to
- find compilers we call find_compilers method for each operating system
- """
-
- def __init__(self, name, version):
- self.name = name.replace('-', '_')
- self.version = str(version).replace('-', '_')
-
- def __str__(self):
- return "%s%s" % (self.name, self.version)
-
- def __repr__(self):
- return self.__str__()
-
- def _cmp_iter(self):
- yield self.name
- yield self.version
-
- def to_dict(self):
- return syaml_dict([
- ('name', self.name),
- ('version', self.version)
- ])
@lang.lazy_lexicographic_ordering
@@ -399,11 +89,13 @@ class Arch(object):
@property
def concrete(self):
- return all((self.platform is not None,
- isinstance(self.platform, Platform),
- self.os is not None,
- isinstance(self.os, OperatingSystem),
- self.target is not None, isinstance(self.target, Target)))
+ return all(
+ (self.platform is not None,
+ isinstance(self.platform, spack.platforms.Platform),
+ self.os is not None,
+ isinstance(self.os, spack.operating_systems.OperatingSystem),
+ self.target is not None, isinstance(self.target, spack.target.Target))
+ )
def __str__(self):
if self.platform or self.os or self.target:
@@ -428,28 +120,28 @@ class Arch(object):
__bool__ = __nonzero__
def _cmp_iter(self):
- if isinstance(self.platform, Platform):
+ if isinstance(self.platform, spack.platforms.Platform):
yield self.platform.name
else:
yield self.platform
- if isinstance(self.os, OperatingSystem):
+ if isinstance(self.os, spack.operating_systems.OperatingSystem):
yield self.os.name
else:
yield self.os
- if isinstance(self.target, Target):
+ if isinstance(self.target, spack.target.Target):
yield self.target.microarchitecture
else:
yield self.target
def to_dict(self):
str_or_none = lambda v: str(v) if v else None
- d = syaml_dict([
+ d = syaml.syaml_dict([
('platform', str_or_none(self.platform)),
('platform_os', str_or_none(self.os)),
('target', self.target.to_dict_or_value())])
- return syaml_dict([('arch', d)])
+ return syaml.syaml_dict([('arch', d)])
def to_spec(self):
"""Convert this Arch to an anonymous Spec with architecture defined."""
@@ -463,64 +155,25 @@ class Arch(object):
return arch_for_spec(spec)
-@lang.memoized
-def get_platform(platform_name):
- """Returns a platform object that corresponds to the given name."""
- platform_list = all_platforms()
- for p in platform_list:
- if platform_name.replace("_", "").lower() == p.__name__.lower():
- return p()
-
-
-def verify_platform(platform_name):
- """ Determines whether or not the platform with the given name is supported
- in Spack. For more information, see the 'spack.platforms' submodule.
- """
- platform_name = platform_name.replace("_", "").lower()
- platform_names = [p.__name__.lower() for p in all_platforms()]
-
- if platform_name not in platform_names:
- tty.die("%s is not a supported platform; supported platforms are %s" %
- (platform_name, platform_names))
-
-
def arch_for_spec(arch_spec):
"""Transforms the given architecture spec into an architecture object."""
arch_spec = spack.spec.ArchSpec(arch_spec)
assert arch_spec.concrete
- arch_plat = get_platform(arch_spec.platform)
+ arch_plat = spack.platforms.by_name(arch_spec.platform)
if not (arch_plat.operating_system(arch_spec.os) and
arch_plat.target(arch_spec.target)):
- raise ValueError(
- "Can't recreate arch for spec %s on current arch %s; "
- "spec architecture is too different" % (arch_spec, sys_type()))
+ sys_type = str(default_arch())
+ msg = ("Can't recreate arch for spec {0} on current arch {1}; "
+ "spec architecture is too different")
+ raise ValueError(msg.format(arch_spec, sys_type))
return Arch(arch_plat, arch_spec.os, arch_spec.target)
@lang.memoized
-def _all_platforms():
- mod_path = spack.paths.platform_path
- return spack.util.classes.list_classes("spack.platforms", mod_path)
-
-
-@lang.memoized
def _platform():
- """Detects the platform for this machine.
-
- Gather a list of all available subclasses of platforms.
- Sorts the list according to their priority looking. Priority is
- an arbitrarily set number. Detects platform either using uname or
- a file path (/opt/cray...)
- """
- # Try to create a Platform object using the config file FIRST
- platform_list = _all_platforms()
- platform_list.sort(key=lambda a: a.priority)
-
- for platform_cls in platform_list:
- if platform_cls.detect():
- return platform_cls()
+ return spack.platforms.host()
#: The "real" platform of the host running Spack. This should not be changed
@@ -531,44 +184,23 @@ real_platform = _platform
#: context manager.
platform = _platform
-#: The list of all platform classes. May be swapped by the use_platform
-#: context manager.
-all_platforms = _all_platforms
-
@lang.memoized
def default_arch():
- """Default ``Arch`` object for this machine.
-
- See ``sys_type()``.
- """
+ """Default ``Arch`` object for this machine"""
return Arch(platform(), 'default_os', 'default_target')
-def sys_type():
- """Print out the "default" platform-os-target tuple for this machine.
-
- On machines with only one target OS/target, prints out the
- platform-os-target for the frontend. For machines with a frontend
- and a backend, prints the default backend.
-
- TODO: replace with use of more explicit methods to get *all* the
- backends, as client code should really be aware of cross-compiled
- architectures.
-
- """
- return str(default_arch())
-
-
@lang.memoized
def compatible_sys_types():
- """Returns a list of all the systypes compatible with the current host."""
- compatible_archs = []
+ """Return a list of all the platform-os-target tuples compatible
+ with the current host.
+ """
current_host = archspec.cpu.host()
compatible_targets = [current_host] + current_host.ancestors
- for target in compatible_targets:
- arch = Arch(platform(), 'default_os', target)
- compatible_archs.append(str(arch))
+ compatible_archs = [
+ str(Arch(platform(), 'default_os', target)) for target in compatible_targets
+ ]
return compatible_archs
@@ -586,16 +218,15 @@ class _PickleableCallable(object):
@contextlib.contextmanager
def use_platform(new_platform):
- global platform, all_platforms
+ global platform
msg = '"{0}" must be an instance of Platform'
- assert isinstance(new_platform, Platform), msg.format(new_platform)
+ assert isinstance(new_platform, spack.platforms.Platform), msg.format(new_platform)
- original_platform_fn, original_all_platforms_fn = platform, all_platforms
+ original_platform_fn = platform
try:
platform = _PickleableCallable(new_platform)
- all_platforms = _PickleableCallable([type(new_platform)])
# Clear configuration and compiler caches
spack.config.config.clear_caches()
@@ -604,7 +235,7 @@ def use_platform(new_platform):
yield new_platform
finally:
- platform, all_platforms = original_platform_fn, original_all_platforms_fn
+ platform = original_platform_fn
# Clear configuration and compiler caches
spack.config.config.clear_caches()
diff --git a/lib/spack/spack/binary_distribution.py b/lib/spack/spack/binary_distribution.py
index 3323e9d71a..c8e866ff14 100644
--- a/lib/spack/spack/binary_distribution.py
+++ b/lib/spack/spack/binary_distribution.py
@@ -31,6 +31,7 @@ import spack.fetch_strategy as fs
import spack.hash_types as ht
import spack.hooks.sbang
import spack.mirror
+import spack.platforms
import spack.relocate as relocate
import spack.util.file_cache as file_cache
import spack.util.gpg
@@ -1156,7 +1157,7 @@ def make_package_relative(workdir, spec, allow_root):
orig_path_names.append(os.path.join(prefix, filename))
cur_path_names.append(os.path.join(workdir, filename))
- platform = spack.architecture.get_platform(spec.platform)
+ platform = spack.platforms.by_name(spec.platform)
if 'macho' in platform.binary_formats:
relocate.make_macho_binaries_relative(
cur_path_names, orig_path_names, old_layout_root)
@@ -1267,7 +1268,7 @@ def relocate_package(spec, allow_root):
]
# If the buildcache was not created with relativized rpaths
# do the relocation of path in binaries
- platform = spack.architecture.get_platform(spec.platform)
+ platform = spack.platforms.by_name(spec.platform)
if 'macho' in platform.binary_formats:
relocate.relocate_macho_binaries(files_to_relocate,
old_layout_root,
diff --git a/lib/spack/spack/compilers/__init__.py b/lib/spack/spack/compilers/__init__.py
index d60ab971bc..7959dad25b 100644
--- a/lib/spack/spack/compilers/__init__.py
+++ b/lib/spack/spack/compilers/__init__.py
@@ -544,7 +544,7 @@ def arguments_to_detect_version_fn(operating_system, paths):
function by providing a method called with the same name.
Args:
- operating_system (spack.architecture.OperatingSystem): the operating system
+ operating_system (spack.operating_systems.OperatingSystem): the operating system
on which we are looking for compilers
paths: paths to search for compilers
diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py
index 2e6e067c9d..fe7bf6406d 100644
--- a/lib/spack/spack/concretize.py
+++ b/lib/spack/spack/concretize.py
@@ -37,6 +37,7 @@ import spack.environment
import spack.error
import spack.repo
import spack.spec
+import spack.target
import spack.tengine
import spack.variant as vt
from spack.config import config
@@ -256,8 +257,7 @@ class Concretizer(object):
# Get platform of nearest spec with a platform, including spec
# If spec has a platform, easy
if spec.architecture.platform:
- new_plat = spack.architecture.get_platform(
- spec.architecture.platform)
+ new_plat = spack.platforms.by_name(spec.architecture.platform)
else:
# Else if anyone else has a platform, take the closest one
# Search up, then down, along build/link deps first
@@ -266,8 +266,7 @@ class Concretizer(object):
spec, lambda x: x.architecture and x.architecture.platform
)
if platform_spec:
- new_plat = spack.architecture.get_platform(
- platform_spec.architecture.platform)
+ new_plat = spack.platforms.by_name(platform_spec.architecture.platform)
else:
# If no platform anywhere in this spec, grab the default
new_plat = spack.architecture.platform()
@@ -612,9 +611,7 @@ class Concretizer(object):
# Try to adjust the target only if it is the default
# target for this platform
current_target = spec.architecture.target
- current_platform = spack.architecture.get_platform(
- spec.architecture.platform
- )
+ current_platform = spack.platforms.by_name(spec.architecture.platform)
default_target = current_platform.target('default_target')
if PackagePrefs.has_preferred_targets(spec.name):
@@ -633,7 +630,7 @@ class Concretizer(object):
for ancestor in microarchitecture.ancestors:
candidate = None
try:
- candidate = spack.architecture.Target(ancestor)
+ candidate = spack.target.Target(ancestor)
candidate.optimization_flags(spec.compiler)
except archspec.cpu.UnsupportedMicroarchitecture:
continue
diff --git a/lib/spack/spack/main.py b/lib/spack/spack/main.py
index ee5b3d5343..8ead8c8f4e 100644
--- a/lib/spack/spack/main.py
+++ b/lib/spack/spack/main.py
@@ -666,7 +666,7 @@ def print_setup_info(*info):
tty.die('shell must be sh or csh')
# print sys type
- shell_set('_sp_sys_type', spack.architecture.sys_type())
+ shell_set('_sp_sys_type', str(spack.architecture.default_arch()))
shell_set('_sp_compatible_sys_types',
':'.join(spack.architecture.compatible_sys_types()))
# print roots for all module systems
diff --git a/lib/spack/spack/operating_systems/__init__.py b/lib/spack/spack/operating_systems/__init__.py
index 103eae6134..bd00df6b47 100644
--- a/lib/spack/spack/operating_systems/__init__.py
+++ b/lib/spack/spack/operating_systems/__init__.py
@@ -2,3 +2,19 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+from ._operating_system import OperatingSystem
+from .cray_backend import CrayBackend
+from .cray_frontend import CrayFrontend
+from .linux_distro import LinuxDistro
+from .mac_os import MacOs
+
+__all__ = [
+ 'OperatingSystem',
+ 'LinuxDistro',
+ 'MacOs',
+ 'CrayFrontend',
+ 'CrayBackend'
+]
+
+#: List of all the Operating Systems known to Spack
+operating_systems = [LinuxDistro, MacOs, CrayFrontend, CrayBackend]
diff --git a/lib/spack/spack/operating_systems/_operating_system.py b/lib/spack/spack/operating_systems/_operating_system.py
new file mode 100644
index 0000000000..d667dea4ab
--- /dev/null
+++ b/lib/spack/spack/operating_systems/_operating_system.py
@@ -0,0 +1,36 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import llnl.util.lang
+
+import spack.util.spack_yaml as syaml
+
+
+@llnl.util.lang.lazy_lexicographic_ordering
+class OperatingSystem(object):
+ """Base class for all the Operating Systems.
+
+ Each Operating System contain its own compiler finding logic, that is used
+ to detect compilers.
+ """
+
+ def __init__(self, name, version):
+ self.name = name.replace('-', '_')
+ self.version = str(version).replace('-', '_')
+
+ def __str__(self):
+ return "%s%s" % (self.name, self.version)
+
+ def __repr__(self):
+ return self.__str__()
+
+ def _cmp_iter(self):
+ yield self.name
+ yield self.version
+
+ def to_dict(self):
+ return syaml.syaml_dict([
+ ('name', self.name),
+ ('version', self.version)
+ ])
diff --git a/lib/spack/spack/operating_systems/cray_backend.py b/lib/spack/spack/operating_systems/cray_backend.py
index 0e59361d70..8f71de9bb4 100644
--- a/lib/spack/spack/operating_systems/cray_backend.py
+++ b/lib/spack/spack/operating_systems/cray_backend.py
@@ -10,9 +10,10 @@ import llnl.util.tty as tty
import spack.error
import spack.version
-from spack.operating_systems.linux_distro import LinuxDistro
from spack.util.module_cmd import module
+from .linux_distro import LinuxDistro
+
#: Possible locations of the Cray CLE release file,
#: which we look at to get the CNL OS version.
_cle_release_file = '/etc/opt/cray/release/cle-release'
diff --git a/lib/spack/spack/operating_systems/cray_frontend.py b/lib/spack/spack/operating_systems/cray_frontend.py
index 820db506cb..5b74033daf 100644
--- a/lib/spack/spack/operating_systems/cray_frontend.py
+++ b/lib/spack/spack/operating_systems/cray_frontend.py
@@ -11,10 +11,11 @@ import llnl.util.filesystem as fs
import llnl.util.lang
import llnl.util.tty as tty
-from spack.operating_systems.linux_distro import LinuxDistro
from spack.util.environment import get_path
from spack.util.module_cmd import module
+from .linux_distro import LinuxDistro
+
@contextlib.contextmanager
def unload_programming_environment():
diff --git a/lib/spack/spack/operating_systems/linux_distro.py b/lib/spack/spack/operating_systems/linux_distro.py
index fe9c00d55f..758e8bea47 100644
--- a/lib/spack/spack/operating_systems/linux_distro.py
+++ b/lib/spack/spack/operating_systems/linux_distro.py
@@ -2,10 +2,9 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import re
-from spack.architecture import OperatingSystem
+from ._operating_system import OperatingSystem
class LinuxDistro(OperatingSystem):
diff --git a/lib/spack/spack/operating_systems/mac_os.py b/lib/spack/spack/operating_systems/mac_os.py
index 180b2e6ac0..7fbeb8ff8f 100644
--- a/lib/spack/spack/operating_systems/mac_os.py
+++ b/lib/spack/spack/operating_systems/mac_os.py
@@ -5,10 +5,11 @@
import platform as py_platform
-from spack.architecture import OperatingSystem
from spack.util.executable import Executable
from spack.version import Version
+from ._operating_system import OperatingSystem
+
# FIXME: store versions inside OperatingSystem as a Version instead of string
def macos_version():
diff --git a/lib/spack/spack/platforms/__init__.py b/lib/spack/spack/platforms/__init__.py
index f401ad0e11..799b9c3171 100644
--- a/lib/spack/spack/platforms/__init__.py
+++ b/lib/spack/spack/platforms/__init__.py
@@ -2,14 +2,54 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import llnl.util.lang
+
+from ._platform import Platform
from .cray import Cray
from .darwin import Darwin
from .linux import Linux
from .test import Test
__all__ = [
+ 'Platform',
'Cray',
'Darwin',
'Linux',
'Test'
]
+
+#: List of all the platform classes known to Spack
+platforms = [Cray, Darwin, Linux, Test]
+
+
+def host():
+ """Detect and return the platform for this machine or None if detection fails."""
+ for platform_cls in sorted(platforms, key=lambda plt: plt.priority):
+ if platform_cls.detect():
+ return platform_cls()
+ return None
+
+
+@llnl.util.lang.memoized
+def cls_by_name(name):
+ """Return a platform class that corresponds to the given name or None
+ if there is no match.
+
+ Args:
+ name (str): name of the platform
+ """
+ for platform_cls in sorted(platforms, key=lambda plt: plt.priority):
+ if name.replace("_", "").lower() == platform_cls.__name__.lower():
+ return platform_cls
+ return None
+
+
+def by_name(name):
+ """Return a platform object that corresponds to the given name or None
+ if there is no match.
+
+ Args:
+ name (str): name of the platform
+ """
+ platform_cls = cls_by_name(name)
+ return platform_cls() if platform_cls else None
diff --git a/lib/spack/spack/platforms/_platform.py b/lib/spack/spack/platforms/_platform.py
new file mode 100644
index 0000000000..c2ee536b2b
--- /dev/null
+++ b/lib/spack/spack/platforms/_platform.py
@@ -0,0 +1,126 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import llnl.util.lang
+
+import spack.error
+
+
+class NoPlatformError(spack.error.SpackError):
+ def __init__(self):
+ msg = "Could not determine a platform for this machine"
+ super(NoPlatformError, self).__init__(msg)
+
+
+@llnl.util.lang.lazy_lexicographic_ordering
+class Platform(object):
+ """Base class for each type of Platform"""
+
+ # Subclass sets number. Controls detection order
+ priority = None # type: int
+
+ #: binary formats used on this platform; used by relocation logic
+ binary_formats = ['elf']
+
+ front_end = None # type: str
+ back_end = None # type: str
+ default = None # type: str # The default back end target.
+
+ front_os = None # type: str
+ back_os = None # type: str
+ default_os = None # type: str
+
+ reserved_targets = ['default_target', 'frontend', 'fe', 'backend', 'be']
+ reserved_oss = ['default_os', 'frontend', 'fe', 'backend', 'be']
+
+ def __init__(self, name):
+ self.targets = {}
+ self.operating_sys = {}
+ self.name = name
+
+ def add_target(self, name, target):
+ """Used by the platform specific subclass to list available targets.
+ Raises an error if the platform specifies a name
+ that is reserved by spack as an alias.
+ """
+ if name in Platform.reserved_targets:
+ msg = "{0} is a spack reserved alias and cannot be the name of a target"
+ raise ValueError(msg.format(name))
+ self.targets[name] = target
+
+ def target(self, name):
+ """This is a getter method for the target dictionary
+ that handles defaulting based on the values provided by default,
+ front-end, and back-end. This can be overwritten
+ by a subclass for which we want to provide further aliasing options.
+ """
+ # TODO: Check if we can avoid using strings here
+ name = str(name)
+ if name == 'default_target':
+ name = self.default
+ elif name == 'frontend' or name == 'fe':
+ name = self.front_end
+ elif name == 'backend' or name == 'be':
+ name = self.back_end
+
+ return self.targets.get(name, None)
+
+ def add_operating_system(self, name, os_class):
+ """Add the operating_system class object into the
+ platform.operating_sys dictionary.
+ """
+ if name in Platform.reserved_oss:
+ msg = "{0} is a spack reserved alias and cannot be the name of an OS"
+ raise ValueError(msg.format(name))
+ self.operating_sys[name] = os_class
+
+ def operating_system(self, name):
+ if name == 'default_os':
+ name = self.default_os
+ if name == 'frontend' or name == "fe":
+ name = self.front_os
+ if name == 'backend' or name == 'be':
+ name = self.back_os
+
+ return self.operating_sys.get(name, None)
+
+ @classmethod
+ def setup_platform_environment(cls, pkg, env):
+ """Subclass can override this method if it requires any
+ platform-specific build environment modifications.
+ """
+
+ @classmethod
+ def detect(cls):
+ """Return True if the the host platform is detected to be the current
+ Platform class, False otherwise.
+
+ Derived classes are responsible for implementing this method.
+ """
+ raise NotImplementedError()
+
+ def __repr__(self):
+ return self.__str__()
+
+ def __str__(self):
+ return self.name
+
+ def _cmp_iter(self):
+ yield self.name
+ yield self.default
+ yield self.front_end
+ yield self.back_end
+ yield self.default_os
+ yield self.front_os
+ yield self.back_os
+
+ def targets():
+ for t in sorted(self.targets.values()):
+ yield t._cmp_iter
+ yield targets
+
+ def oses():
+ for o in sorted(self.operating_sys.values()):
+ yield o._cmp_iter
+ yield oses
diff --git a/lib/spack/spack/platforms/cray.py b/lib/spack/spack/platforms/cray.py
index 85c66fd92f..cc66d8aadc 100644
--- a/lib/spack/spack/platforms/cray.py
+++ b/lib/spack/spack/platforms/cray.py
@@ -11,13 +11,15 @@ import archspec.cpu
import llnl.util.tty as tty
-from spack.architecture import NoPlatformError, Platform, Target
+import spack.target
from spack.operating_systems.cray_backend import CrayBackend
from spack.operating_systems.cray_frontend import CrayFrontend
from spack.paths import build_env_path
from spack.util.executable import Executable
from spack.util.module_cmd import module
+from ._platform import NoPlatformError, Platform
+
_craype_name_to_target_name = {
'x86-cascadelake': 'cascadelake',
'x86-naples': 'zen',
@@ -51,7 +53,7 @@ class Cray(Platform):
# Make all craype targets available.
for target in self._avail_targets():
name = _target_name_from_craype_target_name(target)
- self.add_target(name, Target(name, 'craype-%s' % target))
+ self.add_target(name, spack.target.Target(name, 'craype-%s' % target))
self.back_end = os.environ.get('SPACK_BACK_END',
self._default_target_from_env())
@@ -63,12 +65,12 @@ class Cray(Platform):
# Setup frontend targets
for name in archspec.cpu.TARGETS:
if name not in self.targets:
- self.add_target(name, Target(name))
+ self.add_target(name, spack.target.Target(name))
self.front_end = os.environ.get(
'SPACK_FRONT_END', archspec.cpu.host().name
)
if self.front_end not in self.targets:
- self.add_target(self.front_end, Target(self.front_end))
+ self.add_target(self.front_end, spack.target.Target(self.front_end))
front_distro = CrayFrontend()
back_distro = CrayBackend()
diff --git a/lib/spack/spack/platforms/darwin.py b/lib/spack/spack/platforms/darwin.py
index 3969cc9ec8..2a6355db17 100644
--- a/lib/spack/spack/platforms/darwin.py
+++ b/lib/spack/spack/platforms/darwin.py
@@ -7,9 +7,11 @@ import platform
import archspec.cpu
-from spack.architecture import Platform, Target
+import spack.target
from spack.operating_systems.mac_os import MacOs
+from ._platform import Platform
+
class Darwin(Platform):
priority = 89
@@ -20,7 +22,7 @@ class Darwin(Platform):
super(Darwin, self).__init__('darwin')
for name in archspec.cpu.TARGETS:
- self.add_target(name, Target(name))
+ self.add_target(name, spack.target.Target(name))
self.default = archspec.cpu.host().name
self.front_end = self.default
diff --git a/lib/spack/spack/platforms/linux.py b/lib/spack/spack/platforms/linux.py
index 46906229ba..c11515fa79 100644
--- a/lib/spack/spack/platforms/linux.py
+++ b/lib/spack/spack/platforms/linux.py
@@ -6,9 +6,11 @@ import platform
import archspec.cpu
-from spack.architecture import Platform, Target
+import spack.target
from spack.operating_systems.linux_distro import LinuxDistro
+from ._platform import Platform
+
class Linux(Platform):
priority = 90
@@ -17,7 +19,7 @@ class Linux(Platform):
super(Linux, self).__init__('linux')
for name in archspec.cpu.TARGETS:
- self.add_target(name, Target(name))
+ self.add_target(name, spack.target.Target(name))
# Get specific default
self.default = archspec.cpu.host().name
diff --git a/lib/spack/spack/platforms/test.py b/lib/spack/spack/platforms/test.py
index 9333bb3262..4d48619dfd 100644
--- a/lib/spack/spack/platforms/test.py
+++ b/lib/spack/spack/platforms/test.py
@@ -2,10 +2,12 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
import platform
-from spack.architecture import OperatingSystem, Platform, Target
+import spack.operating_systems
+import spack.target
+
+from ._platform import Platform
class Test(Platform):
@@ -24,13 +26,13 @@ class Test(Platform):
def __init__(self):
super(Test, self).__init__('test')
- self.add_target(self.default, Target(self.default))
- self.add_target(self.front_end, Target(self.front_end))
+ self.add_target(self.default, spack.target.Target(self.default))
+ self.add_target(self.front_end, spack.target.Target(self.front_end))
self.add_operating_system(
- self.default_os, OperatingSystem('debian', 6))
+ self.default_os, spack.operating_systems.OperatingSystem('debian', 6))
self.add_operating_system(
- self.front_os, OperatingSystem('redhat', 6))
+ self.front_os, spack.operating_systems.OperatingSystem('redhat', 6))
@classmethod
def detect(cls):
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index a65f28b807..f5a7f1386a 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -100,10 +100,12 @@ import spack.error
import spack.hash_types as ht
import spack.parse
import spack.paths
+import spack.platforms
import spack.provider_index
import spack.repo
import spack.solver
import spack.store
+import spack.target
import spack.util.crypto
import spack.util.executable
import spack.util.hash
@@ -291,7 +293,7 @@ class ArchSpec(object):
# will assumed to be the host machine's platform.
value = str(value) if value is not None else None
- if value in spack.architecture.Platform.reserved_oss:
+ if value in spack.platforms.Platform.reserved_oss:
curr_platform = str(spack.architecture.platform())
self.platform = self.platform or curr_platform
@@ -301,7 +303,7 @@ class ArchSpec(object):
"arch platform (%s) isn't the current platform (%s)" %
(value, self.platform, curr_platform))
- spec_platform = spack.architecture.get_platform(self.platform)
+ spec_platform = spack.platforms.by_name(self.platform)
value = str(spec_platform.operating_system(value))
self._os = value
@@ -320,15 +322,15 @@ class ArchSpec(object):
# will assumed to be the host machine's platform.
def target_or_none(t):
- if isinstance(t, spack.architecture.Target):
+ if isinstance(t, spack.target.Target):
return t
if t and t != 'None':
- return spack.architecture.Target(t)
+ return spack.target.Target(t)
return None
value = target_or_none(value)
- if str(value) in spack.architecture.Platform.reserved_targets:
+ if str(value) in spack.platforms.Platform.reserved_targets:
curr_platform = str(spack.architecture.platform())
self.platform = self.platform or curr_platform
@@ -338,7 +340,7 @@ class ArchSpec(object):
"the arch platform (%s) isn't the current platform (%s)" %
(value, self.platform, curr_platform))
- spec_platform = spack.architecture.get_platform(self.platform)
+ spec_platform = spack.platforms.by_name(self.platform)
value = spec_platform.target(value)
self._target = value
@@ -416,7 +418,7 @@ class ArchSpec(object):
# s_target_range is a concrete target
# get a microarchitecture reference for at least one side
# of each comparison so we can use archspec comparators
- s_comp = spack.architecture.Target(s_min).microarchitecture
+ s_comp = spack.target.Target(s_min).microarchitecture
if not o_sep:
if s_min == o_min:
results.append(s_min)
@@ -425,22 +427,22 @@ class ArchSpec(object):
results.append(s_min)
elif not o_sep:
# "cast" to microarchitecture
- o_comp = spack.architecture.Target(o_min).microarchitecture
+ o_comp = spack.target.Target(o_min).microarchitecture
if (not s_min or o_comp >= s_min) and (
not s_max or o_comp <= s_max):
results.append(o_min)
else:
# Take intersection of two ranges
# Lots of comparisons needed
- _s_min = spack.architecture.Target(s_min).microarchitecture
- _s_max = spack.architecture.Target(s_max).microarchitecture
- _o_min = spack.architecture.Target(o_min).microarchitecture
- _o_max = spack.architecture.Target(o_max).microarchitecture
+ _s_min = spack.target.Target(s_min).microarchitecture
+ _s_max = spack.target.Target(s_max).microarchitecture
+ _o_min = spack.target.Target(o_min).microarchitecture
+ _o_max = spack.target.Target(o_max).microarchitecture
n_min = s_min if _s_min >= _o_min else o_min
n_max = s_max if _s_max <= _o_max else o_max
- _n_min = spack.architecture.Target(n_min).microarchitecture
- _n_max = spack.architecture.Target(n_max).microarchitecture
+ _n_min = spack.target.Target(n_min).microarchitecture
+ _n_max = spack.target.Target(n_max).microarchitecture
if _n_min == _n_max:
results.append(n_min)
elif not n_min or not n_max or _n_min < _n_max:
@@ -521,7 +523,7 @@ class ArchSpec(object):
d = d['arch']
operating_system = d.get('platform_os', None) or d['os']
- target = spack.architecture.Target.from_dict_or_value(d['target'])
+ target = spack.target.Target.from_dict_or_value(d['target'])
return ArchSpec((d['platform'], operating_system, target))
diff --git a/lib/spack/spack/target.py b/lib/spack/spack/target.py
new file mode 100644
index 0000000000..727df77e1b
--- /dev/null
+++ b/lib/spack/spack/target.py
@@ -0,0 +1,162 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import functools
+import warnings
+
+import six
+
+import archspec.cpu
+
+import llnl.util.tty as tty
+
+import spack.compiler
+import spack.compilers
+import spack.spec
+import spack.util.spack_yaml as syaml
+
+
+def _ensure_other_is_target(method):
+ """In a single argument method, ensure that the argument is an
+ instance of ``Target``.
+ """
+ @functools.wraps(method)
+ def _impl(self, other):
+ if isinstance(other, six.string_types):
+ other = Target(other)
+
+ if not isinstance(other, Target):
+ return NotImplemented
+
+ return method(self, other)
+
+ return _impl
+
+
+class Target(object):
+ def __init__(self, name, module_name=None):
+ """Target models microarchitectures and their compatibility.
+
+ Args:
+ name (str or Microarchitecture): microarchitecture of the target
+ module_name (str): optional module name to get access to the
+ current target. This is typically used on machines
+ like Cray (e.g. craype-compiler)
+ """
+ if not isinstance(name, archspec.cpu.Microarchitecture):
+ name = archspec.cpu.TARGETS.get(
+ name, archspec.cpu.generic_microarchitecture(name)
+ )
+ self.microarchitecture = name
+ self.module_name = module_name
+
+ @property
+ def name(self):
+ return self.microarchitecture.name
+
+ @_ensure_other_is_target
+ def __eq__(self, other):
+ return (self.microarchitecture == other.microarchitecture and
+ self.module_name == other.module_name)
+
+ def __ne__(self, other):
+ # This method is necessary as long as we support Python 2. In Python 3
+ # __ne__ defaults to the implementation below
+ return not self == other
+
+ @_ensure_other_is_target
+ def __lt__(self, other):
+ # TODO: In the future it would be convenient to say
+ # TODO: `spec.architecture.target < other.architecture.target`
+ # TODO: and change the semantic of the comparison operators
+
+ # This is needed to sort deterministically specs in a list.
+ # It doesn't implement a total ordering semantic.
+ return self.microarchitecture.name < other.microarchitecture.name
+
+ def __hash__(self):
+ return hash((self.name, self.module_name))
+
+ @staticmethod
+ def from_dict_or_value(dict_or_value):
+ # A string here represents a generic target (like x86_64 or ppc64) or
+ # a custom micro-architecture
+ if isinstance(dict_or_value, six.string_types):
+ return Target(dict_or_value)
+
+ # TODO: From a dict we actually retrieve much more information than
+ # TODO: just the name. We can use that information to reconstruct an
+ # TODO: "old" micro-architecture or check the current definition.
+ target_info = dict_or_value
+ return Target(target_info['name'])
+
+ def to_dict_or_value(self):
+ """Returns a dict or a value representing the current target.
+
+ String values are used to keep backward compatibility with generic
+ targets, like e.g. x86_64 or ppc64. More specific micro-architectures
+ will return a dictionary which contains information on the name,
+ features, vendor, generation and parents of the current target.
+ """
+ # Generic targets represent either an architecture
+ # family (like x86_64) or a custom micro-architecture
+ if self.microarchitecture.vendor == 'generic':
+ return str(self)
+
+ return syaml.syaml_dict(
+ self.microarchitecture.to_dict(return_list_of_items=True)
+ )
+
+ def __repr__(self):
+ cls_name = self.__class__.__name__
+ fmt = cls_name + '({0}, {1})'
+ return fmt.format(repr(self.microarchitecture),
+ repr(self.module_name))
+
+ def __str__(self):
+ return str(self.microarchitecture)
+
+ def __contains__(self, cpu_flag):
+ return cpu_flag in self.microarchitecture
+
+ def optimization_flags(self, compiler):
+ """Returns the flags needed to optimize for this target using
+ the compiler passed as argument.
+
+ Args:
+ compiler (spack.spec.CompilerSpec or spack.compiler.Compiler): object that
+ contains both the name and the version of the compiler we want to use
+ """
+ # Mixed toolchains are not supported yet
+ if isinstance(compiler, spack.compiler.Compiler):
+ if spack.compilers.is_mixed_toolchain(compiler):
+ msg = ('microarchitecture specific optimizations are not '
+ 'supported yet on mixed compiler toolchains [check'
+ ' {0.name}@{0.version} for further details]')
+ warnings.warn(msg.format(compiler))
+ return ''
+
+ # Try to check if the current compiler comes with a version number or
+ # has an unexpected suffix. If so, treat it as a compiler with a
+ # custom spec.
+ compiler_version = compiler.version
+ version_number, suffix = archspec.cpu.version_components(
+ compiler.version
+ )
+ if not version_number or suffix not in ('', 'apple'):
+ # Try to deduce the underlying version of the compiler, regardless
+ # of its name in compilers.yaml. Depending on where this function
+ # is called we might get either a CompilerSpec or a fully fledged
+ # compiler object.
+ if isinstance(compiler, spack.spec.CompilerSpec):
+ compiler = spack.compilers.compilers_for_spec(compiler).pop()
+ try:
+ compiler_version = compiler.real_version
+ except spack.util.executable.ProcessError as e:
+ # log this and just return compiler.version instead
+ tty.debug(str(e))
+
+ return self.microarchitecture.optimization_flags(
+ compiler.name, str(compiler_version)
+ )
diff --git a/lib/spack/spack/test/architecture.py b/lib/spack/spack/test/architecture.py
index bc12e3f857..fd65c9558c 100644
--- a/lib/spack/spack/test/architecture.py
+++ b/lib/spack/spack/test/architecture.py
@@ -9,8 +9,10 @@ import pytest
import spack.architecture
import spack.concretize
+import spack.operating_systems
import spack.platforms
import spack.spec
+import spack.target
@pytest.fixture
@@ -66,9 +68,9 @@ def test_dict_round_trip(sample_arch):
assert sample_arch == sample_arch_copy
for current_arch in (sample_arch, sample_arch_copy):
assert isinstance(current_arch, spack.architecture.Arch)
- assert isinstance(current_arch.platform, spack.architecture.Platform)
- assert isinstance(current_arch.os, spack.architecture.OperatingSystem)
- assert isinstance(current_arch.target, spack.architecture.Target)
+ assert isinstance(current_arch.platform, spack.platforms.Platform)
+ assert isinstance(current_arch.os, spack.operating_systems.OperatingSystem)
+ assert isinstance(current_arch.target, spack.target.Target)
def test_platform(current_host_platform):
@@ -111,7 +113,7 @@ def test_user_input_combination(config, target_str, os_str):
def test_operating_system_conversion_to_dict():
- operating_system = spack.architecture.OperatingSystem('os', '1.0')
+ operating_system = spack.operating_systems.OperatingSystem('os', '1.0')
assert operating_system.to_dict() == {
'name': 'os', 'version': '1.0'
}
@@ -129,7 +131,7 @@ def test_operating_system_conversion_to_dict():
('avx512', 'icelake'),
])
def test_target_container_semantic(cpu_flag, target_name):
- target = spack.architecture.Target(target_name)
+ target = spack.target.Target(target_name)
assert cpu_flag in target
@@ -159,7 +161,7 @@ def test_arch_spec_container_semantic(item, architecture_str):
def test_optimization_flags(
compiler_spec, target_name, expected_flags, config
):
- target = spack.architecture.Target(target_name)
+ target = spack.target.Target(target_name)
compiler = spack.compilers.compilers_for_spec(compiler_spec).pop()
opt_flags = target.optimization_flags(compiler)
assert opt_flags == expected_flags
@@ -182,7 +184,7 @@ def test_optimization_flags(
def test_optimization_flags_with_custom_versions(
compiler, real_version, target_str, expected_flags, monkeypatch, config
):
- target = spack.architecture.Target(target_str)
+ target = spack.target.Target(target_str)
if real_version:
monkeypatch.setattr(
spack.compiler.Compiler, 'get_real_version',
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
index b86b18392a..b7ae5dd926 100644
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -98,9 +98,6 @@ def current_host(request, monkeypatch):
cpu, _, is_preference = request.param.partition('-')
target = archspec.cpu.TARGETS[cpu]
- # this function is memoized, so clear its state for testing
- spack.architecture.get_platform.cache.clear()
-
monkeypatch.setattr(spack.platforms.Test, 'default', cpu)
monkeypatch.setattr(spack.platforms.Test, 'front_end', cpu)
if not is_preference:
@@ -110,9 +107,6 @@ def current_host(request, monkeypatch):
with spack.config.override('packages:all', {'target': [cpu]}):
yield target
- # clear any test values fetched
- spack.architecture.get_platform.cache.clear()
-
@pytest.fixture()
def repo_with_changing_recipe(tmpdir_factory, mutable_mock_repo):
diff --git a/var/spack/repos/builtin/packages/pmix/package.py b/var/spack/repos/builtin/packages/pmix/package.py
index aec064505d..cc6099d41b 100644
--- a/var/spack/repos/builtin/packages/pmix/package.py
+++ b/var/spack/repos/builtin/packages/pmix/package.py
@@ -2,11 +2,8 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-
-
import os
-import spack.architecture
from spack import *
@@ -111,8 +108,8 @@ class Pmix(AutotoolsPackage):
# Versions < 2.1.1 have a bug in the test code that *sometimes*
# causes problems on strict alignment architectures such as
# aarch64. Work-around is to just not build the test code.
- if 'aarch64' in spack.architecture.sys_type() and \
- self.spec.version < Version('2.1.1'):
+ if (self.spec.satisfies('target=aarch64:') and
+ self.spec.version < Version('2.1.1')):
config_args.append('--without-tests-examples')
# Versions >= 3.0 also use hwloc