From 7383bd393eb36b67f308b643d1ce02b53ebb255d Mon Sep 17 00:00:00 2001
From: Todd Gamblin <tgamblin@llnl.gov>
Date: Fri, 27 Nov 2015 23:06:18 -0800
Subject: [PATCH] Fixed bug #42: problem with satisfies() for virtual
 dependencies.

- _cross_provider_maps() had suffered some bit rot (map returned was
  ill-formed but still worked for cases with one vdep)

- ProviderIndex.satisfies() was only checking whether the result map
  was non-empty.  It should check whether all common vdeps are *in*
  the result map, as that indicates there is *some* way to satisfy
  *all* of them.  We were checking whether there was some way to
  satisfy *any one* of them, which is wrong.

- Above would cause a problem when there is more than one vdep provider.

- Added test that covers this case.

- Added `constrained()` method to Spec. Analogous to `normalized()`:
  `constrain():constrained() :: normalize():normalized()`
---
 lib/spack/spack/spec.py                       |  7 ++++
 lib/spack/spack/test/spec_semantics.py        | 13 ++++++-
 lib/spack/spack/virtual.py                    |  9 +++--
 .../mock_packages/netlib-blas/package.py      | 36 ++++++++++++++++++
 .../mock_packages/netlib-lapack/package.py    | 37 +++++++++++++++++++
 var/spack/mock_packages/openblas/package.py   | 37 +++++++++++++++++++
 6 files changed, 134 insertions(+), 5 deletions(-)
 create mode 100644 var/spack/mock_packages/netlib-blas/package.py
 create mode 100644 var/spack/mock_packages/netlib-lapack/package.py
 create mode 100644 var/spack/mock_packages/openblas/package.py

diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 92880e9cbf..a10edcc6fc 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1207,6 +1207,13 @@ def common_dependencies(self, other):
         return common
 
 
+    def constrained(self, other, deps=True):
+        """Return a constrained copy without modifying this spec."""
+        clone = self.copy(deps=deps)
+        clone.constrain(other, deps)
+        return clone
+
+
     def dep_difference(self, other):
         """Returns dependencies in self that are not in other."""
         mine = set(s.name for s in self.traverse(root=False))
diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py
index 6666dbbb52..64220e5893 100644
--- a/lib/spack/spack/test/spec_semantics.py
+++ b/lib/spack/spack/test/spec_semantics.py
@@ -190,11 +190,23 @@ def test_unsatisfiable_variant_mismatch(self):
 
 
     def test_satisfies_virtual(self):
+        # Don't use check_satisfies: it checks constrain() too, and
+        # you can't constrain a non-virtual by a virtual.
         self.assertTrue(Spec('mpich').satisfies(Spec('mpi')))
         self.assertTrue(Spec('mpich2').satisfies(Spec('mpi')))
         self.assertTrue(Spec('zmpi').satisfies(Spec('mpi')))
 
 
+    def test_satisfies_virtual_dep_with_virtual_constraint(self):
+        """Ensure we can satisfy virtual constraints when there are multiple
+           vdep providers in the specs."""
+        self.assertTrue(Spec('netlib-lapack ^openblas').satisfies('netlib-lapack ^openblas'))
+        self.assertFalse(Spec('netlib-lapack ^netlib-blas').satisfies('netlib-lapack ^openblas'))
+
+        self.assertFalse(Spec('netlib-lapack ^openblas').satisfies('netlib-lapack ^netlib-blas'))
+        self.assertTrue(Spec('netlib-lapack ^netlib-blas').satisfies('netlib-lapack ^netlib-blas'))
+
+
     # ================================================================================
     # Indexing specs
     # ================================================================================
@@ -327,4 +339,3 @@ def test_constrain_dependency_not_changed(self):
         self.check_constrain_not_changed('libelf^foo+debug', 'libelf^foo+debug')
         self.check_constrain_not_changed('libelf^foo~debug', 'libelf^foo~debug')
         self.check_constrain_not_changed('libelf^foo=bgqos_0', 'libelf^foo=bgqos_0')
