From 4acdfeae61d278ecf682adee1b9842d58ce3035a Mon Sep 17 00:00:00 2001
From: Tom Scogland <scogland1@llnl.gov>
Date: Fri, 29 Apr 2016 10:21:55 -0700
Subject: [PATCH] track whether a package was installed "explicitly"

Adds a new attribute in the database to track whether a package was
installed explicitly or not, where explicitly is the user running `spack
install <package>` and implicitly is it being installed as a dependency.
It also adds arguments to `spack find` to find these packages such that
it should be possible to query the packages that were installed
implicitly and are not currently depended upon any longer.
---
 lib/spack/spack/cmd/find.py    | 15 ++++++++++++++-
 lib/spack/spack/cmd/install.py |  3 ++-
 lib/spack/spack/database.py    | 20 ++++++++++++--------
 lib/spack/spack/package.py     |  5 +++--
 4 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py
index a99012a275..6896e92ef5 100644
--- a/lib/spack/spack/cmd/find.py
+++ b/lib/spack/spack/cmd/find.py
@@ -57,6 +57,12 @@ def setup_parser(subparser):
         '-L', '--very-long', action='store_true',
         help='Show dependency hashes as well as versions.')
 
+    subparser.add_argument(
+        '-e', '--explicit', action='store_true',
+        help='Show only specs that were installed explicitly')
+    subparser.add_argument(
+        '-E', '--implicit', action='store_true',
+        help='Show only specs that were installed as dependencies')
     subparser.add_argument(
         '-u', '--unknown', action='store_true',
         help='Show only specs Spack does not have a package for.')
@@ -163,7 +169,14 @@ def find(parser, args):
         installed = any
     if args.unknown:
         known = False
-    q_args = { 'installed' : installed, 'known' : known }
+
+    explicit = None
+    if args.explicit:
+        explicit = False
+    if args.implicit:
+        explicit = True
+
+    q_args = { 'installed' : installed, 'known' : known, "explicit" : explicit }
 
     # Get all the specs the user asked for
     if not query_specs:
diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py
index fef21f15ba..9d3175786b 100644
--- a/lib/spack/spack/cmd/install.py
+++ b/lib/spack/spack/cmd/install.py
@@ -78,4 +78,5 @@ def install(parser, args):
                 ignore_deps=args.ignore_deps,
                 make_jobs=args.jobs,
                 verbose=args.verbose,
-                fake=args.fake)
+                fake=args.fake,
+                explicit=True)
diff --git a/lib/spack/spack/database.py b/lib/spack/spack/database.py
index ee4473e079..73bc8143c8 100644
--- a/lib/spack/spack/database.py
+++ b/lib/spack/spack/database.py
@@ -92,22 +92,24 @@ class InstallRecord(object):
     dependents left.
 
     """
-    def __init__(self, spec, path, installed, ref_count=0):
+    def __init__(self, spec, path, installed, ref_count=0, explicit=False):
         self.spec = spec
         self.path = str(path)
         self.installed = bool(installed)
         self.ref_count = ref_count
+        self.explicit = explicit
 
     def to_dict(self):
         return { 'spec'      : self.spec.to_node_dict(),
                  'path'      : self.path,
                  'installed' : self.installed,
-                 'ref_count' : self.ref_count }
+                 'ref_count' : self.ref_count,
+                 'explicit'  : self.explicit }
 
     @classmethod
     def from_dict(cls, spec, dictionary):
         d = dictionary
-        return InstallRecord(spec, d['path'], d['installed'], d['ref_count'])
+        return InstallRecord(spec, d['path'], d['installed'], d['ref_count'], d.get('explicit', False))
 
 
 class Database(object):
@@ -370,7 +372,7 @@ def _read(self):
             self.reindex(spack.install_layout)
 
 
-    def _add(self, spec, path, directory_layout=None):
+    def _add(self, spec, path, directory_layout=None, explicit=False):
         """Add an install record for spec at path to the database.
 
         This assumes that the spec is not already installed. It
@@ -392,7 +394,7 @@ def _add(self, spec, path, directory_layout=None):
             rec.path = path
 
         else:
-            self._data[key] = InstallRecord(spec, path, True)
+            self._data[key] = InstallRecord(spec, path, True, explicit=explicit)
             for dep in spec.dependencies.values():
                 self._increment_ref_count(dep, directory_layout)
 
@@ -415,7 +417,7 @@ def _increment_ref_count(self, spec, directory_layout=None):
         self._data[key].ref_count += 1
 
     @_autospec
-    def add(self, spec, path):
+    def add(self, spec, path, explicit=False):
         """Add spec at path to database, locking and reading DB to sync.
 
         ``add()`` will lock and read from the DB on disk.
@@ -424,7 +426,7 @@ def add(self, spec, path):
         # TODO: ensure that spec is concrete?
         # Entire add is transactional.
         with self.write_transaction():
-            self._add(spec, path)
+            self._add(spec, path, explicit=explicit)
 
 
     def _get_matching_spec_key(self, spec, **kwargs):
@@ -513,7 +515,7 @@ def installed_extensions_for(self, extendee_spec):
             # TODO: conditional way to do this instead of catching exceptions
 
 
-    def query(self, query_spec=any, known=any, installed=True):
+    def query(self, query_spec=any, known=any, installed=True, explicit=any):
         """Run a query on the database.
 
         ``query_spec``
@@ -553,6 +555,8 @@ def query(self, query_spec=any, known=any, installed=True):
             for key, rec in self._data.items():
                 if installed is not any and rec.installed != installed:
                     continue
+                if explicit is not any and rec.explicit != explicit:
+                    continue
                 if known is not any and spack.repo.exists(rec.spec.name) != known:
                     continue
                 if query_spec is any or rec.spec.satisfies(query_spec):
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 8167341127..d1c36d0670 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -857,7 +857,8 @@ def do_install(self,
                    skip_patch=False,
                    verbose=False,
                    make_jobs=None,
-                   fake=False):
+                   fake=False,
+                   explicit=False):
         """Called by commands to install a package and its dependencies.
 
         Package implementations should override install() to describe
@@ -995,7 +996,7 @@ def build_process():
 
         # note: PARENT of the build process adds the new package to
         # the database, so that we don't need to re-read from file.
-        spack.installed_db.add(self.spec, self.prefix)
+        spack.installed_db.add(self.spec, self.prefix, explicit=explicit)
 
     def sanity_check_prefix(self):
         """This function checks whether install succeeded."""
-- 
GitLab