reclaimed the = sign. Architectures now specified by +arch= instead. Decided to prepend flag names with + for clarity in spec names and ease of parsing. Also generalized variants, although there is not yet a way to specify a generalized (name=value) variant.

This commit is contained in:
Gregory Becker 2015-09-25 09:25:12 -07:00
parent 7989a7f903
commit db1b21b9aa
8 changed files with 104 additions and 60 deletions

View file

@ -72,7 +72,7 @@
dep_list = { ^ spec }
spec = id [ options ]
options = { @version-list | +variant | -variant | ~variant |
%compiler | =architecture }
%compiler | +arch=architecture | +flag=value}
variant = id
architecture = id
compiler = id [ version-list ]
@ -297,22 +297,25 @@ class VariantSpec(object):
on the particular package being built, and each named variant can
be enabled or disabled.
"""
def __init__(self, name, enabled):
def __init__(self, name, value):
self.name = name
self.enabled = enabled
self.value = value
def _cmp_key(self):
return (self.name, self.enabled)
return (self.name, self.value)
def copy(self):
return VariantSpec(self.name, self.enabled)
return VariantSpec(self.name, self.value)
def __str__(self):
out = '+' if self.enabled else '~'
if self.value in [True,False]:
out = '+' if self.value else '~'
return out + self.name
else:
return '+' + self.name + "=" + self.value
class VariantMap(HashableMap):
@ -323,10 +326,10 @@ def __init__(self, spec):
def satisfies(self, other, strict=False):
if strict or self.spec._concrete:
return all(k in self and self[k].enabled == other[k].enabled
return all(k in self and self[k].value == other[k].value
for k in other)
else:
return all(self[k].enabled == other[k].enabled
return all(self[k].value == other[k].value
for k in other if k in self)
@ -344,7 +347,7 @@ def constrain(self, other):
changed = False
for k in other:
if k in self:
if self[k].enabled != other[k].enabled:
if self[k].value != other[k].value:
raise UnsatisfiableVariantSpecError(self[k], other[k])
else:
self[k] = other[k].copy()
@ -437,12 +440,20 @@ def _add_version(self, version):
self.versions.add(version)
def _add_variant(self, name, enabled):
def _add_variant(self, name, value):
"""Called by the parser to add a variant."""
if name in self.variants: raise DuplicateVariantError(
"Cannot specify variant '%s' twice" % name)
self.variants[name] = VariantSpec(name, enabled)
self.variants[name] = VariantSpec(name, value)
def _add_flag(self, name, value):
"""Called by the parser to add a known flag.
Known flags currently include "arch"
"""
if name == 'arch':
self._set_architecture(value)
else:
raise SpecError("Invalid flag specified")
def _set_compiler(self, compiler):
"""Called by the parser to set the compiler."""
@ -653,7 +664,7 @@ def dag_hash(self, length=None):
def to_node_dict(self):
d = {
'variants' : dict(
(name,v.enabled) for name, v in self.variants.items()),
(name,v.value) for name, v in self.variants.items()),
'arch' : self.architecture,
'dependencies' : dict((d, self.dependencies[d].dag_hash())
for d in sorted(self.dependencies))
@ -690,8 +701,8 @@ def from_node_dict(node):
else:
spec.compiler = CompilerSpec.from_dict(node)
for name, enabled in node['variants'].items():
spec.variants[name] = VariantSpec(name, enabled)
for name, value in node['variants'].items():
spec.variants[name] = VariantSpec(name, value)
return spec
@ -804,6 +815,7 @@ def _expand_virtual_packages(self):
spec._replace_with(concrete)
changed = True
# If there are duplicate providers or duplicate provider deps, this
# consolidates them and merge constraints.
changed |= self.normalize(force=True)
@ -1138,7 +1150,7 @@ def constrain(self, other, deps=True):
"""
other = self._autospec(other)
if not self.name == other.name:
if not (self.name == other.name or self.name == "any-pkg-name" or other.name == "any-pkg-name"):
raise UnsatisfiableSpecNameError(self.name, other.name)
if not self.versions.overlaps(other.versions):
@ -1146,7 +1158,7 @@ def constrain(self, other, deps=True):
for v in other.variants:
if (v in self.variants and
self.variants[v].enabled != other.variants[v].enabled):
self.variants[v].value != other.variants[v].value):
raise UnsatisfiableVariantSpecError(self.variants[v],
other.variants[v])
@ -1228,7 +1240,10 @@ def _autospec(self, spec_like):
return spec_like
try:
return spack.spec.Spec(spec_like)
spec = spack.spec.Spec(spec_like)
if spec.name == "any-pkg-name":
raise SpecError("anonymous package -- this will always be handled")
return spec
except SpecError:
return parse_anonymous_spec(spec_like, self.name)
@ -1622,7 +1637,7 @@ def tree(self, **kwargs):
showid = kwargs.pop('ids', False)
cover = kwargs.pop('cover', 'nodes')
indent = kwargs.pop('indent', 0)
fmt = kwargs.pop('format', '$_$@$%@$+$=')
fmt = kwargs.pop('format', '$_$@$%@$+$+arch=')
prefix = kwargs.pop('prefix', None)
check_kwargs(kwargs, self.tree)
@ -1707,7 +1722,14 @@ def do_parse(self):
elif self.accept(ON):
specs.append(self.empty_spec())
specs[-1]._add_variant(self.variant(), True)
self.expect(ID)
self.check_identifier()
name = self.token.value
if self.accept(EQ):
self.expect(ID)
specs[-1]._add_flag(name,self.token.value)
else:
specs[-1]._add_variant(self.variant(name),True)
elif self.accept(OFF):
specs.append(self.empty_spec())
@ -1719,6 +1741,10 @@ def do_parse(self):
except spack.parse.ParseError, e:
raise SpecParseError(e)
for top_spec in specs:
for spec in top_spec.traverse():
if 'arch' in spec.variants:
spec.architecture = spec.variants['arch']
return specs
@ -1763,7 +1789,14 @@ def empty_spec(self):
#Should we be able to add cflags eventually?
while self.next:
if self.accept(ON):
spec._add_variant(self.variant(), True)
self.expect(ID)
self.check_identifier()
name = self.token.value
if self.accept(EQ):
self.expect(ID)
spec._add_flag(name,self.token.value)
else:
spec._add_variant(self.variant(name),True)
elif self.accept(OFF):
spec._add_variant(self.variant(),False)
@ -1805,8 +1838,16 @@ def spec(self):
spec._add_version(version)
added_version = True
elif self.accept(ON):
spec._add_variant(self.variant(), True)
self.expect(ID)
self.check_identifier()
name = self.token.value
if self.accept(EQ):
self.expect(ID)
spec._add_flag(name,self.token.value)
else:
spec._add_variant(self.variant(name),True)
elif self.accept(OFF):
spec._add_variant(self.variant(),False)
@ -1814,9 +1855,6 @@ def spec(self):
elif self.accept(PCT):
spec._set_compiler(self.compiler())
elif self.accept(EQ):
spec._set_architecture(self.architecture())
else:
break
@ -1827,13 +1865,17 @@ def spec(self):
return spec
def variant(self):
def variant(self,name=None):
#TODO: Make generalized variants possible
if name:
return name
else:
self.expect(ID)
self.check_identifier()
return self.token.value
def architecture(self):
#TODO: Make this work properly as a subcase of variant (includes adding names to grammar)
self.expect(ID)
return self.token.value

