summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/cmd/__init__.py2
-rw-r--r--lib/spack/spack/cmd/test.py2
-rw-r--r--lib/spack/spack/parse.py4
-rw-r--r--lib/spack/spack/spec.py120
-rw-r--r--lib/spack/spack/tty.py69
5 files changed, 128 insertions, 69 deletions
diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py
index 9b034b2a60..96d02f7855 100644
--- a/lib/spack/spack/cmd/__init__.py
+++ b/lib/spack/spack/cmd/__init__.py
@@ -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:
diff --git a/lib/spack/spack/cmd/test.py b/lib/spack/spack/cmd/test.py
index bc62798dda..93f38bafe8 100644
--- a/lib/spack/spack/cmd/test.py
+++ b/lib/spack/spack/cmd/test.py
@@ -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)
diff --git a/lib/spack/spack/parse.py b/lib/spack/spack/parse.py
index e34f833bf6..5bcdced6ca 100644
--- a/lib/spack/spack/parse.py
+++ b/lib/spack/spack/parse.py
@@ -1,6 +1,5 @@
import re
import spack.error as err
-import spack.tty as tty
import itertools
@@ -11,9 +10,6 @@ class ParseError(err.SpackError):
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."""
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 86cab3430c..d46eb33201 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -42,8 +42,10 @@ Here is the EBNF grammar for a spec:
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 @@ thing. Spack uses ~variant in directory names and in the canonical form of
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 @@ class Compiler(object):
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 @@ class Spec(object):
"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.getvalue()
+
+ def write(self, stream=sys.stdout):
+ isatty = stream.isatty()
+ stream.write(self.stringify(color=isatty))
- return out
+ 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 @@ class SpecLexer(spack.parse.Lexer):
(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 @@ class SpecParser(spack.parse.Parser):
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
diff --git a/lib/spack/spack/tty.py b/lib/spack/spack/tty.py
index dc31c5456c..531f5660f7 100644
--- a/lib/spack/spack/tty.py
+++ b/lib/spack/spack/tty.py
@@ -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 debug(*args):
+ if spack.debug:
+ info(message, *args, format='*c')
-def verbose(msg, *args):
- if spack.verbose: info(msg, *args, color=green)
-def debug(*args):
- if spack.debug: msg(*args, color=red)
+def error(message, *args):
+ info(message, *args, format='*r')
-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 warn(message, *args):
+ info(message, *args, format='*Y')
-def die(msg, *args):
- error(msg, *args)
+
+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