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