View file

@ -38,7 +38,7 @@ def check_spec(self, abstract, concrete):
for name in abstract.variants:
avariant = abstract.variants[name]
cvariant = concrete.variants[name]
self.assertEqual(avariant.enabled, cvariant.enabled)
self.assertEqual(avariant.value, cvariant.value)
for name in abstract.package.variants:
self.assertTrue(name in concrete.variants)

View file

@ -92,19 +92,19 @@ def test_default_works(self):
def test_architecture_match(self):
pkg = spack.db.get('multimethod=x86_64')
pkg = spack.db.get('multimethod+arch=x86_64')
self.assertEqual(pkg.different_by_architecture(), 'x86_64')
pkg = spack.db.get('multimethod=ppc64')
pkg = spack.db.get('multimethod+arch=ppc64')
self.assertEqual(pkg.different_by_architecture(), 'ppc64')
pkg = spack.db.get('multimethod=ppc32')
pkg = spack.db.get('multimethod+arch=ppc32')
self.assertEqual(pkg.different_by_architecture(), 'ppc32')
pkg = spack.db.get('multimethod=arm64')
pkg = spack.db.get('multimethod+arch=arm64')
self.assertEqual(pkg.different_by_architecture(), 'arm64')
pkg = spack.db.get('multimethod=macos')
pkg = spack.db.get('multimethod+arch=macos')
self.assertRaises(NoSuchMethodError, pkg.different_by_architecture)

