Repository: spack/spack

Issue:
Patch file cache can get out of date when using two repos
### Steps to reproduce

(@becker33 -- this is the issue we chatted about)

When running spack with multiple repos, and a patch gets added to one of those repos, you can get errors when trying to stage from the other repo.  To reproduce:

```
#1. Get a fresh clone of spack:
%  git clone git@github.com:spack/spack.git
...
% source spack/share/spack/setup-env.sh
% spack debug report
* **Spack:** 0.17.0-473-e199d7ef6b
* **Python:** 3.6.8
* **Platform:** linux-rhel8-zen2
* **Concretizer:** clingo


#2. Concretize and stage llvm. This works.
% spack stage llvm@11.0.0
... 

#3. Make a repo with a llvm package and add it
% mkdir myrepo/packages/llvm
% echo "repo:\n  namespace: 'myrepo'\n" > myrepo/repo.yaml 
% echo "from spack import *\nfrom spack.pk.builtin.llvm import Llvm as BuiltinLlvm\nclass Llvm(BuiltinLlvm):\n  pass\n" > myrepo/packages/llvm/package.py
% spack repo add myrepo

#4. Concretize and stage myrepo.llvm. This works.
% spack stage myrepo.llvm@11.0.0

#5. Add a patch to builtin.llvm
echo a > spack/var/spack/repos.builtin/packages/llvm/mypatch.patch
Add the line 'patch("mypatch.patch")' to spack/var/spack/repos.builtin/packages/llvm/package.py

#6. Concretize and stage myrepo.llvm. This fails.
% spack stage myrepo.llvm@11.0.0
==> Error: Couldn't find patch for package myrepo.llvm with sha256: 87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7
```

### Error message

The full spack --debug output for the failing command is:

