env: move add, remove, and concretize to top-level commands

This commit is contained in:
Todd Gamblin 2018-10-27 22:47:22 -07:00
parent e63b45b293
commit 8b549f664c
7 changed files with 155 additions and 104 deletions

View File

@ -0,0 +1,33 @@
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import argparse
import llnl.util.tty as tty
import spack.cmd
import spack.environment as ev
description = 'add a spec to an environment'
section = "environment"
level = "long"
def setup_parser(subparser):
subparser.add_argument(
'specs', nargs=argparse.REMAINDER, help="specs of packages to add")
def add(parser, args):
env = ev.get_env(args, 'add')
for spec in spack.cmd.parse_specs(args.specs):
if not env.add(spec):
tty.msg("Package {0} was already added to {1}"
.format(spec.name, env.name))
else:
tty.msg('Adding %s to environment %s' % (spec, env.name))
env.write()

View File

@ -0,0 +1,22 @@
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import spack.environment as ev
description = 'concretize an environment and write a lockfile'
section = "environment"
level = "long"
def setup_parser(subparser):
subparser.add_argument(
'-f', '--force', action='store_true',
help="Re-concretize even if already concretized.")
def concretize(parser, args):
env = ev.get_env(args, 'concretize')
env.concretize(force=args.force)
env.write()

View File

@ -5,7 +5,6 @@
import os import os
import sys import sys
import argparse
import llnl.util.tty as tty import llnl.util.tty as tty
import llnl.util.filesystem as fs import llnl.util.filesystem as fs
@ -33,9 +32,6 @@
'create', 'create',
'destroy', 'destroy',
['list', 'ls'], ['list', 'ls'],
'add',
['remove', 'rm'],
'concretize',
['status', 'st'], ['status', 'st'],
'loads', 'loads',
'stage', 'stage',
@ -270,68 +266,6 @@ def env_list(args):
colify(color_names, indent=4) colify(color_names, indent=4)
#
# env add
#
def env_add_setup_parser(subparser):
"""add a spec to an environment"""
subparser.add_argument(
'specs', nargs=argparse.REMAINDER, help="spec of the package to add")
def env_add(args):
env = ev.get_env(args, 'env add')
for spec in spack.cmd.parse_specs(args.specs):
if not env.add(spec):
tty.msg("Package {0} was already added to {1}"
.format(spec.name, env.name))
else:
tty.msg('Adding %s to environment %s' % (spec, env.name))
env.write()
#
# env remove
#
def env_remove_setup_parser(subparser):
"""remove a spec from an environment"""
subparser.add_argument(
'-a', '--all', action='store_true', dest='all',
help="Remove all specs from (clear) the environment")
subparser.add_argument(
'specs', nargs=argparse.REMAINDER, help="specs to be removed")
def env_remove(args):
env = ev.get_env(args, 'env remove <spec>')
if args.all:
env.clear()
else:
for spec in spack.cmd.parse_specs(args.specs):
tty.msg('Removing %s from environment %s' % (spec, env.name))
env.remove(spec)
env.write()
#
# env concretize
#
def env_concretize_setup_parser(subparser):
"""concretize user specs and write lockfile"""
subparser.add_argument(
'env', nargs='?', help='concretize all packages for this environment')
subparser.add_argument(
'-f', '--force', action='store_true',
help="Re-concretize even if already concretized.")
def env_concretize(args):
env = ev.get_env(args, 'env concretize')
env.concretize(force=args.force)
env.write()
# REMOVE # REMOVE
# env uninstall # env uninstall
# #
@ -456,7 +390,7 @@ def setup_parser(subparser):
setup_parser_cmd(subsubparser) setup_parser_cmd(subsubparser)
def env(parser, args, **kwargs): def env(parser, args):
"""Look for a function called environment_<name> and call it.""" """Look for a function called environment_<name> and call it."""
action = subcommand_functions[args.env_command] action = subcommand_functions[args.env_command]
action(args) action(args)

View File

