From 47e60d5ef8c539c3abe99c6d7d3d721730268397 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Tue, 31 Jul 2018 01:23:19 -0700 Subject: env: add --env argument to `spack find` - add a common argument for `-e/--env` - modify the database to support queries on subsets of hashes - allow `spack find` to be filtered by hashes in an environment --- lib/spack/spack/cmd/common/arguments.py | 20 +++++++++++++++++++- lib/spack/spack/cmd/find.py | 7 ++++--- lib/spack/spack/cmd/modules/__init__.py | 4 +--- lib/spack/spack/database.py | 15 +++++++++++++-- lib/spack/spack/environment.py | 15 +++++++++++++++ 5 files changed, 52 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/cmd/common/arguments.py b/lib/spack/spack/cmd/common/arguments.py index 7eeded8daa..84f61a12cc 100644 --- a/lib/spack/spack/cmd/common/arguments.py +++ b/lib/spack/spack/cmd/common/arguments.py @@ -8,6 +8,7 @@ import argparse import spack.cmd import spack.config +import spack.environment import spack.modules import spack.spec import spack.store @@ -42,16 +43,23 @@ class ConstraintAction(argparse.Action): To obtain the specs from a command the function must be called. """ - def __call__(self, parser, namespace, values, option_string=None): # Query specs from command line self.values = values namespace.constraint = values namespace.specs = self._specs + # env comes from EnvAction if --env is provided + self.env = None if not hasattr(namespace, 'env') else namespace.env + def _specs(self, **kwargs): qspecs = spack.cmd.parse_specs(self.values) + # If an environment is provided, we'll restrict the search to + # only its installed packages. + if self.env: + kwargs['hashes'] = set(self.env.specs_by_hash.keys()) + # return everything for an empty query. if not qspecs: return spack.store.db.query(**kwargs) @@ -66,6 +74,16 @@ class ConstraintAction(argparse.Action): return sorted(specs.values()) +class EnvAction(argparse.Action): + """Records the environment to which a command applies.""" + def __call__(self, parser, namespace, env_name, option_string=None): + namespace.env = spack.environment.read(env_name) + + +_arguments['env'] = Args( + '-e', '--env', action=EnvAction, default=None, + help="run this command on a specific environment") + _arguments['constraint'] = Args( 'constraint', nargs=argparse.REMAINDER, action=ConstraintAction, help='constraint to select a subset of installed packages') diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py index 0e8e2ef5f0..5c7a508a46 100644 --- a/lib/spack/spack/cmd/find.py +++ b/lib/spack/spack/cmd/find.py @@ -37,7 +37,8 @@ def setup_parser(subparser): const='deps', help='show full dependency DAG of installed packages') - arguments.add_common_arguments(subparser, ['long', 'very_long', 'tags']) + arguments.add_common_arguments( + subparser, ['env', 'long', 'very_long', 'tags']) subparser.add_argument('-f', '--show-flags', action='store_true', @@ -49,11 +50,11 @@ def setup_parser(subparser): help='show full compiler specs') implicit_explicit = subparser.add_mutually_exclusive_group() implicit_explicit.add_argument( - '-e', '--explicit', + '-x', '--explicit', action='store_true', help='show only specs that were installed explicitly') implicit_explicit.add_argument( - '-E', '--implicit', + '-X', '--implicit', action='store_true', help='show only specs that were installed as dependencies') subparser.add_argument( diff --git a/lib/spack/spack/cmd/modules/__init__.py b/lib/spack/spack/cmd/modules/__init__.py index 598130c4da..00f008b2cf 100644 --- a/lib/spack/spack/cmd/modules/__init__.py +++ b/lib/spack/spack/cmd/modules/__init__.py @@ -55,9 +55,7 @@ def setup_parser(subparser): help='prompt the list of modules associated with a constraint' ) add_loads_arguments(loads_parser) - arguments.add_common_arguments( - loads_parser, ['constraint'] - ) + arguments.add_common_arguments(loads_parser, ['constraint']) return sp diff --git a/lib/spack/spack/database.py b/lib/spack/spack/database.py index ae643ae72c..33942c362f 100644 --- a/lib/spack/spack/database.py +++ b/lib/spack/spack/database.py @@ -851,7 +851,8 @@ class Database(object): installed=True, explicit=any, start_date=None, - end_date=None + end_date=None, + hashes=None ): """Run a query on the database @@ -885,19 +886,26 @@ class Database(object): end_date (datetime, optional): filters the query discarding specs that have been installed after ``end_date``. + hashes (container): list or set of hashes that we can use to + restrict the search + Returns: list of specs that match the query + """ # TODO: Specs are a lot like queries. Should there be a # TODO: wildcard spec object, and should specs have attributes # TODO: like installed and known that can be queried? Or are # TODO: these really special cases that only belong here? + + # TODO: handling of hashes restriction is not particularly elegant. with self.read_transaction(): # Just look up concrete specs with hashes; no fancy search. if isinstance(query_spec, spack.spec.Spec) and query_spec.concrete: hash_key = query_spec.dag_hash() - if hash_key in self._data: + if (hash_key in self._data and + (not hashes or hash_key in hashes)): return [self._data[hash_key].spec] else: return [] @@ -909,6 +917,9 @@ class Database(object): end_date = end_date or datetime.datetime.max for key, rec in self._data.items(): + if hashes is not None and rec.spec.dag_hash() not in hashes: + continue + if installed is not any and rec.installed != installed: continue diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py index 4dc794b2e1..597fa14802 100644 --- a/lib/spack/spack/environment.py +++ b/lib/spack/spack/environment.py @@ -31,6 +31,7 @@ from six.moves import zip_longest import llnl.util.filesystem as fs import llnl.util.tty as tty +import spack.error import spack.repo import spack.schema.env import spack.util.spack_json as sjson @@ -389,9 +390,15 @@ def repair(environment_name): def read(environment_name): + """Read environment state from disk.""" # Check that env is in a consistent state on disk env_root = root(environment_name) + if not os.path.isdir(env_root): + raise EnvError("no such environment '%s'" % environment_name) + if not os.access(env_root, os.R_OK): + raise EnvError("can't read environment '%s'" % environment_name) + # Read env.yaml file env_yaml = spack.config._read_config_file( fs.join_path(env_root, 'env.yaml'), @@ -458,3 +465,11 @@ def prepare_config_scope(environment): tty.msg('Using Spack config %s scope at %s' % (config_name, config_dir)) spack.config.config.push_scope(ConfigScope(config_name, config_dir)) + + +class EnvError(spack.error.SpackError): + """Superclass for all errors to do with Spack environments. + + Note that this is called ``EnvError`` to distinguish it from the + builtin ``EnvironmentError``. + """ -- cgit v1.2.3-60-g2f50