```
% spack --debug stage myrepo.llvm@11.0.0
==> [2021-12-09-17:45:08.653378] Imported stage from built-in commands
==> [2021-12-09-17:45:08.654000] Imported stage from built-in commands
==> [2021-12-09-17:45:08.655819] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/config.yaml
==> [2021-12-09-17:45:08.676321] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/bootstrap.yaml
==> [2021-12-09-17:45:08.686499] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/config.yaml
==> [2021-12-09-17:45:08.704312] DATABASE LOCK TIMEOUT: 3s
==> [2021-12-09-17:45:08.704353] PACKAGE LOCK TIMEOUT: No timeout
==> [2021-12-09-17:45:08.704647] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/bootstrap.yaml
==> [2021-12-09-17:45:08.711925] DATABASE LOCK TIMEOUT: 3s
==> [2021-12-09-17:45:08.711964] PACKAGE LOCK TIMEOUT: No timeout
==> [2021-12-09-17:45:08.712015] [BOOTSTRAP CONFIG SCOPE] name=_builtin
==> [2021-12-09-17:45:08.712286] [BOOTSTRAP CONFIG SCOPE] name=defaults, path=/usr/WS1/legendre/repopatch/spack/etc/spack/defaults
==> [2021-12-09-17:45:08.712320] [BOOTSTRAP CONFIG SCOPE] name=defaults/linux, path=/usr/WS1/legendre/repopatch/spack/etc/spack/defaults/linux
==> [2021-12-09-17:45:08.712347] [BOOTSTRAP CONFIG SCOPE] name=bootstrap, path=/g/g0/legendre/.spack/bootstrap/config
==> [2021-12-09-17:45:08.712369] [BOOTSTRAP CONFIG SCOPE] name=bootstrap/linux, path=/g/g0/legendre/.spack/bootstrap/config/linux
==> [2021-12-09-17:45:08.712611] Reading config file /g/g0/legendre/.spack/bootstrap/config/linux/compilers.yaml
==> [2021-12-09-17:45:08.739965] [BOOTSTRAP ROOT SPEC] clingo-bootstrap@spack+python %gcc target=x86_64
==> [2021-12-09-17:45:08.740008] [BOOTSTRAP MODULE clingo] Try importing from Python
==> [2021-12-09-17:45:08.740196] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/bootstrap.yaml
==> [2021-12-09-17:45:08.872038] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/config.yaml
==> [2021-12-09-17:45:08.933096] [BOOTSTRAP MODULE clingo] The installed spec "clingo-bootstrap@spack+python %gcc target=x86_64 ^python@3.6 /vcipwnf57slgoo7busvvkzjkk7vydeb5" provides the "clingo" Python module
==> [2021-12-09-17:45:08.933361] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/repos.yaml
==> [2021-12-09-17:45:08.935191] Reading config file /g/g0/legendre/.spack/repos.yaml
==> [2021-12-09-17:45:09.022109] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/config.yaml
==> [2021-12-09-17:45:10.352489] Reading config file /g/g0/legendre/.spack/linux/compilers.yaml
==> [2021-12-09-17:45:10.362283] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/packages.yaml
==> [2021-12-09-17:45:16.765757] build(llvm)
==> [2021-12-09-17:45:16.765884] build(cmake)
==> [2021-12-09-17:45:16.765912] build(hwloc)
==> [2021-12-09-17:45:16.765935] build(ncurses)
==> [2021-12-09-17:45:16.765958] build(ninja)
==> [2021-12-09-17:45:16.765979] build(perl-data-dumper)
==> [2021-12-09-17:45:16.766001] build(pkgconf)
==> [2021-12-09-17:45:16.766022] build(libffi)
==> [2021-12-09-17:45:16.766043] build(binutils)
==> [2021-12-09-17:45:16.766063] build(swig)
==> [2021-12-09-17:45:16.766084] build(libedit)
==> [2021-12-09-17:45:16.766104] build(python)
==> [2021-12-09-17:45:16.766125] build(perl)
==> [2021-12-09-17:45:16.766145] build(libtool)
==> [2021-12-09-17:45:16.766165] build(m4)
==> [2021-12-09-17:45:16.766185] build(pcre)
==> [2021-12-09-17:45:16.766206] build(diffutils)
==> [2021-12-09-17:45:16.766226] build(libxml2)
==> [2021-12-09-17:45:16.766246] build(libpciaccess)
==> [2021-12-09-17:45:16.766266] build(zlib)
==> [2021-12-09-17:45:16.766286] build(xz)
==> [2021-12-09-17:45:16.766306] build(expat)
==> [2021-12-09-17:45:16.766326] build(bzip2)
==> [2021-12-09-17:45:16.766346] build(openssl)
==> [2021-12-09-17:45:16.766365] build(gdbm)
==> [2021-12-09-17:45:16.766386] build(readline)
==> [2021-12-09-17:45:16.766406] build(sqlite)
==> [2021-12-09-17:45:16.766426] build(gettext)
==> [2021-12-09-17:45:16.766446] build(util-linux-uuid)
==> [2021-12-09-17:45:16.766468] build(berkeley-db)
==> [2021-12-09-17:45:16.766488] build(util-macros)
==> [2021-12-09-17:45:16.766508] build(libsigsegv)
==> [2021-12-09-17:45:16.766528] build(libiconv)
==> [2021-12-09-17:45:16.766549] build(libbsd)
==> [2021-12-09-17:45:16.766568] build(tar)
==> [2021-12-09-17:45:16.766588] build(libmd)
==> [2021-12-09-17:45:16.778132] Ordered hashes [llvm]: llvm/36/87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7, llvm/43/af41889be3db22ba7270c4418ab31089f449d85214de2b836fc9d0e36dbfa8a7, llvm/48/47284669559b98dd068b193b72a48add09112fcb90a9bfe5c609b79628a20d2b, llvm/49/7b286b164174a2eeb72931c7d9d246\
baef170f476d1abcedcec48a9c0692366c, llvm/51/6811ad441aa0221fb6f519550e7fc75a69a84998d44c1bea6d507605213c769c
==> [2021-12-09-17:45:16.778507] Ordered hashes [berkeley-db]: berkeley-db/123/b231fcc4d5cff05e5c3a4814f6a5af0e9a966428dc2176540d2c05aff41de522
==> [2021-12-09-17:45:16.778611] Ordered hashes [m4]: m4/105/bfdffa7c2eb01021d5849b36972c069693654ad826c1a20b53534009a4ec7a89, m4/106/9dc5fbd0d5cb1037ab1e6d0ecc74a30df218d0a94bdd5a02759a97f62daca573
==> [2021-12-09-17:45:16.779507] Ordered hashes [python]: python/64/0d98e93189bc278fbc37a50ed7f183bd8aaf249a8e1670a465f0db6bb4f8cf87, python/65/f2fd060afc4b4618fe8104c4c5d771f36dc55b1db5a4623785a4ea707ec72fb4, python/67/4c2457325f2b608b1b6a2c63087df8c26e07db3e3d493caf36a56f0ecf6fb768
==> [2021-12-09-17:45:16.779930] Ordered hashes [libffi]: libffi/142/26f26c6f29a7ce9bf370ad3ab2610f99365b4bdd7b82e7c31df41a3370d685c0
==> [2021-12-09-17:45:17.018900] Creating stage lock spack-stage-llvm-11.0.0-g6d6q42un3maqj7hkqcyjtutxmry4yf4
==> [2021-12-09-17:45:17.019186] Reading config file /usr/WS1/legendre/repopatch/spack/etc/spack/defaults/mirrors.yaml
==> [2021-12-09-17:45:17.121432] Using cached archive: /usr/WS1/legendre/repopatch/spack/var/spack/cache/_source-cache/archive/8a/8ad4ddbafac4f2c8f2ea523c2c4196f940e8e16f9e635210537582a48622a5d5.tar.gz
==> [2021-12-09-17:45:17.218663] NoSuchPatchError: Couldn't find patch for package myrepo.llvm with sha256: 87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7
==> [2021-12-09-17:45:17.218722] Error: Couldn't find patch for package myrepo.llvm with sha256: 87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7
Traceback (most recent call last):
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/main.py", line 882, in main
    return _main(argv)
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/main.py", line 865, in _main
    return _invoke_command(command, parser, args, unknown)
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/main.py", line 535, in _invoke_command
    return_val = command(parser, args)
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/cmd/stage.py", line 66, in stage
    package.do_stage()
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/package.py", line 1408, in do_stage
    self.do_fetch(mirror_only)
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/package.py", line 1395, in do_fetch
    for patch in self.spec.patches:
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/llnl/util/lang.py", line 190, in _memoized_function
    func.cache[args] = func(*args)
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/spec.py", line 3427, in patches
    patch = index.patch_for_package(sha256, self.package)
  File "/usr/WS1/legendre/repopatch/spack/lib/spack/spack/patch.py", line 383, in patch_for_package
    % (pkg.fullname, sha256))
spack.patch.NoSuchPatchError: Couldn't find patch for package myrepo.llvm with sha256: 87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7
```

