From cf2f902b82a3080243eae58ca728b55189108c10 Mon Sep 17 00:00:00 2001
From: Todd Gamblin <tgamblin@llnl.gov>
Date: Sat, 28 May 2016 20:13:13 -0500
Subject: [PATCH] Make ProviderIndexes mergeable, so we can cache them
 per-repo.

---
 lib/spack/spack/repository.py   | 31 +++++++++++++++++++++++--------
 lib/spack/spack/test/virtual.py |  6 ++++++
 lib/spack/spack/virtual.py      | 28 ++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 8 deletions(-)

diff --git a/lib/spack/spack/repository.py b/lib/spack/spack/repository.py
index bf0dac6a22..6e7e95b8bc 100644
--- a/lib/spack/spack/repository.py
+++ b/lib/spack/spack/repository.py
@@ -224,12 +224,20 @@ def all_packages(self):
             yield self.get(name)
 
 
-    @_autospec
-    def providers_for(self, vpkg_spec):
+    @property
+    def provider_index(self):
+        """Merged ProviderIndex from all Repos in the RepoPath."""
         if self._provider_index is None:
-            self._provider_index = ProviderIndex(self.all_package_names())
+            self._provider_index = ProviderIndex()
+            for repo in reversed(self.repos):
+                self._provider_index.merge(repo.provider_index)
+
+        return self._provider_index
 
-        providers = self._provider_index.providers_for(vpkg_spec)
+
+    @_autospec
+    def providers_for(self, vpkg_spec):
+        providers = self.provider_index.providers_for(vpkg_spec)
         if not providers:
             raise UnknownPackageError(vpkg_spec.name)
         return providers
@@ -603,12 +611,19 @@ def purge(self):
         self._instances.clear()
 
 
-    @_autospec
-    def providers_for(self, vpkg_spec):
+    @property
+    def provider_index(self):
+        """A provider index with names *specific* to this repo."""
         if self._provider_index is None:
-            self._provider_index = ProviderIndex(self.all_package_names())
+            namespaced_names = ['%s.%s' % (self.namespace, n)
+                                for n in self.all_package_names()]
+            self._provider_index = ProviderIndex(namespaced_names)
+        return self._provider_index
+
 
-        providers = self._provider_index.providers_for(vpkg_spec)
+    @_autospec
+    def providers_for(self, vpkg_spec):
+        providers = self.provider_index.providers_for(vpkg_spec)
         if not providers:
             raise UnknownPackageError(vpkg_spec.name)
         return providers
diff --git a/lib/spack/spack/test/virtual.py b/lib/spack/spack/test/virtual.py
index 7699165554..1923e7006f 100644
--- a/lib/spack/spack/test/virtual.py
+++ b/lib/spack/spack/test/virtual.py
@@ -41,3 +41,9 @@ def test_write_and_read(self):
         q = ProviderIndex.from_yaml(istream)
 
         self.assertTrue(p == q)
+
+
+    def test_copy(self):
+        p = ProviderIndex(spack.repo.all_package_names())
+        q = p.copy()
+        self.assertTrue(p == q)
diff --git a/lib/spack/spack/virtual.py b/lib/spack/spack/virtual.py
index bf6d8227a4..2c47921a3f 100644
--- a/lib/spack/spack/virtual.py
+++ b/lib/spack/spack/virtual.py
@@ -209,5 +209,33 @@ def from_yaml(stream):
         return index
 
 
+    def merge(self, other):
+        """Merge `other` ProviderIndex into this one."""
+        other = other.copy()   # defensive copy.
+
+        for pkg in other.providers:
+            if pkg not in self.providers:
+                self.providers[pkg] = other.providers[pkg]
+                continue
+
+            spdict, opdict = self.providers[pkg], other.providers[pkg]
+            for provided_spec in opdict:
+                if provided_spec not in spdict:
+                    spdict[provided_spec] = opdict[provided_spec]
+                    continue
+
+                spdict[provided_spec] += opdict[provided_spec]
+
+
+    def copy(self):
+        """Deep copy of this ProviderIndex."""
+        clone = ProviderIndex()
+        clone.providers = dict(
+            (name, dict((vpkg, set((p.copy() for p in pset)))
+                        for vpkg, pset in pdict.items()))
+             for name, pdict in self.providers.items())
+        return clone
+
+
     def __eq__(self, other):
         return self.providers == other.providers
-- 
GitLab