Add spec architeccture, redo color output support.

This commit is contained in:
Todd Gamblin 2013-06-29 15:59:43 -07:00
parent ebc507dc6e
commit 157737efbe
5 changed files with 129 additions and 70 deletions

View file

@ -61,7 +61,7 @@ def parse_specs(args):
return spack.spec.parse(" ".join(args))
except spack.parse.ParseError, e:
e.print_error(sys.stdout)
tty.error(e.message, e.string, e.pos * " " + "^")
sys.exit(1)
except spack.spec.SpecError, e:

View file

@ -24,10 +24,10 @@ def test(parser, args):
spack.test.run(name, verbose=args.verbose)
elif not args.names:
print parser._subparsers
print "Available tests:"
colify(list_modules(spack.test_path))
else:
for name in args.names:
spack.test.run(name, verbose=args.verbose)

View file

@ -1,6 +1,5 @@
import re
import spack.error as err
import spack.tty as tty
import itertools
@ -11,9 +10,6 @@ def __init__(self, message, string, pos):
self.string = string
self.pos = pos
def print_error(self, out):
tty.error(self.message, self.string, self.pos * " " + "^")
class LexError(ParseError):
"""Raised when we don't know how to lex something."""

View file

@ -42,8 +42,10 @@
spec-list = { spec [ dep-list ] }
dep_list = { ^ spec }
spec = id [ options ]
options = { @version-list | +variant | -variant | ~variant | %compiler }
options = { @version-list | +variant | -variant | ~variant |
%compiler | =architecture }
variant = id
architecture = id
compiler = id [ version-list ]
version-list = version [ { , version } ]
version = id | id: | :id | id:id
@ -59,11 +61,22 @@
specs to avoid ambiguity. Both are provided because ~ can cause shell
expansion when it is the first character in an id typed on the command line.
"""
import sys
from functools import total_ordering
from StringIO import StringIO
import tty
import spack.parse
from spack.version import Version, VersionRange
import spack.error
from spack.version import Version, VersionRange
from spack.color import ColorStream
# Color formats for various parts of specs when using color output.
compiler_fmt = '@g'
version_fmt = '@c'
architecture_fmt = '@m'
variant_enabled_fmt = '@B'
variant_disabled_fmt = '@r'
class SpecError(spack.error.SpackError):
@ -86,6 +99,11 @@ class DuplicateCompilerError(SpecError):
def __init__(self, message):
super(DuplicateCompilerError, self).__init__(message)
class DuplicateArchitectureError(SpecError):
"""Raised when the same architecture occurs in a spec twice."""
def __init__(self, message):
super(DuplicateArchitectureError, self).__init__(message)
class Compiler(object):
def __init__(self, name):
@ -95,19 +113,28 @@ def __init__(self, name):
def add_version(self, version):
self.versions.append(version)
def __str__(self):
out = "%%%s" % self.name
def stringify(self, **kwargs):
color = kwargs.get("color", False)
out = StringIO()
out.write("%s{%%%s}" % (compiler_fmt, self.name))
if self.versions:
vlist = ",".join(str(v) for v in sorted(self.versions))
out += "@%s" % vlist
return out
out.write("%s{@%s}" % (compiler_fmt, vlist))
return out.getvalue()
def __str__(self):
return self.stringify()
class Spec(object):
def __init__(self, name):
self.name = name
self._package = None
self.versions = []
self.variants = {}
self.architecture = None
self.compiler = None
self.dependencies = {}
@ -124,37 +151,76 @@ def add_compiler(self, compiler):
"Spec for '%s' cannot have two compilers." % self.name)
self.compiler = compiler
def add_architecture(self, architecture):
if self.architecture: raise DuplicateArchitectureError(
"Spec for '%s' cannot have two architectures." % self.name)
self.architecture = architecture
def add_dependency(self, dep):
if dep.name in self.dependencies:
raise DuplicateDependencyError("Cannot depend on '%s' twice" % dep)
self.dependencies[dep.name] = dep
def __str__(self):
out = self.name
def canonicalize(self):
"""Ensures that the spec is in canonical form.
This means:
1. All dependencies of this package and of its dependencies are
in the dependencies list (transitive closure of deps).
2. All dependencies in the dependencies list are canonicalized.
This function also serves to validate the spec, in that it makes sure
that each package exists an that spec criteria don't violate package
criteria.
"""
pass
@property
def package(self):
if self._package == None:
self._package = packages.get(self.name)
return self._package
def stringify(self, **kwargs):
color = kwargs.get("color", False)
out = ColorStream(StringIO(), color)
out.write("%s" % self.name)
if self.versions:
vlist = ",".join(str(v) for v in sorted(self.versions))
out += "@%s" % vlist
out.write("%s{@%s}" % (version_fmt, vlist))
if self.compiler:
out += str(self.compiler)
out.write(self.compiler.stringify(color=color))
for name in sorted(self.variants.keys()):
enabled = self.variants[name]
if enabled:
out += '+%s' % name
out.write('%s{+%s}' % (variant_enabled_fmt, name))
else:
out += '~%s' % name
out.write('%s{~%s}' % (variant_disabled_fmt, name))
if self.architecture:
out.write("%s{=%s}" % (architecture_fmt, self.architecture))
for name in sorted(self.dependencies.keys()):
out += " ^%s" % str(self.dependencies[name])
dep = " ^" + self.dependencies[name].stringify(color=color)
out.write(dep, raw=True)
return out
return out.getvalue()
def write(self, stream=sys.stdout):
isatty = stream.isatty()
stream.write(self.stringify(color=isatty))
def __str__(self):
return self.stringify()
#
# These are possible token types in the spec grammar.
#
DEP, AT, COLON, COMMA, ON, OFF, PCT, ID = range(8)
DEP, AT, COLON, COMMA, ON, OFF, PCT, EQ, ID = range(9)
class SpecLexer(spack.parse.Lexer):
"""Parses tokens that make up spack specs."""
@ -168,6 +234,7 @@ def __init__(self):
(r'\-', lambda scanner, val: self.token(OFF, val)),
(r'\~', lambda scanner, val: self.token(OFF, val)),
(r'\%', lambda scanner, val: self.token(PCT, val)),
(r'\=', lambda scanner, val: self.token(EQ, val)),
(r'\w[\w.-]*', lambda scanner, val: self.token(ID, val)),
(r'\s+', lambda scanner, val: None)])
@ -206,24 +273,35 @@ def spec(self):
spec.add_version(version)
elif self.accept(ON):
self.expect(ID)
self.check_identifier()
spec.add_variant(self.token.value, True)
spec.add_variant(self.variant(), True)
elif self.accept(OFF):
self.expect(ID)
self.check_identifier()
spec.add_variant(self.token.value, False)
spec.add_variant(self.variant(), False)
elif self.accept(PCT):
spec.add_compiler(self.compiler())
elif self.accept(EQ):
spec.add_architecture(self.architecture())
else:
break
return spec
def variant(self):
self.expect(ID)
self.check_identifier()
return self.token.value
def architecture(self):
self.expect(ID)
self.check_identifier()
return self.token.value
def version(self):
start = None
end = None

View file

@ -1,61 +1,46 @@
import sys
import spack
from spack.color import cprint
indent = " "
def escape(s):
"""Returns a TTY escape code if stdout is a tty, otherwise empty string"""
if sys.stdout.isatty():
return "\033[{}m".format(s)
return ''
def msg(message, *args):
cprint("@*b{==>} @*w{%s}" % str(message))
for arg in args:
print indent + str(arg)
def color(n):
return escape("0;{}".format(n))
def bold(n):
return escape("1;{}".format(n))
def info(message, *args, **kwargs):
format = kwargs.get('format', '*b')
cprint("@%s{==>} %s" % (format, str(message)))
for arg in args:
print indent + str(arg)
def underline(n):
return escape("4;{}".format(n))
blue = bold(34)
white = bold(39)
red = bold(31)
yellow = underline(33)
green = bold(92)
gray = bold(30)
em = underline(39)
reset = escape(0)
def verbose(message, *args):
if spack.verbose:
info(message, *args, format='*g')
def msg(msg, *args, **kwargs):
color = kwargs.get("color", blue)
print "{}==>{} {}{}".format(color, white, str(msg), reset)
for arg in args: print indent + str(arg)
def info(msg, *args, **kwargs):
color = kwargs.get("color", blue)
print "{}==>{} {}".format(color, reset, str(msg))
for arg in args: print indent + str(arg)
def verbose(msg, *args):
if spack.verbose: info(msg, *args, color=green)
def debug(*args):
if spack.debug: msg(*args, color=red)
if spack.debug:
info(message, *args, format='*c')
def error(msg, *args):
print "{}Error{}: {}".format(red, reset, str(msg))
for arg in args: print indent + str(arg)
def warn(msg, *args):
print "{}Warning{}: {}".format(yellow, reset, str(msg))
for arg in args: print indent + str(arg)
def error(message, *args):
info(message, *args, format='*r')
def die(msg, *args):
error(msg, *args)
def warn(message, *args):
info(message, *args, format='*Y')
def die(message, *args):
error(message, *args)
sys.exit(1)
def pkg(msg):
def pkg(message):
"""Outputs a message with a package icon."""
import platform
from version import Version
@ -64,5 +49,5 @@ def pkg(msg):
if mac_ver and Version(mac_ver) >= Version('10.7'):
print u"\U0001F4E6" + indent,
else:
print '{}[+]{} '.format(green, reset),
print msg
cprint('@*g{[+]} ')
print message