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)) return spack.spec.parse(" ".join(args))
except spack.parse.ParseError, e: except spack.parse.ParseError, e:
e.print_error(sys.stdout) tty.error(e.message, e.string, e.pos * " " + "^")
sys.exit(1) sys.exit(1)
except spack.spec.SpecError, e: except spack.spec.SpecError, e:

View file

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

View file

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

View file

@ -42,8 +42,10 @@
spec-list = { spec [ dep-list ] } spec-list = { spec [ dep-list ] }
dep_list = { ^ spec } dep_list = { ^ spec }
spec = id [ options ] spec = id [ options ]
options = { @version-list | +variant | -variant | ~variant | %compiler } options = { @version-list | +variant | -variant | ~variant |
%compiler | =architecture }
variant = id variant = 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
@ -59,11 +61,22 @@
specs to avoid ambiguity. Both are provided because ~ can cause shell 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. expansion when it is the first character in an id typed on the command line.
""" """
import sys
from functools import total_ordering from functools import total_ordering
from StringIO import StringIO
import tty
import spack.parse import spack.parse
from spack.version import Version, VersionRange
import spack.error 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): class SpecError(spack.error.SpackError):
@ -86,6 +99,11 @@ class DuplicateCompilerError(SpecError):
def __init__(self, message): def __init__(self, message):
super(DuplicateCompilerError, self).__init__(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): class Compiler(object):
def __init__(self, name): def __init__(self, name):
@ -95,19 +113,28 @@ def __init__(self, name):
def add_version(self, version): def add_version(self, version):
self.versions.append(version) self.versions.append(version)
def __str__(self): def stringify(self, **kwargs):
out = "%%%s" % self.name color = kwargs.get("color", False)
out = StringIO()
out.write("%s{%%%s}" % (compiler_fmt, self.name))
if self.versions: if self.versions:
vlist = ",".join(str(v) for v in sorted(self.versions)) vlist = ",".join(str(v) for v in sorted(self.versions))
out += "@%s" % vlist out.write("%s{@%s}" % (compiler_fmt, vlist))
return out return out.getvalue()
def __str__(self):
return self.stringify()
class Spec(object): class Spec(object):
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
self._package = None
self.versions = [] self.versions = []
self.variants = {} self.variants = {}
self.architecture = None
self.compiler = None self.compiler = None
self.dependencies = {} self.dependencies = {}
@ -124,37 +151,76 @@ def add_compiler(self, compiler):
"Spec for '%s' cannot have two compilers." % self.name) "Spec for '%s' cannot have two compilers." % self.name)
self.compiler = compiler 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): def add_dependency(self, dep):
if dep.name in self.dependencies: if dep.name in self.dependencies:
raise DuplicateDependencyError("Cannot depend on '%s' twice" % dep) raise DuplicateDependencyError("Cannot depend on '%s' twice" % dep)
self.dependencies[dep.name] = dep self.dependencies[dep.name] = dep
def __str__(self): def canonicalize(self):
out = self.name """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: if self.versions:
vlist = ",".join(str(v) for v in sorted(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: if self.compiler:
out += str(self.compiler) out.write(self.compiler.stringify(color=color))
for name in sorted(self.variants.keys()): for name in sorted(self.variants.keys()):
enabled = self.variants[name] enabled = self.variants[name]
if enabled: if enabled:
out += '+%s' % name out.write('%s{+%s}' % (variant_enabled_fmt, name))
else: 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()): 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. # 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): class SpecLexer(spack.parse.Lexer):
"""Parses tokens that make up spack specs.""" """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(OFF, val)), (r'\~', lambda scanner, val: self.token(OFF, val)),
(r'\%', lambda scanner, val: self.token(PCT, 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'\w[\w.-]*', lambda scanner, val: self.token(ID, val)),
(r'\s+', lambda scanner, val: None)]) (r'\s+', lambda scanner, val: None)])
@ -206,24 +273,35 @@ def spec(self):
spec.add_version(version) spec.add_version(version)
elif self.accept(ON): elif self.accept(ON):
self.expect(ID) spec.add_variant(self.variant(), True)
self.check_identifier()
spec.add_variant(self.token.value, True)
elif self.accept(OFF): elif self.accept(OFF):
self.expect(ID) spec.add_variant(self.variant(), False)
self.check_identifier()
spec.add_variant(self.token.value, False)
elif self.accept(PCT): elif self.accept(PCT):
spec.add_compiler(self.compiler()) spec.add_compiler(self.compiler())
elif self.accept(EQ):
spec.add_architecture(self.architecture())
else: else:
break break
return spec 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): def version(self):
start = None start = None
end = None end = None

View file

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