### Information on your system

I reproduced this on a LLNL RHEL8 system.

### 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:

Resolving patch:
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -372,6 +372,21 @@ def fullname(self):
         """Name of this package, including the namespace"""
         return '%s.%s' % (self.namespace, self.name)
 
+    @property
+    def fullnames(self):
+        """
+        Fullnames for this package and any packages from which it inherits.
+        """
+        fullnames = []
+        for cls in inspect.getmro(self):
+            namespace = getattr(cls, 'namespace', None)
+            if namespace:
+                fullnames.append('%s.%s' % (namespace, self.name))
+            if namespace == 'builtin':
+                # builtin packages cannot inherit from other repos
+                break
+        return fullnames
+
     @property
     def name(self):
         """The name of this package.
@@ -871,6 +886,10 @@ def fullname(self):
         """Name of this package, including namespace: namespace.name."""
         return type(self).fullname
 
+    @property
+    def fullnames(self):
+        return type(self).fullnames
+
     @property
     def name(self):
         """Name of this package (the module without parent modules)."""
diff --git a/lib/spack/spack/patch.py b/lib/spack/spack/patch.py
--- a/lib/spack/spack/patch.py
+++ b/lib/spack/spack/patch.py
@@ -348,8 +348,12 @@ def patch_for_package(self, sha256, pkg):
                 "Couldn't find patch for package %s with sha256: %s"
                 % (pkg.fullname, sha256))
 
-        patch_dict = sha_index.get(pkg.fullname)
-        if not patch_dict:
+        # Find patches for this class or any class it inherits from
+        for fullname in pkg.fullnames:
+            patch_dict = sha_index.get(fullname)
+            if patch_dict:
+                break
+        else:
             raise NoSuchPatchError(
                 "Couldn't find patch for package %s with sha256: %s"
                 % (pkg.fullname, sha256))


Test patch: