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
This commit is contained in:
Todd Gamblin 2018-07-31 01:23:19 -07:00
parent ea7648ff84
commit 47e60d5ef8
5 changed files with 52 additions and 9 deletions

View File

@ -8,6 +8,7 @@
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 @@ def _specs(self, **kwargs):
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')

View File

@ -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(

View File

@ -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

View File

@ -851,7 +851,8 @@ def query(
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 @@ def query(
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 @@ def query(
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

View File

@ -31,6 +31,7 @@
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``.
"""