Fixes to Handling Multiple Architectures (#2261)

* Added some notes about how multiarch detection could be fixed.

* Implemented a preliminary version of the "spack.spec.ArchSpec" class.

* Updated the "spack.spec.Spec" class to use "ArchSpec" instead of "Arch".

* Fixed a number of small bugs in the "spack.spec.ArchSpec" class.

* Fixed the 'Concretizer.concretize_architecture' method so that it uses the new architecture specs.

* Updated the package class to properly use arch specs.
Removed a number of unused architecture functions.

* Fixed up a number of bugs that were causing the regression tests to fail.
Added a couple of additional regression tests related to architecture parsing/specification.
Fixed a few bugs with setting reserved os/target values on "ArchSpec" objects.
Removed a number of unnecessary functions in the "spack.architecture" and "spack.concretize" modules.

* Fixed a few bugs with reading architecture information from specs.
Updated the tests to use a uniform architecture to improve reliability.
Fixed a few minor style issues.

* Adapted the compiler component of Spack to use arch specs.

* Implemented more test cases for the extended architecture spec features.
Improved error detection for multiple arch components in a spec.

* Fix for backwards compatibility with v0.8 and prior

* Changed os to unknown for compatibility specs

* Use `spack09` instead of `spackcompat` for the platform of old specs.
This commit is contained in:
Joseph Ciurej 2016-12-03 15:38:31 -08:00 committed by Todd Gamblin
parent 2f46613132
commit 552b4eae9e
13 changed files with 471 additions and 270 deletions

View File

@ -126,8 +126,7 @@ def __str__(self):
@key_ordering
class Platform(object):
""" Abstract class that each type of Platform will subclass.
Will return a instance of it once it
is returned
Will return a instance of it once it is returned.
"""
priority = None # Subclass sets number. Controls detection order
@ -139,6 +138,9 @@ class Platform(object):
back_os = None
default_os = None
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 = {}
@ -149,7 +151,7 @@ def add_target(self, name, target):
Raises an error if the platform specifies a name
that is reserved by spack as an alias.
"""
if name in ['frontend', 'fe', 'backend', 'be', 'default_target']:
if name in Platform.reserved_targets:
raise ValueError(
"%s is a spack reserved alias "
"and cannot be the name of a target" % name)
@ -174,7 +176,7 @@ def add_operating_system(self, name, os_class):
""" Add the operating_system class object into the
platform.operating_sys dictionary
"""
if name in ['frontend', 'fe', 'backend', 'be', 'default_os']:
if name in Platform.reserved_oss:
raise ValueError(
"%s is a spack reserved alias "
"and cannot be the name of an OS" % name)
@ -241,7 +243,7 @@ def __init__(self, name, version):
self.version = version
def __str__(self):
return self.name + self.version
return "%s%s" % (self.name, self.version)
def __repr__(self):
return self.__str__()
@ -409,86 +411,52 @@ def _cmp_key(self):
return (platform, platform_os, target)
def to_dict(self):
return syaml_dict((
('platform',
str(self.platform) if self.platform else None),
('platform_os',
str(self.platform_os) if self.platform_os else None),
('target',
str(self.target) if self.target else None)))
str_or_none = lambda v: str(v) if v else None
d = syaml_dict([
('platform', str_or_none(self.platform)),
('platform_os', str_or_none(self.platform_os)),
('target', str_or_none(self.target))])
return syaml_dict([('arch', d)])
@staticmethod
def from_dict(d):
spec = spack.spec.ArchSpec.from_dict(d)
return arch_for_spec(spec)
def _target_from_dict(target_name, plat=None):
""" Creates new instance of target and assigns all the attributes of
that target from the dictionary
"""
if not plat:
plat = platform()
return plat.target(target_name)
def _operating_system_from_dict(os_name, plat=None):
""" uses platform's operating system method to grab the constructed
operating systems that are valid on the platform.
"""
if not plat:
plat = platform()
if isinstance(os_name, dict):
name = os_name['name']
version = os_name['version']
return plat.operating_system(name + version)
else:
return plat.operating_system(os_name)
def _platform_from_dict(platform_name):
""" Constructs a platform from a dictionary. """
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 arch_from_dict(d):
""" Uses _platform_from_dict, _operating_system_from_dict, _target_from_dict
helper methods to recreate the arch tuple from the dictionary read from
a yaml file
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.
"""
arch = Arch()
platform_name = platform_name.replace("_", "").lower()
platform_names = [p.__name__.lower() for p in all_platforms()]
if isinstance(d, basestring):
# We have an old spec using a string for the architecture
arch.platform = Platform('spack_compatibility')
arch.platform_os = OperatingSystem('unknown', '')
arch.target = Target(d)
if platform_name not in platform_names:
tty.die("%s is not a supported platform; supported platforms are %s" %
(platform_name, platform_names))
arch.os_string = None
arch.target_string = None
else:
if d is None:
return None
platform_name = d['platform']
os_name = d['platform_os']
target_name = d['target']
if platform_name:
arch.platform = _platform_from_dict(platform_name)
else:
arch.platform = None
if target_name:
arch.target = _target_from_dict(target_name, arch.platform)
else:
arch.target = None
if os_name:
arch.platform_os = _operating_system_from_dict(os_name,
arch.platform)
else:
arch.platform_os = None
def arch_for_spec(arch_spec):
"""Transforms the given architecture spec into an architecture objct."""
arch_spec = spack.spec.ArchSpec(arch_spec)
assert(arch_spec.concrete)
arch.os_string = None
arch.target_string = None
arch_plat = get_platform(arch_spec.platform)
if not (arch_plat.operating_system(arch_spec.platform_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()))
return arch
return Arch(arch_plat, arch_spec.platform_os, arch_spec.target)
@memoized

View File

@ -339,7 +339,7 @@ def set_build_environment_variables(pkg, env, dirty=False):
if os.path.isdir(pcdir):
env.prepend_path('PKG_CONFIG_PATH', pcdir)
if pkg.spec.architecture.target.module_name:
if pkg.architecture.target.module_name:
load_module(pkg.spec.architecture.target.module_name)
return env
@ -492,7 +492,7 @@ def setup_package(pkg, dirty=False):
set_compiler_environment_variables(pkg, spack_env)
set_build_environment_variables(pkg, spack_env, dirty)
pkg.spec.architecture.platform.setup_platform_environment(pkg, spack_env)
pkg.architecture.platform.setup_platform_environment(pkg, spack_env)
load_external_modules(pkg)
# traverse in postorder so package can use vars from its dependencies
spec = pkg.spec

View File

@ -115,8 +115,8 @@ def fc_rpath_arg(self):
def __init__(self, cspec, operating_system,
paths, modules=[], alias=None, environment=None,
extra_rpaths=None, **kwargs):
self.operating_system = operating_system
self.spec = cspec
self.operating_system = str(operating_system)
self.modules = modules
self.alias = alias

View File

@ -202,20 +202,23 @@ def find(compiler_spec, scope=None):
@_auto_compiler_spec
def compilers_for_spec(compiler_spec, scope=None, **kwargs):
def compilers_for_spec(compiler_spec, arch_spec=None, scope=None):
"""This gets all compilers that satisfy the supplied CompilerSpec.
Returns an empty list if none are found.
"""
platform = kwargs.get('platform', None)
config = all_compilers_config(scope)
def get_compilers(cspec):
compilers = []
for items in config:
if items['compiler']['spec'] != str(cspec):
continue
items = items['compiler']
if items['spec'] != str(cspec):
continue
os = items.get('operating_system', None)
if arch_spec and os != arch_spec.platform_os:
continue
if not ('paths' in items and
all(n in items['paths'] for n in _path_instance_vars)):
@ -235,11 +238,6 @@ def get_compilers(cspec):
if mods == 'None':
mods = []
os = None
if 'operating_system' in items:
os = spack.architecture._operating_system_from_dict(
items['operating_system'], platform)
alias = items.get('alias', None)
compiler_flags = items.get('flags', {})
environment = items.get('environment', {})
@ -259,17 +257,15 @@ def get_compilers(cspec):
@_auto_compiler_spec
def compiler_for_spec(compiler_spec, arch):
def compiler_for_spec(compiler_spec, arch_spec):
"""Get the compiler that satisfies compiler_spec. compiler_spec must
be concrete."""
operating_system = arch.platform_os
assert(compiler_spec.concrete)
assert(arch_spec.concrete)
compilers = [
c for c in compilers_for_spec(compiler_spec, platform=arch.platform)
if c.operating_system == operating_system]
compilers = compilers_for_spec(compiler_spec, arch_spec=arch_spec)
if len(compilers) < 1:
raise NoCompilerForSpecError(compiler_spec, operating_system)
raise NoCompilerForSpecError(compiler_spec, arch_spec.platform_os)
if len(compilers) > 1:
raise CompilerSpecInsufficientlySpecificError(compiler_spec)
return compilers[0]

View File

@ -240,47 +240,6 @@ def concretize_version(self, spec):
return True # Things changed
def _concretize_operating_system(self, spec):
if spec.architecture.platform_os is not None and isinstance(
spec.architecture.platform_os,
spack.architecture.OperatingSystem):
return False
if spec.root.architecture and spec.root.architecture.platform_os:
if isinstance(spec.root.architecture.platform_os,
spack.architecture.OperatingSystem):
spec.architecture.platform_os = \
spec.root.architecture.platform_os
else:
spec.architecture.platform_os = \
spec.architecture.platform.operating_system('default_os')
return True # changed
def _concretize_target(self, spec):
if spec.architecture.target is not None and isinstance(
spec.architecture.target, spack.architecture.Target):
return False
if spec.root.architecture and spec.root.architecture.target:
if isinstance(spec.root.architecture.target,
spack.architecture.Target):
spec.architecture.target = spec.root.architecture.target
else:
spec.architecture.target = spec.architecture.platform.target(
'default_target')
return True # changed
def _concretize_platform(self, spec):
if spec.architecture.platform is not None and isinstance(
spec.architecture.platform, spack.architecture.Platform):
return False
if spec.root.architecture and spec.root.architecture.platform:
if isinstance(spec.root.architecture.platform,
spack.architecture.Platform):
spec.architecture.platform = spec.root.architecture.platform
else:
spec.architecture.platform = spack.architecture.platform()
return True # changed?
def concretize_architecture(self, spec):
"""If the spec is empty provide the defaults of the platform. If the
architecture is not a basestring, then check if either the platform,
@ -292,16 +251,25 @@ def concretize_architecture(self, spec):
DAG has an architecture, then use the root otherwise use the defaults
on the platform.
"""
if spec.architecture is None:
# Set the architecture to all defaults
spec.architecture = spack.architecture.Arch()
return True
root_arch = spec.root.architecture
sys_arch = spack.spec.ArchSpec(spack.architecture.sys_type())
spec_changed = False
# Concretize the operating_system and target based of the spec
ret = any((self._concretize_platform(spec),
self._concretize_operating_system(spec),
self._concretize_target(spec)))
return ret
if spec.architecture is None:
spec.architecture = spack.spec.ArchSpec(sys_arch)
spec_changed = True
default_archs = [root_arch, sys_arch]
while not spec.architecture.concrete and default_archs:
arch = default_archs.pop(0)
replacement_fields = [k for k, v in arch.to_cmp_dict().iteritems()
if v and not getattr(spec.architecture, k)]
for field in replacement_fields:
setattr(spec.architecture, field, getattr(arch, field))
spec_changed = True
return spec_changed
def concretize_variants(self, spec):
"""If the spec already has variants filled in, return. Otherwise, add
@ -343,13 +311,8 @@ def concretize_compiler(self, spec):
# Takes advantage of the proper logic already existing in
# compiler_for_spec Should think whether this can be more
# efficient
def _proper_compiler_style(cspec, arch):
platform = arch.platform
compilers = spack.compilers.compilers_for_spec(cspec,
platform=platform)
return filter(lambda c: c.operating_system ==
arch.platform_os, compilers)
# return compilers
def _proper_compiler_style(cspec, aspec):
return spack.compilers.compilers_for_spec(cspec, arch_spec=aspec)
all_compilers = spack.compilers.all_compilers()

View File

@ -898,7 +898,14 @@ def prefix(self):
return self.spec.prefix
@property
# TODO: Change this to architecture
def architecture(self):
"""Get the spack.architecture.Arch object that represents the
environment in which this package will be built."""
if not self.spec.concrete:
raise ValueError("Can only get the arch for concrete package.")
return spack.architecture.arch_for_spec(self.spec.architecture)
@property
def compiler(self):
"""Get the spack.compiler.Compiler object used to build this package"""
if not self.spec.concrete:

View File

@ -23,8 +23,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack.architecture import Platform, Target
from spack.operating_systems.linux_distro import LinuxDistro
from spack.operating_systems.cnl import Cnl
from spack.architecture import OperatingSystem as OS
class Test(Platform):
@ -33,18 +32,17 @@ class Test(Platform):
back_end = 'x86_64'
default = 'x86_64'
back_os = 'CNL10'
default_os = 'CNL10'
front_os = 'redhat6'
back_os = 'debian6'
default_os = 'debian6'
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_operating_system(self.default_os, Cnl())
linux_dist = LinuxDistro()
self.front_os = linux_dist.name
self.add_operating_system(self.front_os, linux_dist)
self.add_operating_system(self.default_os, OS('debian', 6))
self.add_operating_system(self.front_os, OS('redhat', 6))
@classmethod
def detect(self):

View File

@ -97,7 +97,6 @@
"""
import base64
import hashlib
import imp
import ctypes
from StringIO import StringIO
from operator import attrgetter
@ -105,7 +104,6 @@
from yaml.error import MarkedYAMLError
import llnl.util.tty as tty
from llnl.util.filesystem import join_path
from llnl.util.lang import *
from llnl.util.tty.color import *
@ -116,7 +114,6 @@
import spack.error
import spack.parse
from spack.build_environment import get_path_from_module, load_module
from spack.util.naming import mod_to_class
from spack.util.prefix import Prefix
from spack.util.string import *
import spack.util.spack_yaml as syaml
@ -255,6 +252,204 @@ def __call__(self, match):
return colorize(re.sub(_separators, insert_color(), str(spec)) + '@.')
@key_ordering
class ArchSpec(object):
""" The ArchSpec class represents an abstract architecture specification
that a package should be built with. At its core, each ArchSpec is
comprised of three elements: a platform (e.g. Linux), an OS (e.g.
RHEL6), and a target (e.g. x86_64).
"""
# TODO: Formalize the specifications for architectures and then use
# the appropriate parser here to read these specifications.
def __init__(self, *args):
to_attr_string = lambda s: str(s) if s and s != "None" else None
self.platform, self.platform_os, self.target = (None, None, None)
if len(args) == 1:
spec_like = args[0]
if isinstance(spec_like, ArchSpec):
self._dup(spec_like)
elif isinstance(spec_like, basestring):
spec_fields = spec_like.split("-")
if len(spec_fields) == 3:
self.platform, self.platform_os, self.target = tuple(
to_attr_string(f) for f in spec_fields)
else:
raise ValueError("%s is an invalid arch spec" % spec_like)
elif len(args) == 3:
self.platform = to_attr_string(args[0])
self.platform_os = to_attr_string(args[1])
self.target = to_attr_string(args[2])
elif len(args) != 0:
raise TypeError("Can't make arch spec from %s" % args)
def _autospec(self, spec_like):
if isinstance(spec_like, ArchSpec):
return spec_like
return ArchSpec(spec_like)
def _cmp_key(self):
return (self.platform, self.platform_os, self.target)
def _dup(self, other):
self.platform = other.platform
self.platform_os = other.platform_os
self.target = other.target
@property
def platform(self):
return self._platform
@platform.setter
def platform(self, value):
""" The platform of the architecture spec will be verified as a
supported Spack platform before it's set to ensure all specs
refer to valid platforms.
"""
value = str(value) if value is not None else None
self._platform = value
@property
def platform_os(self):
return self._platform_os
@platform_os.setter
def platform_os(self, value):
""" The OS of the architecture spec will update the platform field
if the OS is set to one of the reserved OS types so that the
default OS type can be resolved. Since the reserved OS
information is only available for the host machine, the platform
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:
curr_platform = str(spack.architecture.platform())
self.platform = self.platform or curr_platform
if self.platform != curr_platform:
raise ValueError(
"Can't set arch spec OS to reserved value '%s' when the "
"arch platform (%s) isn't the current platform (%s)" %
(value, self.platform, curr_platform))
spec_platform = spack.architecture.get_platform(self.platform)
value = str(spec_platform.operating_system(value))
self._platform_os = value
@property
def target(self):
return self._target
@target.setter
def target(self, value):
""" The target of the architecture spec will update the platform field
if the target is set to one of the reserved target types so that
the default target type can be resolved. Since the reserved target
information is only available for the host machine, the platform
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_targets:
curr_platform = str(spack.architecture.platform())
self.platform = self.platform or curr_platform
if self.platform != curr_platform:
raise ValueError(
"Can't set arch spec target to reserved value '%s' when "
"the arch platform (%s) isn't the current platform (%s)" %
(value, self.platform, curr_platform))
spec_platform = spack.architecture.get_platform(self.platform)
value = str(spec_platform.target(value))
self._target = value
def satisfies(self, other, strict=False):
other = self._autospec(other)
sdict, odict = self.to_cmp_dict(), other.to_cmp_dict()
if strict or self.concrete:
return all(getattr(self, attr) == getattr(other, attr)
for attr in odict if odict[attr])
else:
return all(getattr(self, attr) == getattr(other, attr)
for attr in odict if sdict[attr] and odict[attr])
def constrain(self, other):
""" Projects all architecture fields that are specified in the given
spec onto the instance spec if they're missing from the instance
spec. This will only work if the two specs are compatible.
"""
other = self._autospec(other)
if not self.satisfies(other):
raise UnsatisfiableArchitectureSpecError(self, other)
constrained = False
for attr, svalue in self.to_cmp_dict().iteritems():
ovalue = getattr(other, attr)
if svalue is None and ovalue is not None:
setattr(self, attr, ovalue)
constrained = True
return constrained
def copy(self):
clone = ArchSpec.__new__(ArchSpec)
clone._dup(self)
return clone
@property
def concrete(self):
return all(v for k, v in self.to_cmp_dict().iteritems())
def to_cmp_dict(self):
"""Returns a dictionary that can be used for field comparison."""
return dict([
('platform', self.platform),
('platform_os', self.platform_os),
('target', self.target)])
def to_dict(self):
d = syaml_dict([
('platform', self.platform),
('platform_os', self.platform_os),
('target', self.target)])
return syaml_dict([('arch', d)])
@staticmethod
def from_dict(d):
"""Import an ArchSpec from raw YAML/JSON data.
This routine implements a measure of compatibility with older
versions of Spack. Spack releases before 0.10 used a single
string with no OS or platform identifiers. We import old Spack
architectures with platform ``spack09``, OS ``unknown``, and the
old arch string as the target.
Specs from `0.10` or later have a more fleshed out architecture
descriptor with a platform, an OS, and a target.
"""
if not isinstance(d['arch'], dict):
return ArchSpec('spack09', 'unknown', d['arch'])
d = d['arch']
return ArchSpec(d['platform'], d['platform_os'], d['target'])
def __str__(self):
return "%s-%s-%s" % (self.platform, self.platform_os, self.target)
def __repr__(self):
return str(self)
@key_ordering
class CompilerSpec(object):
"""The CompilerSpec field represents the compiler or range of compiler
@ -664,38 +859,42 @@ def _add_flag(self, name, value):
"""
valid_flags = FlagMap.valid_compiler_flags()
if name == 'arch' or name == 'architecture':
parts = value.split('-')
if len(parts) == 3:
platform, op_sys, target = parts
else:
platform, op_sys, target = None, None, value
assert(self.architecture.platform is None)
assert(self.architecture.platform_os is None)
assert(self.architecture.target is None)
assert(self.architecture.os_string is None)
assert(self.architecture.target_string is None)
self._set_platform(platform)
self._set_os(op_sys)
self._set_target(target)
parts = tuple(value.split('-'))
plat, os, tgt = parts if len(parts) == 3 else (None, None, value)
self._set_architecture(platform=plat, platform_os=os, target=tgt)
elif name == 'platform':
self._set_platform(value)
self._set_architecture(platform=value)
elif name == 'os' or name == 'operating_system':
if self.architecture.platform:
self._set_os(value)
else:
self.architecture.os_string = value
self._set_architecture(platform_os=value)
elif name == 'target':
if self.architecture.platform:
self._set_target(value)
else:
self.architecture.target_string = value
self._set_architecture(target=value)
elif name in valid_flags:
assert(self.compiler_flags is not None)
self.compiler_flags[name] = value.split()
else:
self._add_variant(name, value)
def _set_architecture(self, **kwargs):
"""Called by the parser to set the architecture."""
arch_attrs = ['platform', 'platform_os', 'target']
if self.architecture and self.architecture.concrete:
raise DuplicateArchitectureError(
"Spec for '%s' cannot have two architectures." % self.name)
if not self.architecture:
new_vals = tuple(kwargs.get(arg, None) for arg in arch_attrs)
self.architecture = ArchSpec(*new_vals)
else:
new_attrvals = [(a, v) for a, v in kwargs.iteritems()
if a in arch_attrs]
for new_attr, new_value in new_attrvals:
if getattr(self.architecture, new_attr):
raise DuplicateArchitectureError(
"Spec for '%s' cannot have two '%s' specified "
"for its architecture" % (self.name, new_attr))
else:
setattr(self.architecture, new_attr, new_value)
def _set_compiler(self, compiler):
"""Called by the parser to set the compiler."""
if self.compiler:
@ -703,53 +902,6 @@ def _set_compiler(self, compiler):
"Spec for '%s' cannot have two compilers." % self.name)
self.compiler = compiler
def _set_platform(self, value):
"""Called by the parser to set the architecture platform"""
if isinstance(value, basestring):
mod_path = spack.platform_path
mod_string = 'spack.platformss'
names = list_modules(mod_path)
if value in names:
# Create a platform object from the name
mod_name = mod_string + value
path = join_path(mod_path, value) + '.py'
mod = imp.load_source(mod_name, path)
class_name = mod_to_class(value)
if not hasattr(mod, class_name):
tty.die(
'No class %s defined in %s' % (class_name, mod_name))
cls = getattr(mod, class_name)
if not inspect.isclass(cls):
tty.die('%s.%s is not a class' % (mod_name, class_name))
platform = cls()
else:
tty.die("No platform class %s defined." % value)
else:
# The value is a platform
platform = value
self.architecture.platform = platform
# Set os and target if we previously got strings for them
if self.architecture.os_string:
self._set_os(self.architecture.os_string)
self.architecture.os_string = None
if self.architecture.target_string:
self._set_target(self.architecture.target_string)
self.architecture.target_string = None
def _set_os(self, value):
"""Called by the parser to set the architecture operating system"""
arch = self.architecture
if arch.platform:
arch.platform_os = arch.platform.operating_system(value)
def _set_target(self, value):
"""Called by the parser to set the architecture target"""
arch = self.architecture
if arch.platform:
arch.target = arch.platform.target(value)
def _add_dependency(self, spec, deptypes):
"""Called by the parser to add another spec as a dependency."""
if spec.name in self._dependencies:
@ -990,6 +1142,9 @@ def to_node_dict(self):
if self.versions:
d.update(self.versions.to_dict())
if self.architecture:
d.update(self.architecture.to_dict())
if self.compiler:
d.update(self.compiler.to_dict())
@ -1002,9 +1157,6 @@ def to_node_dict(self):
if params:
d['parameters'] = params
if self.architecture:
d['arch'] = self.architecture.to_dict()
# TODO: restore build dependencies here once we have less picky
# TODO: concretization.
deps = self.dependencies_dict(deptype=('link', 'run'))
@ -1042,7 +1194,7 @@ def from_node_dict(node):
spec.versions = VersionList.from_dict(node)
if 'arch' in node:
spec.architecture = spack.architecture.arch_from_dict(node['arch'])
spec.architecture = ArchSpec.from_dict(node)
if 'compiler' in node:
spec.compiler = CompilerSpec.from_dict(node)
@ -1861,25 +2013,10 @@ def satisfies(self, other, deps=True, strict=False):
# Architecture satisfaction is currently just string equality.
# If not strict, None means unconstrained.
sarch, oarch = self.architecture, other.architecture
if sarch and oarch:
if ((sarch.platform and
oarch.platform and
sarch.platform != oarch.platform) or
(sarch.platform_os and
oarch.platform_os and
sarch.platform_os != oarch.platform_os) or
(sarch.target and
oarch.target and
sarch.target != oarch.target)):
if self.architecture and other.architecture:
if not self.architecture.satisfies(other.architecture, strict):
return False
elif strict and ((oarch and not sarch) or
(oarch.platform and not sarch.platform) or
(oarch.platform_os and not sarch.platform_os) or
(oarch.target and not sarch.target)):
elif strict and (other.architecture and not self.architecture):
return False
if not self.compiler_flags.satisfies(
@ -1975,7 +2112,8 @@ def _dup(self, other, deps=True, cleardeps=True):
# Local node attributes get copied first.
self.name = other.name
self.versions = other.versions.copy()
self.architecture = other.architecture
self.architecture = other.architecture.copy() if other.architecture \
else None
self.compiler = other.compiler.copy() if other.compiler else None
if cleardeps:
self._dependents = DependencyMap()
@ -2540,10 +2678,12 @@ def do_parse(self):
# If the spec has an os or a target and no platform, give it
# the default platform
platform_default = spack.architecture.platform().name
for spec in specs:
for s in spec.traverse():
if s.architecture.os_string or s.architecture.target_string:
s._set_platform(spack.architecture.platform())
if s.architecture and not s.architecture.platform and \
(s.architecture.platform_os or s.architecture.target):
s._set_architecture(platform=platform_default)
return specs
def parse_compiler(self, text):
@ -2585,7 +2725,7 @@ def spec(self, name, check_valid_token=False):
spec.name = spec_name
spec.versions = VersionList()
spec.variants = VariantMap(spec)
spec.architecture = spack.architecture.Arch()
spec.architecture = None
spec.compiler = None
spec.external = None
spec.external_module = None

View File

@ -28,9 +28,11 @@
import llnl.util.tty as tty
import nose
import spack
import spack.architecture
from llnl.util.filesystem import join_path
from llnl.util.tty.colify import colify
from spack.test.tally_plugin import Tally
from spack.platforms.test import Test as TestPlatform
"""Names of tests to be included in Spack's test suite"""
# All the tests Spack knows about.
@ -84,6 +86,13 @@
]
def setup_tests():
"""Prepare the environment for the Spack tests to be run."""
test_platform = TestPlatform()
spack.architecture.real_platform = spack.architecture.platform
spack.architecture.platform = lambda: test_platform
def list_tests():
"""Return names of all tests that can be run for Spack."""
return test_names
@ -117,6 +126,8 @@ def run(names, outputDir, verbose=False):
runOpts += ["--with-xunit",
"--xunit-file={0}".format(xmlOutputPath)]
argv = [""] + runOpts + modules
setup_tests()
nose.run(argv=argv, addplugins=[tally])
succeeded = not tally.failCount and not tally.errorCount

