From d6587ea36524805f9cd9364a1bd427cf5b492183 Mon Sep 17 00:00:00 2001
From: Harmen Stoppels <harmenstoppels@gmail.com>
Date: Thu, 13 Aug 2020 18:59:20 +0200
Subject: [PATCH] fix buildcache create for environments with uninstalled root
 specs (#17859)

* Handle uninstalled rootspecs in buildcache

- Do not parse specs / find matching specs when in an environment and no
package string is provided
- Error only when a spec.yaml or spec string are not installed. In an
environment it is fine when the root spec does not exist.
- When iterating through the matched specs, simply skip uninstalled
packages
---
 lib/spack/spack/cmd/buildcache.py | 34 +++++++++++++++++++------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/lib/spack/spack/cmd/buildcache.py b/lib/spack/spack/cmd/buildcache.py
index 7de2758082..1e0ccb27d4 100644
--- a/lib/spack/spack/cmd/buildcache.py
+++ b/lib/spack/spack/cmd/buildcache.py
@@ -239,8 +239,9 @@ def find_matching_specs(pkgs, allow_multiple_matches=False, env=None):
        concretized specs given from cli
 
     Args:
-        specs: list of specs to be matched against installed packages
-        allow_multiple_matches : if True multiple matches are admitted
+        pkgs (string): spec to be matched against installed packages
+        allow_multiple_matches (bool): if True multiple matches are admitted
+        env (Environment): active environment, or ``None`` if there is not one
 
     Return:
         list of specs
@@ -332,26 +333,25 @@ def _createtarball(env, spec_yaml=None, packages=None, add_spec=True,
                    signing_key=None, force=False, make_relative=False,
                    unsigned=False, allow_root=False, rebuild_index=False):
     if spec_yaml:
-        packages = set()
         with open(spec_yaml, 'r') as fd:
             yaml_text = fd.read()
             tty.debug('createtarball read spec yaml:')
             tty.debug(yaml_text)
             s = Spec.from_yaml(yaml_text)
-            packages.add('/{0}'.format(s.dag_hash()))
+            package = '/{0}'.format(s.dag_hash())
+            matches = find_matching_specs(package, env=env)
 
     elif packages:
-        packages = packages
+        matches = find_matching_specs(packages, env=env)
 
     elif env:
-        packages = env.concretized_user_specs
+        matches = [env.specs_by_hash[h] for h in env.concretized_order]
 
     else:
         tty.die("build cache file creation requires at least one" +
-                " installed package spec, an activate environment," +
+                " installed package spec, an active environment," +
                 " or else a path to a yaml file containing a spec" +
                 " to install")
-    pkgs = set(packages)
     specs = set()
 
     mirror = spack.mirror.MirrorCollection().lookup(output_location)
@@ -360,8 +360,6 @@ def _createtarball(env, spec_yaml=None, packages=None, add_spec=True,
     msg = 'Buildcache files will be output to %s/build_cache' % outdir
     tty.msg(msg)
 
-    matches = find_matching_specs(pkgs, env=env)
-
     if matches:
         tty.debug('Found at least one matching spec')
 
@@ -371,11 +369,16 @@ def _createtarball(env, spec_yaml=None, packages=None, add_spec=True,
             tty.debug('skipping external or virtual spec %s' %
                       match.format())
         else:
-            if add_spec:
+            lookup = spack.store.db.query_one(match)
+
+            if not add_spec:
+                tty.debug('skipping matching root spec %s' % match.format())
+            elif lookup is None:
+                tty.debug('skipping uninstalled matching spec %s' %
+                          match.format())
+            else:
                 tty.debug('adding matching spec %s' % match.format())
                 specs.add(match)
-            else:
-                tty.debug('skipping matching spec %s' % match.format())
 
             if not add_deps:
                 continue
@@ -388,9 +391,14 @@ def _createtarball(env, spec_yaml=None, packages=None, add_spec=True,
                 if d == 0:
                     continue
 
+                lookup = spack.store.db.query_one(node)
+
                 if node.external or node.virtual:
                     tty.debug('skipping external or virtual dependency %s' %
                               node.format())
+                elif lookup is None:
+                    tty.debug('skipping uninstalled depenendency %s' %
+                              node.format())
                 else:
                     tty.debug('adding dependency %s' % node.format())
                     specs.add(node)
-- 
GitLab