diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py
index 9b034b2a6055ed4e56e91e9d8ab8c7bdbecb3e7d..96d02f7855b69beea7758d3b69654fc68d2950b4 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 bc62798dda9f0b8e823c93ee6c5fe20c2f5081c4..93f38bafe8cff641511b942f70919d2633819227 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 e34f833bf67ded639605ce424c3e53fe723cf15f..5bcdced6ca4f779461294d371e309fed16c2a956 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 @@ 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."""
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 86cab3430c4cd258c6f07cc7f2a7f0a0241fe4e9..d46eb332016cd710d49a7823e0383ff1d1bba819 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -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.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 @@ 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
diff --git a/lib/spack/spack/tty.py b/lib/spack/spack/tty.py
index dc31c5456cb978a7cae0b8ad9af7b4ab17fbe864..531f5660f7f31f8556d9b4a541495d3563f230f8 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