Repository: spack/spack

Issue:
"Error: Detected uninstalled dependencies" when installing an environment
I have a weird issue happening when installing an environment: the environment has a bunch of packages, some of which depend on cmake. cmake is also listed as a spec in the environment file. In the middle of installing the packages in the environment, I’m getting this error:

```
==> Error: Detected uninstalled dependencies for cereal-1.3.0-x3tqv4abcgscvo525anvutu22piwh6ra: {'cmake-3.20.3-pnhys54luzct2iqdb53nqdknnr64nsub'}
==> Updating view at /home/runner/work/mochi-doc/mochi-doc/code/.spack-env/view
==> Warning: cereal-1.3.0-x3tqv4abcgscvo525anvutu22piwh6ra does NOT actually have any uninstalled deps left
==> Warning: [/home/runner/work/mochi-doc/mochi-doc/code/.spack-env/view] Skipping external package: cmake@3.20.3%gcc@10.2.0~doc+ncurses+openssl+ownlibs~qt arch=linux-ubuntu20.04-haswell/pnhys54
==> Warning: [/home/runner/work/mochi-doc/mochi-doc/code/.spack-env/view] Skipping external package: mpich@3.3.2%gcc@10.2.0~argobots~benvolio+fortran+hwloc~hydra+libxml2+pci+romio~slurm~verbs+wrapperrpath device=ch3 netmod=tcp patches=eb982de3366d48cbc55eb5e0df43373a45d9f51df208abf0835a72dc6c0b4774 pmi=pmi arch=linux-ubuntu20.04-haswell/oi7yaru
==> Error: Cannot proceed with cereal-1.3.0-x3tqv4abcgscvo525anvutu22piwh6ra: 1 uninstalled dependency: cmake-3.20.3-pnhys54luzct2iqdb53nqdknnr64nsub
```

The strange thing is that before this error happens, spack successfully installed a spec that also needed cmake.

cmake itself is listed as a spec of the package, at the end of the list of specs. If I make it the first spec to install, the problem does not happen. cmake is an external package in this context, not installed by spack but listed in my packayes.yaml file (I don't know that matters).

I don't have an easy reproducer for this problem; it happens in a github workflow that builds some code my team works on:
https://github.com/mochi-hpc/mochi-doc/
You can find the github workflow [here](https://github.com/mochi-hpc/mochi-doc/blob/main/.github/workflows/build.yml) (it's relatively simple). This workflow uses `spack external find` to find cmake, mpich, and perl, before jumping into the `code` directory and creating an environment using [spack.yaml](https://github.com/mochi-hpc/mochi-doc/blob/main/code/spack.yaml). With cmake listed at the top, everything works fine. But put it as last dependency and the problem above happens.


Discussion:

Resolving patch:
diff --git a/lib/spack/spack/repo.py b/lib/spack/spack/repo.py
--- a/lib/spack/spack/repo.py
+++ b/lib/spack/spack/repo.py
@@ -473,7 +473,6 @@ def __init__(self, *repos):
         self.repos = []
         self.by_namespace = nm.NamespaceTrie()
 
-        self._all_package_names = None
         self._provider_index = None
         self._patch_index = None
 
@@ -544,15 +543,17 @@ def first_repo(self):
         """Get the first repo in precedence order."""
         return self.repos[0] if self.repos else None
 
-    def all_package_names(self, include_virtuals=False):
+    @llnl.util.lang.memoized
+    def _all_package_names(self, include_virtuals):
         """Return all unique package names in all repositories."""
-        if self._all_package_names is None:
-            all_pkgs = set()
-            for repo in self.repos:
-                for name in repo.all_package_names(include_virtuals):
-                    all_pkgs.add(name)
-            self._all_package_names = sorted(all_pkgs, key=lambda n: n.lower())
-        return self._all_package_names
+        all_pkgs = set()
+        for repo in self.repos:
+            for name in repo.all_package_names(include_virtuals):
+                all_pkgs.add(name)
+        return sorted(all_pkgs, key=lambda n: n.lower())
+
+    def all_package_names(self, include_virtuals=False):
+        return self._all_package_names(include_virtuals)
 
     def packages_with_tags(self, *tags):
         r = set()


Test patch:
diff --git a/lib/spack/spack/test/repo.py b/lib/spack/spack/test/repo.py
--- a/lib/spack/spack/test/repo.py
+++ b/lib/spack/spack/test/repo.py
@@ -80,3 +80,9 @@ def test_namespace_hasattr(attr_name, exists, mutable_mock_repo):
     # of a custom __getattr__ implementation
     nms = spack.repo.SpackNamespace('spack.pkg.builtin.mock')
     assert hasattr(nms, attr_name) == exists
+
+
+@pytest.mark.regression('24552')
+def test_all_package_names_is_cached_correctly():
+    assert 'mpi' in spack.repo.all_package_names(include_virtuals=True)
+    assert 'mpi' not in spack.repo.all_package_names(include_virtuals=False)