View File

@ -54,10 +54,7 @@ def test_dict_functions_for_architecture(self):
arch.platform_os = arch.platform.operating_system('default_os')
arch.target = arch.platform.target('default_target')
d = arch.to_dict()
new_arch = spack.architecture.arch_from_dict(d)
new_arch = spack.architecture.Arch.from_dict(arch.to_dict())
self.assertEqual(arch, new_arch)
self.assertTrue(isinstance(arch, spack.architecture.Arch))
@ -75,7 +72,7 @@ def test_dict_functions_for_architecture(self):
spack.architecture.Target))
def test_platform(self):
output_platform_class = spack.architecture.platform()
output_platform_class = spack.architecture.real_platform()
if os.path.exists('/opt/cray/craype'):
my_platform_class = Cray()
elif os.path.exists('/bgsys'):
@ -114,10 +111,12 @@ def test_user_front_end_input(self):
"""Test when user inputs just frontend that both the frontend target
and frontend operating system match
"""
frontend_os = self.platform.operating_system("frontend")
frontend_target = self.platform.target("frontend")
frontend_os = str(self.platform.operating_system("frontend"))
frontend_target = str(self.platform.target("frontend"))
frontend_spec = Spec("libelf os=frontend target=frontend")
frontend_spec.concretize()
self.assertEqual(frontend_os, frontend_spec.architecture.platform_os)
self.assertEqual(frontend_target, frontend_spec.architecture.target)
@ -125,19 +124,22 @@ def test_user_back_end_input(self):
"""Test when user inputs backend that both the backend target and
backend operating system match
"""
backend_os = self.platform.operating_system("backend")
backend_target = self.platform.target("backend")
backend_os = str(self.platform.operating_system("backend"))
backend_target = str(self.platform.target("backend"))
backend_spec = Spec("libelf os=backend target=backend")
backend_spec.concretize()
self.assertEqual(backend_os, backend_spec.architecture.platform_os)
self.assertEqual(backend_target, backend_spec.architecture.target)
def test_user_defaults(self):
default_os = self.platform.operating_system("default_os")
default_target = self.platform.target("default_target")
default_os = str(self.platform.operating_system("default_os"))
default_target = str(self.platform.target("default_target"))
default_spec = Spec("libelf") # default is no args
default_spec.concretize()
self.assertEqual(default_os, default_spec.architecture.platform_os)
self.assertEqual(default_target, default_spec.architecture.target)
@ -156,8 +158,9 @@ def test_user_input_combination(self):
spec = Spec("libelf os=%s target=%s" % (o, t))
spec.concretize()
results.append(spec.architecture.platform_os ==
self.platform.operating_system(o))
results.append(spec.architecture.target == self.platform.target(t))
str(self.platform.operating_system(o)))
results.append(spec.architecture.target ==
str(self.platform.target(t)))
res = all(results)
self.assertTrue(res)