@ -0,0 +1,39 @@
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import argparse
import llnl.util.tty as tty
import spack.cmd
import spack.environment as ev
description = 'remove specs from an environment'
section = "environment"
level = "long"
def setup_parser(subparser):
subparser.add_argument(
'-a', '--all', action='store_true',
help="remove all specs from (clear) the environment")
subparser.add_argument(
'-f', '--force', action='store_true',
help="remove concretized spec (if any) immediately")
subparser.add_argument(
'specs', nargs=argparse.REMAINDER, help="specs to be removed")
def remove(parser, args):
env = ev.get_env(args, 'remove')
if args.all:
env.clear()
else:
for spec in spack.cmd.parse_specs(args.specs):
tty.msg('Removing %s from environment %s' % (spec, env.name))
env.remove(spec, force=args.force)
env.write()

View File

@ -466,17 +466,31 @@ def add(self, user_spec):
self.user_specs.append(spec) self.user_specs.append(spec)
return bool(not existing) return bool(not existing)
def remove(self, query_spec): def remove(self, query_spec, force=False):
"""Remove specs from an environment that match a query_spec""" """Remove specs from an environment that match a query_spec"""
query_spec = Spec(query_spec) query_spec = Spec(query_spec)
matches = [s for s in self.user_specs if s.satisfies(query_spec)]
# try abstract specs first
matches = []
if not query_spec.concrete:
matches = [s for s in self.user_specs if s.satisfies(query_spec)]
if not matches:
# concrete specs match against concrete specs in the env
specs_hashes = zip(
self.concretized_user_specs, self.concretized_order)
matches = [
s for s, h in specs_hashes
if s.satisfies(query_spec) or query_spec.dag_hash() == h]
if not matches: if not matches:
raise EnvError("Not found: {0}".format(query_spec)) raise EnvError("Not found: {0}".format(query_spec))
for spec in matches: for spec in matches:
self.user_specs.remove(spec) if spec in self.user_specs:
if spec in self.concretized_user_specs: self.user_specs.remove(spec)
if force and spec in self.concretized_user_specs:
i = self.concretized_user_specs.index(spec) i = self.concretized_user_specs.index(spec)
del self.concretized_user_specs[i] del self.concretized_user_specs[i]

View File

@ -38,6 +38,11 @@
#: names of profile statistics #: names of profile statistics
stat_names = pstats.Stats.sort_arg_dict_default stat_names = pstats.Stats.sort_arg_dict_default
#: top-level aliases for Spack commands
aliases = {
'rm': 'remove'
}
#: help levels in order of detail (i.e., number of commands shown) #: help levels in order of detail (i.e., number of commands shown)
levels = ['short', 'long'] levels = ['short', 'long']
@ -174,8 +179,8 @@ def add_subcommand_group(title, commands):
cmd_set = set(c for c in commands) cmd_set = set(c for c in commands)
# make a dict of commands of interest # make a dict of commands of interest
cmds = dict((a.metavar, a) for a in self.actions cmds = dict((a.dest, a) for a in self.actions
if a.metavar in cmd_set) if a.dest in cmd_set)
# add commands to a group in order, and add the group # add commands to a group in order, and add the group
group = argparse._ArgumentGroup(self, title=title) group = argparse._ArgumentGroup(self, title=title)
@ -271,8 +276,13 @@ def add_command(self, cmd_name):
# each command module implements a parser() function, to which we # each command module implements a parser() function, to which we
# pass its subparser for setup. # pass its subparser for setup.
module = spack.cmd.get_module(cmd_name) module = spack.cmd.get_module(cmd_name)
# build a list of aliases
alias_list = [k for k, v in aliases.items() if v == cmd_name]
subparser = self.subparsers.add_parser( subparser = self.subparsers.add_parser(
cmd_name, help=module.description, description=module.description) cmd_name, aliases=alias_list,
help=module.description, description=module.description)
module.setup_parser(subparser) module.setup_parser(subparser)
# return the callable function for the command # return the callable function for the command
@ -647,6 +657,8 @@ def main(argv=None):
# Try to load the particular command the caller asked for. If there # Try to load the particular command the caller asked for. If there
# is no module for it, just die. # is no module for it, just die.
cmd_name = args.command[0] cmd_name = args.command[0]
cmd_name = aliases.get(cmd_name, cmd_name)
try: try:
command = parser.add_command(cmd_name) command = parser.add_command(cmd_name)
except ImportError: except ImportError:

View File

