Repository: spack/spack

Issue:
intel-oneapi-mkl: linking with the +cluster variant is broken for external mkl
### Steps to reproduce

1. Spec an external installation of MKL as `intel-oneapi-mkl +cluster` in `packages.yaml`
2. Install any package with the above `intel-oneapi-mkl +cluster` as a dependency.

This bug was introduced in https://github.com/spack/spack/pull/37386. Since externals don't have dependencies in their specs, the reference to mpi fails and results in an error.

It would be ideal to have a fix which accomplishes the same goal as the above PR but in a way which works for external MKL as well, as the PR is valuable for fixing build errors which result when building serial libraries, for example `blaspp ^intel-oneapi-mkl +cluster`.

### Error message

``` console
==> Error: KeyError: 'No spec with name mpi in intel-oneapi-mkl@2023.1.0%oneapi@2023.1.0+cluster~envmods~ilp64+shared build_system=generic arch=linux-sles15-x86_64'

.../spack/var/spack/repos/builtin/packages/intel-oneapi-mkl/package.py:168, in _find_mkl_libs:
        165
        166        resolved_libs = find_libraries(libs, lib_path, shared=shared)
        167        if "+cluster" in self.spec:
  >>    168            resolved_libs = resolved_libs + self.spec["mpi"].libs
        169        return resolved_libs
```

### Information on your system

* **Spack:** 0.20.0.dev0 (b1b7dab180ba0be240dd8983d8173fa1590ef080)
* **Python:** 3.6.15
* **Platform:** linux-sles15-icelake
* **Concretizer:** clingo

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have searched the issues of this repo and believe this is not a duplicate
- [X] I have run the failing commands in debug mode and reported the output


Discussion:
@rscohn2 fyi
Does:
```
spack install blaspp ^intel-oneapi-mkl +cluster ^intel-oneapi-mpi
```
work? I could omit `self.spec["mpi"}.libs` if MPI is not in the spec, and fall back to the old behavior of assuming the application added the mpi flags.
I guess you have another external package that is already linked against MPI, and that is why MKL does not need to provide the mpi flags.
@koysean: Can you check if this fixes the problem? I don't know a good way to test it. #38263 
@koysean Was this fixed for you by #38263 ?
Sorry about the delay in reviewing the PR. https://github.com/spack/spack/pull/38263 does seem to fix the immediate issue by making external MKL usable again. Unfortunately https://github.com/spack/spack/issues/35423 still persists with external MKL, so there is still some work to be done.

It seems like a more recent PR has once again made external MKL unusable for me, but I need to investigate further before updating or opening a separate issue.
https://github.com/spack/spack/pull/38554 is the culprit for the same reasons as before. That PR enforces `^mpi` for the `+cluster` variant by raising a RuntimeError if there is no mpi in the spec (and externals have no dependencies in the spec).

I'm not sure how the package can generate the correct link line for external MKL if it depends on the MPI implementation. This feature request looks like it could help with that problem: https://github.com/spack/spack/issues/38638
> This feature request looks like it could help with that problem: #38638

That would work. 

While we are waiting, I could add a variant:
```
intel-oneapi-mkl +cluster mpi=(mpich|openmpi)
```
and that would make it possible to pick the right libraries when using an external mkl. Would that work?
That seems like a reasonable workaround to me. Thank you for working through this!

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/intel-oneapi-mkl/package.py b/var/spack/repos/builtin/packages/intel-oneapi-mkl/package.py
--- a/var/spack/repos/builtin/packages/intel-oneapi-mkl/package.py
+++ b/var/spack/repos/builtin/packages/intel-oneapi-mkl/package.py
@@ -103,6 +103,14 @@ class IntelOneapiMkl(IntelOneApiLibraryPackage):
     variant(
         "cluster", default=False, description="Build with cluster support: scalapack, blacs, etc"
     )
+    variant(
+        "mpi_family",
+        default="none",
+        values=("none", "mpich", "openmpi"),
+        description="MPI family",
+        multi=False,
+    )
+
     variant(
         "threads",
         default="none",
@@ -184,16 +192,23 @@ def _find_mkl_libs(self, shared):
         if self.spec.satisfies("+cluster"):
             if any(
                 self.spec.satisfies(m)
-                for m in ["^intel-oneapi-mpi", "^intel-mpi", "^mpich", "^cray-mpich"]
+                for m in [
+                    "^intel-oneapi-mpi",
+                    "^intel-mpi",
+                    "^mpich",
+                    "^cray-mpich",
+                    "mpi_family=mpich",
+                ]
             ):
                 libs.append(self._xlp64_lib("libmkl_blacs_intelmpi"))
-            elif self.spec.satisfies("^openmpi"):
+            elif any(self.spec.satisfies(m) for m in ["^openmpi", "mpi_family=openmpi"]):
                 libs.append(self._xlp64_lib("libmkl_blacs_openmpi"))
             else:
                 raise RuntimeError(
                     (
-                        "intel-oneapi-mpi +cluster requires one of "
-                        "^intel-oneapi-mpi, ^intel-mpi, ^mpich, or ^openmpi"
+                        "intel-oneapi-mpi +cluster requires one of ^intel-oneapi-mpi, "
+                        "^intel-mpi, ^mpich, ^cray-mpich, mpi_family=mpich, ^openmpi, "
+                        "or mpi_family=openmpi"
                     )
                 )
 


Test patch: