diff --git a/lib/spack/spack/repository.py b/lib/spack/spack/repository.py
index ae9fd7bee65df29eef839957baa5d7f5c056654a..bf0dac6a22b8190967cf2e66b37cb5652edffed4 100644
--- a/lib/spack/spack/repository.py
+++ b/lib/spack/spack/repository.py
@@ -51,6 +51,7 @@
 # These names describe how repos should be laid out in the filesystem.
 #
 repo_config_name   = 'repo.yaml'   # Top-level filename for repo config.
+repo_index_name    = 'index.yaml'  # Top-level filename for repository index.
 packages_dir_name  = 'packages'    # Top-level repo directory containing pkgs.
 package_file_name  = 'package.py'  # Filename for packages in a repository.
 
diff --git a/lib/spack/spack/test/__init__.py b/lib/spack/spack/test/__init__.py
index 3439764ee6e585408531276730de3e2d20b7bc3f..11f7298c04f91f2431d400c5cf6a5f3872c95dad 100644
--- a/lib/spack/spack/test/__init__.py
+++ b/lib/spack/spack/test/__init__.py
@@ -33,15 +33,49 @@
 """Names of tests to be included in Spack's test suite"""
 
 test_names = [
-    'architecture', 'versions', 'url_parse', 'url_substitution', 'packages',
-    'stage', 'spec_syntax', 'spec_semantics', 'spec_dag', 'concretize',
-    'multimethod', 'install', 'package_sanity', 'config', 'directory_layout',
-    'pattern', 'python_version', 'git_fetch', 'svn_fetch', 'hg_fetch',
-    'mirror', 'modules', 'url_extrapolate', 'cc', 'link_tree', 'spec_yaml',
-    'optional_deps', 'make_executable', 'build_system_guess', 'lock',
-    'database', 'namespace_trie', 'yaml', 'sbang', 'environment',
-    'concretize_preferences', 'cmd.find', 'cmd.uninstall', 'cmd.test_install',
-    'cmd.test_compiler_cmd', 'cmd.module'
+    'architecture',
+    'build_system_guess',
+    'cc',
+    'cmd.find',
+    'cmd.module',
+    'cmd.test_compiler_cmd',
+    'cmd.test_install',
+    'cmd.uninstall',
+    'concretize',
+    'concretize_preferences',
+    'config',
+    'configure_guess',
+    'database',
+    'directory_layout',
+    'environment',
+    'git_fetch',
+    'hg_fetch',
+    'install',
+    'link_tree',
+    'lock',
+    'make_executable',
+    'mirror',
+    'modules',
+    'multimethod',
+    'namespace_trie',
+    'optional_deps',
+    'package_sanity',
+    'packages',
+    'pattern',
+    'python_version',
+    'sbang',
+    'spec_dag',
+    'spec_semantics',
+    'spec_syntax',
+    'spec_yaml',
+    'stage',
+    'svn_fetch',
+    'url_extrapolate',
+    'url_parse',
+    'url_substitution',
+    'versions',
+    'virtual',
+    'yaml',
 ]
 
 
diff --git a/lib/spack/spack/test/virtual.py b/lib/spack/spack/test/virtual.py
new file mode 100644
index 0000000000000000000000000000000000000000..7699165554e0a103ec9499ace1c1b3a9fbb47466
--- /dev/null
+++ b/lib/spack/spack/test/virtual.py
@@ -0,0 +1,43 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the LICENSE file for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+from StringIO import StringIO
+import unittest
+
+import spack
+from spack.virtual import ProviderIndex
+
+
+class VirtualTest(unittest.TestCase):
+
+    def test_write_and_read(self):
+        p = ProviderIndex(spack.repo.all_package_names())
+
+        ostream = StringIO ()
+        p.to_yaml(ostream)
+
+        istream = StringIO(ostream.getvalue())
+        q = ProviderIndex.from_yaml(istream)
+
+        self.assertTrue(p == q)
diff --git a/lib/spack/spack/virtual.py b/lib/spack/spack/virtual.py
index bb8333f023501332ef182051d9f6c2dbaeb5f220..bf6d8227a424cfcfcc29815aca39ea7c5189e746 100644
--- a/lib/spack/spack/virtual.py
+++ b/lib/spack/spack/virtual.py
@@ -25,8 +25,12 @@
 """
 The ``virtual`` module contains utility classes for virtual dependencies.
 """
-import spack.spec
 import itertools
+import yaml
+from yaml.error import MarkedYAMLError
+
+import spack
+
 
 class ProviderIndex(object):
     """This is a dict of dicts used for finding providers of particular
@@ -45,10 +49,11 @@ class ProviderIndex(object):
        Calling providers_for(spec) will find specs that provide a
        matching implementation of MPI.
     """
-    def __init__(self, specs, **kwargs):
+    def __init__(self, specs=None, **kwargs):
         # TODO: come up with another name for this.  This "restricts" values to
         # the verbatim impu specs (i.e., it doesn't pre-apply package's constraints, and
         # keeps things as broad as possible, so it's really the wrong name)
+        if specs is None: specs = []
         self.restrict = kwargs.setdefault('restrict', False)
 
         self.providers = {}
@@ -64,7 +69,7 @@ def __init__(self, specs, **kwargs):
 
 
     def update(self, spec):
-        if type(spec) != spack.spec.Spec:
+        if not isinstance(spec, spack.spec.Spec):
             spec = spack.spec.Spec(spec)
 
         if not spec.name:
@@ -75,7 +80,8 @@ def update(self, spec):
 
         pkg = spec.package
         for provided_spec, provider_spec in pkg.provided.iteritems():
-            provider_spec.compiler_flags = spec.compiler_flags.copy()#We want satisfaction other than flags
+            # We want satisfaction other than flags
+            provider_spec.compiler_flags = spec.compiler_flags.copy()
             if provider_spec.satisfies(spec, deps=False):
                 provided_name = provided_spec.name
 
@@ -164,3 +170,44 @@ def satisfies(self, other):
                 result[name] = crossed
 
         return all(c in result for c in common)
+
+
+    def to_yaml(self, stream=None):
+        provider_list = dict(
+            (name, [[vpkg.to_node_dict(), [p.to_node_dict() for p in pset]]
+                    for vpkg, pset in pdict.items()])
+             for name, pdict in self.providers.items())
+
+        yaml.dump({'provider_index': {'providers': provider_list}},
+                  stream=stream)
+
+
+    @staticmethod
+    def from_yaml(stream):
+        try:
+            yfile = yaml.load(stream)
+        except MarkedYAMLError, e:
+            raise spack.spec.SpackYAMLError(
+                "error parsing YAML ProviderIndex cache:", str(e))
+
+        if not isinstance(yfile, dict):
+            raise spack.spec.SpackYAMLError(
+                "YAML ProviderIndex was not a dict.")
+
+        if not 'provider_index' in yfile:
+            raise spack.spec.SpackYAMLError(
+                "YAML ProviderIndex does not start with 'provider_index'")
+
+        index = ProviderIndex()
+        providers = yfile['provider_index']['providers']
+        index.providers = dict(
+            (name, dict((spack.spec.Spec.from_node_dict(vpkg),
+                         set(spack.spec.Spec.from_node_dict(p) for p in plist))
+                        for vpkg, plist in pdict_list))
+            for name, pdict_list in providers.items())
+
+        return index
+
+
+    def __eq__(self, other):
+        return self.providers == other.providers