View file

@ -241,8 +241,8 @@ def test_unsatisfiable_compiler_version(self):
def test_unsatisfiable_architecture(self):
set_pkg_dep('mpileaks', 'mpich=bgqos_0')
spec = Spec('mpileaks ^mpich=sles_10_ppc64 ^callpath ^dyninst ^libelf ^libdwarf')
set_pkg_dep('mpileaks', 'mpich+arch=bgqos_0')
spec = Spec('mpileaks ^mpich+arch=sles_10_ppc64 ^callpath ^dyninst ^libelf ^libdwarf')
self.assertRaises(spack.spec.UnsatisfiableArchitectureSpecError, spec.normalize)
@ -426,6 +426,7 @@ def test_copy_concretized(self):
orig.concretize()
copy = orig.copy()
print orig
self.check_links(copy)
self.assertEqual(orig, copy)

View file

@ -111,11 +111,11 @@ def test_satisfies_compiler_version(self):
def test_satisfies_architecture(self):
self.check_satisfies('foo=chaos_5_x86_64_ib', '=chaos_5_x86_64_ib')
self.check_satisfies('foo=bgqos_0', '=bgqos_0')
self.check_satisfies('foo+arch=chaos_5_x86_64_ib', '+arch=chaos_5_x86_64_ib')
self.check_satisfies('foo+arch=bgqos_0', '+arch=bgqos_0')
self.check_unsatisfiable('foo=bgqos_0', '=chaos_5_x86_64_ib')
self.check_unsatisfiable('foo=chaos_5_x86_64_ib', '=bgqos_0')
self.check_unsatisfiable('foo+arch=bgqos_0', '+arch=chaos_5_x86_64_ib')
self.check_unsatisfiable('foo+arch=chaos_5_x86_64_ib', '+arch=bgqos_0')
def test_satisfies_dependencies(self):
@ -267,13 +267,13 @@ def test_constrain_variants(self):
def test_constrain_arch(self):
self.check_constrain('libelf=bgqos_0', 'libelf=bgqos_0', 'libelf=bgqos_0')
self.check_constrain('libelf=bgqos_0', 'libelf', 'libelf=bgqos_0')
self.check_constrain('libelf+arch=bgqos_0', 'libelf+arch=bgqos_0', 'libelf+arch=bgqos_0')
self.check_constrain('libelf+arch=bgqos_0', 'libelf', 'libelf+arch=bgqos_0')
def test_constrain_compiler(self):
self.check_constrain('libelf=bgqos_0', 'libelf=bgqos_0', 'libelf=bgqos_0')
self.check_constrain('libelf=bgqos_0', 'libelf', 'libelf=bgqos_0')
self.check_constrain('libelf+arch=bgqos_0', 'libelf+arch=bgqos_0', 'libelf+arch=bgqos_0')
self.check_constrain('libelf+arch=bgqos_0', 'libelf', 'libelf+arch=bgqos_0')
def test_invalid_constraint(self):
@ -283,7 +283,7 @@ def test_invalid_constraint(self):
self.check_invalid_constraint('libelf+debug', 'libelf~debug')
self.check_invalid_constraint('libelf+debug~foo', 'libelf+debug+foo')
self.check_invalid_constraint('libelf=bgqos_0', 'libelf=x86_54')
self.check_invalid_constraint('libelf+arch=bgqos_0', 'libelf+arch=x86_54')
def test_constrain_changed(self):
@ -293,7 +293,7 @@ def test_constrain_changed(self):
self.check_constrain_changed('libelf%gcc', '%gcc@4.5')
self.check_constrain_changed('libelf', '+debug')
self.check_constrain_changed('libelf', '~debug')
self.check_constrain_changed('libelf', '=bgqos_0')
self.check_constrain_changed('libelf', '+arch=bgqos_0')
def test_constrain_not_changed(self):
@ -304,7 +304,7 @@ def test_constrain_not_changed(self):
self.check_constrain_not_changed('libelf%gcc@4.5', '%gcc@4.5')
self.check_constrain_not_changed('libelf+debug', '+debug')
self.check_constrain_not_changed('libelf~debug', '~debug')
self.check_constrain_not_changed('libelf=bgqos_0', '=bgqos_0')
self.check_constrain_not_changed('libelf+arch=bgqos_0', '+arch=bgqos_0')
self.check_constrain_not_changed('libelf^foo', 'libelf^foo')
self.check_constrain_not_changed('libelf^foo^bar', 'libelf^foo^bar')
@ -316,7 +316,7 @@ def test_constrain_dependency_changed(self):
self.check_constrain_changed('libelf^foo%gcc', 'libelf^foo%gcc@4.5')
self.check_constrain_changed('libelf^foo', 'libelf^foo+debug')
self.check_constrain_changed('libelf^foo', 'libelf^foo~debug')
self.check_constrain_changed('libelf^foo', 'libelf^foo=bgqos_0')
self.check_constrain_changed('libelf^foo', 'libelf^foo+arch=bgqos_0')
def test_constrain_dependency_not_changed(self):
@ -326,5 +326,5 @@ def test_constrain_dependency_not_changed(self):
self.check_constrain_not_changed('libelf^foo%gcc@4.5', 'libelf^foo%gcc@4.5')
self.check_constrain_not_changed('libelf^foo+debug', 'libelf^foo+debug')
self.check_constrain_not_changed('libelf^foo~debug', 'libelf^foo~debug')
self.check_constrain_not_changed('libelf^foo=bgqos_0', 'libelf^foo=bgqos_0')
self.check_constrain_not_changed('libelf^foo+arch=bgqos_0', 'libelf^foo+arch=bgqos_0')

View file

@ -70,6 +70,7 @@ def check_parse(self, expected, spec=None):
spec = expected
output = spack.spec.parse(spec)
parsed = (" ".join(str(spec) for spec in output))
print output, parsed
self.assertEqual(expected, parsed)

View file

@ -32,5 +32,5 @@
class Variant(object):
"""Represents a variant on a build. Can be either on or off."""
def __init__(self, default, description):
self.default = bool(default)
self.default = default
self.description = str(description)

View file

@ -103,19 +103,19 @@ def has_a_default(self):
#
# Make sure we can switch methods on different architectures
#
@when('=x86_64')
@when('+arch=x86_64')
def different_by_architecture(self):
return 'x86_64'
@when('=ppc64')
@when('+arch=ppc64')
def different_by_architecture(self):
return 'ppc64'
@when('=ppc32')
@when('+arch=ppc32')
def different_by_architecture(self):
return 'ppc32'
@when('=arm64')
@when('+arch=arm64')
def different_by_architecture(self):
return 'arm64'