@ -21,9 +21,11 @@
pytestmark = pytest.mark.usefixtures( pytestmark = pytest.mark.usefixtures(
'mutable_mock_env_path', 'config', 'mutable_mock_packages') 'mutable_mock_env_path', 'config', 'mutable_mock_packages')
env = SpackCommand('env')
env = SpackCommand('env') install = SpackCommand('install')
install = SpackCommand('install') add = SpackCommand('add')
remove = SpackCommand('remove')
concretize = SpackCommand('concretize')
def test_add(): def test_add():
@ -137,32 +139,34 @@ def test_remove_after_concretize():
e.concretize() e.concretize()
e.remove('mpileaks') e.remove('mpileaks')
assert Spec('mpileaks') not in e.user_specs
env_specs = e._get_environment_specs() env_specs = e._get_environment_specs()
assert not any(x.name == 'mpileaks' for x in env_specs) assert any(s.name == 'mpileaks' for s in env_specs)
e.add('mpileaks')
assert any(s.name == 'mpileaks' for s in e.user_specs)
e.remove('mpileaks', force=True)
assert Spec('mpileaks') not in e.user_specs
env_specs = e._get_environment_specs()
assert not any(s.name == 'mpileaks' for s in env_specs)
def test_remove_command(): def test_remove_command():
env('create', 'test') env('create', 'test')
with ev.read('test'): with ev.read('test'):
env('add', 'mpileaks') add('mpileaks')
assert 'mpileaks' in env('status', 'test') assert 'mpileaks' in env('status', 'test')
with ev.read('test'): with ev.read('test'):
env('remove', 'mpileaks') remove('mpileaks')
assert 'mpileaks' not in env('status', 'test') assert 'mpileaks' not in env('status', 'test')
with ev.read('test'): with ev.read('test'):
env('add', 'mpileaks') add('mpileaks')
assert 'mpileaks' in env('status', 'test') assert 'mpileaks' in env('status', 'test')
env('concretize', 'test')
assert 'mpileaks' in env('status', 'test')
with ev.read('test'):
env('remove', 'mpileaks')
assert 'mpileaks' not in env('status', 'test')
def test_environment_status(): def test_environment_status():
e = ev.create('test') e = ev.create('test')
@ -195,7 +199,8 @@ def test_env_repo():
e.add('mpileaks') e.add('mpileaks')
e.write() e.write()
env('concretize', 'test') with ev.read('test'):
concretize()
package = e.repo.get('mpileaks') package = e.repo.get('mpileaks')
assert package.name == 'mpileaks' assert package.name == 'mpileaks'
@ -487,14 +492,12 @@ def test_env_loads(install_mockery, mock_fetch):
env('create', 'test') env('create', 'test')
with ev.read('test'): with ev.read('test'):
env('add', 'mpileaks') add('mpileaks')
concretize()
env('concretize', 'test')
with ev.read('test'):
install('--fake') install('--fake')
env('loads', 'test') with ev.read('test'):
env('loads', 'test')
e = ev.read('test') e = ev.read('test')
@ -510,9 +513,9 @@ def test_env_loads(install_mockery, mock_fetch):
def test_env_stage(mock_stage, mock_fetch, install_mockery): def test_env_stage(mock_stage, mock_fetch, install_mockery):
env('create', 'test') env('create', 'test')
with ev.read('test'): with ev.read('test'):
print env('add', 'mpileaks') add('mpileaks')
print env('add', 'zmpi') add('zmpi')
env('concretize', 'test') concretize()
env('stage', 'test') env('stage', 'test')
root = str(mock_stage) root = str(mock_stage)
@ -535,18 +538,12 @@ def test_env_commands_die_with_no_env_arg():
env('destroy') env('destroy')
# these have an optional env arg and raise errors via tty.die # these have an optional env arg and raise errors via tty.die
with pytest.raises(spack.main.SpackCommandError):
env('concretize')
with pytest.raises(spack.main.SpackCommandError): with pytest.raises(spack.main.SpackCommandError):
env('loads') env('loads')
with pytest.raises(spack.main.SpackCommandError): with pytest.raises(spack.main.SpackCommandError):
env('stage') env('stage')
with pytest.raises(spack.main.SpackCommandError): with pytest.raises(spack.main.SpackCommandError):
env('uninstall') env('uninstall')
with pytest.raises(spack.main.SpackCommandError):
env('add')
with pytest.raises(spack.main.SpackCommandError):
env('remove')
# This should NOT raise an error with no environment # This should NOT raise an error with no environment
# it just tells the user there isn't an environment # it just tells the user there isn't an environment