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

View file

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

View file

@ -92,19 +92,19 @@ def test_default_works(self):
def test_architecture_match(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') 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') 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') 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') 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) self.assertRaises(NoSuchMethodError, pkg.different_by_architecture)

View file

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

View file

@ -111,11 +111,11 @@ def test_satisfies_compiler_version(self):
def test_satisfies_architecture(self): def test_satisfies_architecture(self):
self.check_satisfies('foo=chaos_5_x86_64_ib', '=chaos_5_x86_64_ib') self.check_satisfies('foo+arch=chaos_5_x86_64_ib', '+arch=chaos_5_x86_64_ib')
self.check_satisfies('foo=bgqos_0', '=bgqos_0') 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+arch=bgqos_0', '+arch=chaos_5_x86_64_ib')
self.check_unsatisfiable('foo=chaos_5_x86_64_ib', '=bgqos_0') self.check_unsatisfiable('foo+arch=chaos_5_x86_64_ib', '+arch=bgqos_0')
def test_satisfies_dependencies(self): def test_satisfies_dependencies(self):
@ -267,13 +267,13 @@ def test_constrain_variants(self):
def test_constrain_arch(self): def test_constrain_arch(self):
self.check_constrain('libelf=bgqos_0', 'libelf=bgqos_0', 'libelf=bgqos_0') self.check_constrain('libelf+arch=bgqos_0', 'libelf+arch=bgqos_0', 'libelf+arch=bgqos_0')
self.check_constrain('libelf=bgqos_0', 'libelf', 'libelf=bgqos_0') self.check_constrain('libelf+arch=bgqos_0', 'libelf', 'libelf+arch=bgqos_0')
def test_constrain_compiler(self): def test_constrain_compiler(self):
self.check_constrain('libelf=bgqos_0', 'libelf=bgqos_0', 'libelf=bgqos_0') self.check_constrain('libelf+arch=bgqos_0', 'libelf+arch=bgqos_0', 'libelf+arch=bgqos_0')
self.check_constrain('libelf=bgqos_0', 'libelf', 'libelf=bgqos_0') self.check_constrain('libelf+arch=bgqos_0', 'libelf', 'libelf+arch=bgqos_0')
def test_invalid_constraint(self): 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', 'libelf~debug')
self.check_invalid_constraint('libelf+debug~foo', 'libelf+debug+foo') 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): 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%gcc', '%gcc@4.5')
self.check_constrain_changed('libelf', '+debug') self.check_constrain_changed('libelf', '+debug')
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): 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%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~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', 'libelf^foo')
self.check_constrain_not_changed('libelf^foo^bar', 'libelf^foo^bar') 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%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~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): 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%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~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 spec = expected
output = spack.spec.parse(spec) output = spack.spec.parse(spec)
parsed = (" ".join(str(spec) for spec in output)) parsed = (" ".join(str(spec) for spec in output))
print output, parsed
self.assertEqual(expected, parsed) self.assertEqual(expected, parsed)

View file

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

View file

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