-
diff --git a/lib/spack/spack/virtual.py b/lib/spack/spack/virtual.py
index c77b259d61..f92cc4509c 100644
--- a/lib/spack/spack/virtual.py
+++ b/lib/spack/spack/virtual.py
@@ -117,12 +117,13 @@ def providers_for(self, *vpkg_specs):
         return sorted(providers)
 
 
-    # TODO: this is pretty darned nasty, and inefficient.
+    # TODO: this is pretty darned nasty, and inefficient, but there
+    # are not that many vdeps in most specs.
     def _cross_provider_maps(self, lmap, rmap):
         result = {}
         for lspec, rspec in itertools.product(lmap, rmap):
             try:
-                constrained = lspec.copy().constrain(rspec)
+                constrained = lspec.constrained(rspec)
             except spack.spec.UnsatisfiableSpecError:
                 continue
 
@@ -130,7 +131,7 @@ def _cross_provider_maps(self, lmap, rmap):
             for lp_spec, rp_spec in itertools.product(lmap[lspec], rmap[rspec]):
                 if lp_spec.name == rp_spec.name:
                     try:
-                        const = lp_spec.copy().constrain(rp_spec,deps=False)
+                        const = lp_spec.constrained(rp_spec, deps=False)
                         result.setdefault(constrained, set()).add(const)
                     except spack.spec.UnsatisfiableSpecError:
                         continue
@@ -157,4 +158,4 @@ def satisfies(self, other):
             if crossed:
                 result[name] = crossed
 
-        return bool(result)
+        return all(c in result for c in common)
diff --git a/var/spack/mock_packages/netlib-blas/package.py b/var/spack/mock_packages/netlib-blas/package.py
new file mode 100644
index 0000000000..199327812e
--- /dev/null
+++ b/var/spack/mock_packages/netlib-blas/package.py
@@ -0,0 +1,36 @@
+##############################################################################
+# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://scalability-llnl.github.io/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 General Public License (as published by
+# the Free Software Foundation) version 2.1 dated 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 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 spack import *
+
+class NetlibBlas(Package):
+    homepage = "http://www.netlib.org/lapack/"
+    url      = "http://www.netlib.org/lapack/lapack-3.5.0.tgz"
+
+    version('3.5.0', 'b1d3e3e425b2e44a06760ff173104bdf')
+
+    provides('blas')
+
+    def install(self, spec, prefix):
+        pass
diff --git a/var/spack/mock_packages/netlib-lapack/package.py b/var/spack/mock_packages/netlib-lapack/package.py
new file mode 100644
index 0000000000..8f7f236f1b
--- /dev/null
+++ b/var/spack/mock_packages/netlib-lapack/package.py
@@ -0,0 +1,37 @@
+##############################################################################
+# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://scalability-llnl.github.io/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 General Public License (as published by
+# the Free Software Foundation) version 2.1 dated 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 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 spack import *
+
+class NetlibLapack(Package):
+    homepage = "http://www.netlib.org/lapack/"
+    url      = "http://www.netlib.org/lapack/lapack-3.5.0.tgz"
+
+    version('3.5.0', 'b1d3e3e425b2e44a06760ff173104bdf')
+
+    provides('lapack')
+    depends_on('blas')
+
+    def install(self, spec, prefix):
+        pass
diff --git a/var/spack/mock_packages/openblas/package.py b/var/spack/mock_packages/openblas/package.py
new file mode 100644
index 0000000000..9c2fb30573
--- /dev/null
+++ b/var/spack/mock_packages/openblas/package.py
@@ -0,0 +1,37 @@
+##############################################################################
+# Copyright (c) 2013-2015, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://scalability-llnl.github.io/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 General Public License (as published by
+# the Free Software Foundation) version 2.1 dated 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 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 spack import *
+
+class Openblas(Package):
+    """OpenBLAS: An optimized BLAS library"""
+    homepage = "http://www.openblas.net"
+    url      = "http://github.com/xianyi/OpenBLAS/archive/v0.2.15.tar.gz"
+
+    version('0.2.15', 'b1190f3d3471685f17cfd1ec1d252ac9')
+
+    provides('blas')
+
+    def install(self, spec, prefix):
+        pass
-- 
GitLab