View File

@ -250,7 +250,7 @@ def test_external_package(self):
def test_external_package_module(self):
# No tcl modules on darwin/linux machines
# TODO: improved way to check for this.
platform = spack.architecture.platform().name
platform = spack.architecture.real_platform().name
if (platform == 'darwin' or platform == 'linux'):
return

View File

@ -132,15 +132,60 @@ def test_satisfies_compiler_version(self):
self.check_unsatisfiable('foo %gcc@4.7', '%gcc@4.7.3')
def test_satisfies_architecture(self):
self.check_satisfies(
'foo platform=test',
'platform=test')
self.check_satisfies(
'foo platform=linux',
'platform=linux')
self.check_satisfies(
'foo platform=test',
'platform=test target=frontend')
self.check_satisfies(
'foo platform=test',
'platform=test os=frontend target=frontend')
self.check_satisfies(
'foo platform=test os=frontend target=frontend',
'platform=test')
self.check_unsatisfiable(
'foo platform=linux',
'platform=test os=redhat6 target=x86_32')
self.check_unsatisfiable(
'foo os=redhat6',
'platform=test os=debian6 target=x86_64')
self.check_unsatisfiable(
'foo target=x86_64',
'platform=test os=redhat6 target=x86_32')
self.check_satisfies(
'foo arch=test-None-None',
'platform=test')
self.check_satisfies(
'foo arch=test-None-frontend',
'platform=test target=frontend')
self.check_satisfies(
'foo arch=test-frontend-frontend',
'platform=test os=frontend target=frontend')
self.check_satisfies(
'foo arch=test-frontend-frontend',
'platform=test')
self.check_unsatisfiable(
'foo arch=test-frontend-frontend',
'platform=test os=frontend target=backend')
self.check_satisfies(
'foo platform=test target=frontend os=frontend',
'platform=test target=frontend os=frontend')
self.check_satisfies(
'foo platform=test target=backend os=backend',
'platform=test target=backend', 'platform=test os=backend')
'platform=test target=backend os=backend')
self.check_satisfies(
'foo platform=test target=default_target os=default_os',
'platform=test target=default_target os=default_os')
'platform=test os=default_os')
self.check_unsatisfiable(
'foo platform=test target=x86_32 os=redhat6',
'platform=linux target=x86_32 os=redhat6')
def test_satisfies_dependencies(self):
self.check_satisfies('mpileaks^mpich', '^mpich')

