Changed the method in which architecture is converted from string to namedtuple. Also added some TODO comments to later consider changing how architecture is handled

This commit is contained in:
Mario Melara 2016-02-13 14:37:07 -08:00
parent 7732f375d2
commit c799301011

View file

@ -63,7 +63,7 @@
if it comes immediately after the compiler name. Otherwise it will be
associated with the current package spec.
6. The target to build with. This is needed on machines where
6. The architecture to build with. This is needed on machines where
cross-compilation is required
Here is the EBNF grammar for a spec::
@ -72,9 +72,9 @@
dep_list = { ^ spec }
spec = id [ options ]
options = { @version-list | +variant | -variant | ~variant |
%compiler | =target }
%compiler | =architecture }
variant = id
target = id
architecture = id
compiler = id [ version-list ]
version-list = version [ { , version } ]
version = id | id: | :id | id:id
@ -107,6 +107,7 @@
from llnl.util.tty.color import *
import spack
import spack.architecture
import spack.parse
import spack.error
import spack.compilers as compilers
@ -123,7 +124,7 @@
# Convenient names for color formats so that other things can use them
compiler_color = '@g'
version_color = '@c'
target_color = '@m'
architecture_color = '@m'
enabled_variant_color = '@B'
disabled_variant_color = '@r'
dependency_color = '@.'
@ -134,7 +135,7 @@
See spack.color for descriptions of the color codes. """
color_formats = {'%' : compiler_color,
'@' : version_color,
'=' : target_color,
'=' : architecture_color,
'+' : enabled_variant_color,
'~' : disabled_variant_color,
'^' : dependency_color,
@ -411,7 +412,7 @@ def __init__(self, spec_like, *dep_like, **kwargs):
self.name = other.name
self.dependents = other.dependents
self.versions = other.versions
self.target = other.target
self.architecture = other.architecture
self.compiler = other.compiler
self.dependencies = other.dependencies
self.variants = other.variants
@ -456,11 +457,11 @@ def _set_compiler(self, compiler):
self.compiler = compiler
def _set_target(self, target):
"""Called by the parser to set the target."""
if self.target: raise DuplicateTargetError(
"Spec for '%s' cannot have two targets." % self.name)
self.target = target # a string can be set
def _set_architecture(self, architecture):
"""Called by the parser to set the architecture."""
if self.architecture: raise DuplicateArchitectureError(
"Spec for '%s' cannot have two architectures." % self.name)
self.architecture = architecture # a string can be set
def _add_dependency(self, spec):
@ -516,7 +517,7 @@ def is_virtual(name):
@property
def concrete(self):
"""A spec is concrete if it can describe only ONE build of a package.
If any of the name, version, target, compiler,
If any of the name, version, architecture, compiler,
variants, or depdenencies are ambiguous,then it is not concrete.
"""
if self._concrete:
@ -525,7 +526,7 @@ def concrete(self):
self._concrete = bool(not self.virtual
and self.versions.concrete
and self.variants.concrete
and self.target
and self.architecture
and self.compiler and self.compiler.concrete
and self.dependencies.concrete)
return self._concrete
@ -660,10 +661,12 @@ def to_node_dict(self):
'dependencies' : dict((d, self.dependencies[d].dag_hash())
for d in sorted(self.dependencies))
}
if self.target:
d['target'] = self.target.target.to_dict()
if self.architecture:
# TODO: Fix the target.to_dict to account for the tuple
# Want it to be a dict of dicts
d['architecture'] = self.architecture.target.to_dict()
else:
d['target'] = None
d['architecture'] = None
if self.compiler:
d.update(self.compiler.to_dict())
else:
@ -689,7 +692,9 @@ def from_node_dict(node):
spec = Spec(name)
spec.versions = VersionList.from_dict(node)
spec.target = spack.architecture.Target.from_dict(node['target'])
# TODO: Need to fix the architecture.Target.from_dict
spec.architecture = spack.architecture.Target.from_dict(
node['architecture'])
if node['compiler'] is None:
spec.compiler = None
@ -760,7 +765,7 @@ def _concretize_helper(self, presets=None, visited=None):
# to presets below, their constraints will all be merged, but we'll
# still need to select a concrete package later.
changed |= any(
(spack.concretizer.concretize_target(self),
(spack.concretizer.concretize_architecture(self),
spack.concretizer.concretize_compiler(self),
spack.concretizer.concretize_version(self),
spack.concretizer.concretize_variants(self)))
@ -1149,10 +1154,11 @@ def constrain(self, other, deps=True):
raise UnsatisfiableVariantSpecError(self.variants[v],
other.variants[v])
if self.target is not None and other.target is not None:
if self.target != other.target:
raise UnsatisfiableTargetSpecError(self.target,
other.target)
# TODO: Check out the logic here
if self.architecture is not None and other.architecture is not None:
if self.architecture != other.architecture:
raise UnsatisfiableTargetSpecError(self.architecture,
other.architecture)
changed = False
if self.compiler is not None and other.compiler is not None:
@ -1164,9 +1170,9 @@ def constrain(self, other, deps=True):
changed |= self.versions.intersect(other.versions)
changed |= self.variants.constrain(other.variants)
old = self.target
self.target = self.target or other.target
changed |= (self.target != old)
old = self.architecture
self.architecture = self.architecture or other.architecture
changed |= (self.architecture != old)
if deps:
changed |= self._constrain_dependencies(other)
@ -1231,33 +1237,66 @@ def _autospec(self, spec_like):
except SpecError:
return parse_anonymous_spec(spec_like, self.name)
def _is_valid_platform(self, platform, platform_list):
if platform in platform_names:
return True
return False
def add_target_from_string(self, arch):
"""If only a target is provided, spack will assume the default architecture.
A platform-target pair can be input delimited by a '-'. If either portion of
a platform-target pair is empty, spack will supply a default, in the case of
a blank target the default will be dependent on the platform.
E.g. x86_64 -> 64 bit x86
bgq- -> default bgq target (back end/powerpc)
cray-hawswell -> haswell target on cray platform
def _is_valid_target(self, target, platform):
if target in platform.targets:
return True
return False
def add_architecture_from_string(self, arch):
""" The user is able to provide a architecture string of the form
platform-os-target. This string will be parsed by this function
and turn the architecture string into an architecture tuple of
platform, operating system and target processor classes.
The platform-os-target triplet can be delimited by a '-'. If any
portion of the architecture triplet is empty, spack will supply
the default. If the architecture is blank then defaults will
be provided based off of the platform
e.g
=linux-ubuntu10-x84_64 -> (linux, ubuntu10, x86_64)
=cray_xc-SuSE11-haswell -> (cray_xc, SuSE11, haswell)
=bgq -> (bgq,
default_os,
default_target)
=elcapitan -> (darwin, elcapitan, x86_64)
=x86_64 -> (autodetected platform,
default_os,
x86_64)
"""
Arch = namedtuple("Arch", "arch_os target")
platform = spack.architecture.sys_type()
platform_list = spack.architecture.all_platforms()
platform_names = [plat.__name__.lower() for plat in platform_list]
if arch is None:
return
if '-' in arch:
os_name, target = arch.split('-')
self.target = Arch(arch_os=platform.operating_system(os_name),
target = platform.target(target))
elif arch in platform.targets:
self.target = Arch(arch_os=platform.operating_system('default'),
target=platform.target(target))
# Get all the platforms to check whether string is valid
Arch = namedtuple("Arch", "platform platform_os target")
arch_list = arch.split("-")
platform = spack.architecture.sys_type()
for entry in arch_list:
if _is_valid_platform(entry, platform_names):
platform = entry()
elif _is_valid_target(entry, platform):
target = entry
else:
os_name = arch # Odd naming here, definitely need to change
self.target = Arch(arch_os=platform.operating_system(os_name),
target=platform.target('default'))
platform_os = entry
if target is None:
target = platform.target('default')
if platform_os is None:
platform_os = platform.operating_system('default')
self.architecture = Arch(platform=platform,
platform_os=platform_os,
target=target)
def satisfies(self, other, deps=True, strict=False):
"""determine if this spec satisfies all constraints of another.
@ -1306,15 +1345,16 @@ def satisfies(self, other, deps=True, strict=False):
# Target satisfaction is currently just class equality.
# If not strict, None means unconstrained.
if isinstance(self.target, basestring):
self.add_target_from_string(self.target)
if isinstance(other.target, basestring):
other.add_target_from_string(other.target)
if isinstance(self.architecture, basestring):
self.add_architecture_from_string(self.architecture)
if isinstance(other.architecture, basestring):
other.add_architecture_from_string(other.architecture)
if self.target and other.target:
if self.target != other.target:
# TODO: Need to make sure that comparisons can be made via classes
if self.architecture and other.architecture:
if self.architecture != other.architecture:
return False
elif strict and (other.target and not self.target):
elif strict and (other.architecture and not self.architecture):
return False
# If we need to descend into dependencies, do it, otherwise we're done.
@ -1384,11 +1424,12 @@ def _dup(self, other, **kwargs):
spec but not its dependencies.
"""
# TODO: Check if comparisons for tuple are valid
# We don't count dependencies as changes here
changed = True
if hasattr(self, 'name'):
changed = (self.name != other.name and self.versions != other.versions and \
self.target != other.target and self.compiler != other.compiler and \
self.architecture != other.architecture and self.compiler != other.compiler and \
self.variants != other.variants and self._normal != other._normal and \
self.concrete != other.concrete and self.external != other.external and \
self.external_module != other.external_module)
@ -1396,7 +1437,7 @@ def _dup(self, other, **kwargs):
# Local node attributes get copied first.
self.name = other.name
self.versions = other.versions.copy()
self.target = other.target
self.architecture = other.architecture
self.compiler = other.compiler.copy() if other.compiler else None
if kwargs.get('cleardeps', True):
self.dependents = DependencyMap()
@ -1526,7 +1567,7 @@ def ne_dag(self, other):
def _cmp_node(self):
"""Comparison key for just *this node* and not its deps."""
return (self.name, self.versions, self.variants,
self.target, self.compiler)
self.architecture, self.compiler)
def eq_node(self, other):
@ -1585,7 +1626,7 @@ def format(self, format_string='$_$@$%@$+$=', **kwargs):
Anything else is copied verbatim into the output stream.
*Example:* ``$_$@$+`` translates to the name, version, and options
of the package, but no dependencies, target, or compiler.
of the package, but no dependencies, architecture, or compiler.
TODO: allow, e.g., $6# to customize short hash length
TODO: allow, e.g., $## for full hash.
@ -1628,9 +1669,10 @@ def write(s, c):
elif c == '+':
if self.variants:
write(fmt % str(self.variants), c)
# TODO: Check string methods here
elif c == '=':
if self.target:
write(fmt % (c + str(self.target)), c)
if self.architecture:
write(fmt % (c + str(self.architecture)), c)
elif c == '#':
out.write('-' + fmt % (self.dag_hash(7)))
elif c == '$':
@ -2036,10 +2078,10 @@ def __init__(self, pkg, variant):
"Package %s has no variant %s!" % (pkg, variant))
class DuplicateTargetError(SpecError):
class DuplicateArchitectureError(SpecError):
"""Raised when the same target occurs in a spec twice."""
def __init__(self, message):
super(DuplicateTargetError, self).__init__(message)
super(DuplicateArchitectureError, self).__init__(message)
class InconsistentSpecError(SpecError):