View File

@ -120,6 +120,10 @@ def test_full_specs(self):
'mvapich_foo'
'^_openmpi@1.2:1.4,1.6%intel@12.1 cppflags="-O3"+debug~qt_4'
'^stackwalker@8.1_1e')
self.check_parse(
"mvapich_foo"
"^_openmpi@1.2:1.4,1.6%intel@12.1 debug=2~qt_4"
"^stackwalker@8.1_1e arch=test-redhat6-x86_32")
def test_canonicalize(self):
self.check_parse(
@ -144,6 +148,22 @@ def test_canonicalize(self):
"x^y@1,2:3,4%intel@1,2,3,4+a~b+c~d+e~f",
"x ^y~f+e~d+c~b+a@4,2:3,1%intel@4,3,2,1")
self.check_parse(
"x arch=test-redhat6-None"
"^y arch=test-None-x86_64"
"^z arch=linux-None-None",
"x os=fe"
"^y target=be"
"^z platform=linux")
self.check_parse(
"x arch=test-debian6-x86_64"
"^y arch=test-debian6-x86_64",
"x os=default_os target=default_target"
"^y os=default_os target=default_target")
self.check_parse("x^y", "x@: ^y@:")
def test_parse_errors(self):
@ -169,10 +189,12 @@ def test_duplicate_depdendence(self):
def test_duplicate_compiler(self):
self.assertRaises(DuplicateCompilerSpecError,
self.check_parse, "x%intel%intel")
self.assertRaises(DuplicateCompilerSpecError,
self.check_parse, "x%intel%gcc")
self.assertRaises(DuplicateCompilerSpecError,
self.check_parse, "x%gcc%intel")
self.assertRaises(DuplicateCompilerSpecError,
self.check_parse, "x ^y%intel%intel")
self.assertRaises(DuplicateCompilerSpecError,
@ -180,6 +202,54 @@ def test_duplicate_compiler(self):
self.assertRaises(DuplicateCompilerSpecError,
self.check_parse, "x ^y%gcc%intel")
def test_duplicate_architecture(self):
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x arch=linux-rhel7-x86_64 arch=linux-rhel7-x86_64")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x arch=linux-rhel7-x86_64 arch=linux-rhel7-ppc64le")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x arch=linux-rhel7-ppc64le arch=linux-rhel7-x86_64")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"y ^x arch=linux-rhel7-x86_64 arch=linux-rhel7-x86_64")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"y ^x arch=linux-rhel7-x86_64 arch=linux-rhel7-ppc64le")
def test_duplicate_architecture_component(self):
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x os=fe os=fe")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x os=fe os=be")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x target=fe target=fe")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x target=fe target=be")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x platform=test platform=test")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x platform=test platform=test")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x os=fe platform=test target=fe os=fe")
self.assertRaises(
DuplicateArchitectureError, self.check_parse,
"x target=be platform=test os=be os=fe")
# ========================================================================
# Lex checks
# ========================================================================