Improve spack find
output in environments (#42334)
This adds some improvements to `spack find` output when in environments based around some thoughts about what users want to know when they're in an env. If you're working in an enviroment, you mostly care about: * What are the roots * Which ones are installed / not installed * What's been added that still needs to be concretized So, this PR adds a couple tweaks to display that information more clearly: - [x] We now display install status next to every root. You can easily see which are installed and which aren't. - [x] When you run `spack find -l` in an env, the roots now show their concrete hash (if they've been concretized). They previously would show `-------` (b/c the root spec itself is abstract), but showing the concretized root's hash is a lot more useful. - [x] Newly added/unconcretized specs still show `-------`, which now makes more sense, b/c they are not concretized. - [x] There is a new option, `-r` / `--only-roots` to *only* show env roots if you don't want to look at all the installed specs. - [x] Roots in the installed spec list are now highlighted as bold. This is actually an old feature from the first env implementation , but various refactors had disabled it inadvertently.
This commit is contained in:
parent
de6c6f0cd9
commit
eefe0b2eec
4 changed files with 68 additions and 40 deletions
|
@ -334,8 +334,7 @@ def display_specs(specs, args=None, **kwargs):
|
||||||
variants (bool): Show variants with specs
|
variants (bool): Show variants with specs
|
||||||
indent (int): indent each line this much
|
indent (int): indent each line this much
|
||||||
groups (bool): display specs grouped by arch/compiler (default True)
|
groups (bool): display specs grouped by arch/compiler (default True)
|
||||||
decorators (dict): dictionary mappng specs to decorators
|
decorator (typing.Callable): function to call to decorate specs
|
||||||
header_callback (typing.Callable): called at start of arch/compiler groups
|
|
||||||
all_headers (bool): show headers even when arch/compiler aren't defined
|
all_headers (bool): show headers even when arch/compiler aren't defined
|
||||||
output (typing.IO): A file object to write to. Default is ``sys.stdout``
|
output (typing.IO): A file object to write to. Default is ``sys.stdout``
|
||||||
|
|
||||||
|
@ -384,15 +383,13 @@ def get_arg(name, default=None):
|
||||||
vfmt = "{variants}" if variants else ""
|
vfmt = "{variants}" if variants else ""
|
||||||
format_string = nfmt + "{@version}" + ffmt + vfmt
|
format_string = nfmt + "{@version}" + ffmt + vfmt
|
||||||
|
|
||||||
transform = {"package": decorator, "fullpackage": decorator}
|
|
||||||
|
|
||||||
def fmt(s, depth=0):
|
def fmt(s, depth=0):
|
||||||
"""Formatter function for all output specs"""
|
"""Formatter function for all output specs"""
|
||||||
string = ""
|
string = ""
|
||||||
if hashes:
|
if hashes:
|
||||||
string += gray_hash(s, hlen) + " "
|
string += gray_hash(s, hlen) + " "
|
||||||
string += depth * " "
|
string += depth * " "
|
||||||
string += s.cformat(format_string, transform=transform)
|
string += decorator(s, s.cformat(format_string))
|
||||||
return string
|
return string
|
||||||
|
|
||||||
def format_list(specs):
|
def format_list(specs):
|
||||||
|
@ -451,7 +448,7 @@ def filter_loaded_specs(specs):
|
||||||
return [x for x in specs if x.dag_hash() in hashes]
|
return [x for x in specs if x.dag_hash() in hashes]
|
||||||
|
|
||||||
|
|
||||||
def print_how_many_pkgs(specs, pkg_type=""):
|
def print_how_many_pkgs(specs, pkg_type="", suffix=""):
|
||||||
"""Given a list of specs, this will print a message about how many
|
"""Given a list of specs, this will print a message about how many
|
||||||
specs are in that list.
|
specs are in that list.
|
||||||
|
|
||||||
|
@ -462,7 +459,7 @@ def print_how_many_pkgs(specs, pkg_type=""):
|
||||||
category, e.g. if pkg_type is "installed" then the message
|
category, e.g. if pkg_type is "installed" then the message
|
||||||
would be "3 installed packages"
|
would be "3 installed packages"
|
||||||
"""
|
"""
|
||||||
tty.msg("%s" % llnl.string.plural(len(specs), pkg_type + " package"))
|
tty.msg("%s" % llnl.string.plural(len(specs), pkg_type + " package") + suffix)
|
||||||
|
|
||||||
|
|
||||||
def spack_is_git_repo():
|
def spack_is_git_repo():
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
import copy
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
|
@ -14,6 +13,7 @@
|
||||||
import spack.cmd as cmd
|
import spack.cmd as cmd
|
||||||
import spack.environment as ev
|
import spack.environment as ev
|
||||||
import spack.repo
|
import spack.repo
|
||||||
|
import spack.store
|
||||||
from spack.cmd.common import arguments
|
from spack.cmd.common import arguments
|
||||||
from spack.database import InstallStatuses
|
from spack.database import InstallStatuses
|
||||||
|
|
||||||
|
@ -69,6 +69,12 @@ def setup_parser(subparser):
|
||||||
|
|
||||||
arguments.add_common_arguments(subparser, ["long", "very_long", "tags", "namespaces"])
|
arguments.add_common_arguments(subparser, ["long", "very_long", "tags", "namespaces"])
|
||||||
|
|
||||||
|
subparser.add_argument(
|
||||||
|
"-r",
|
||||||
|
"--only-roots",
|
||||||
|
action="store_true",
|
||||||
|
help="don't show full list of installed specs in an environment",
|
||||||
|
)
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
"-c",
|
"-c",
|
||||||
"--show-concretized",
|
"--show-concretized",
|
||||||
|
@ -189,26 +195,22 @@ def query_arguments(args):
|
||||||
return q_args
|
return q_args
|
||||||
|
|
||||||
|
|
||||||
def setup_env(env):
|
def make_env_decorator(env):
|
||||||
"""Create a function for decorating specs when in an environment."""
|
"""Create a function for decorating specs when in an environment."""
|
||||||
|
|
||||||
def strip_build(seq):
|
roots = set(env.roots())
|
||||||
return set(s.copy(deps=("link", "run")) for s in seq)
|
removed = set(env.removed_specs())
|
||||||
|
|
||||||
added = set(strip_build(env.added_specs()))
|
|
||||||
roots = set(strip_build(env.roots()))
|
|
||||||
removed = set(strip_build(env.removed_specs()))
|
|
||||||
|
|
||||||
def decorator(spec, fmt):
|
def decorator(spec, fmt):
|
||||||
# add +/-/* to show added/removed/root specs
|
# add +/-/* to show added/removed/root specs
|
||||||
if any(spec.dag_hash() == r.dag_hash() for r in roots):
|
if any(spec.dag_hash() == r.dag_hash() for r in roots):
|
||||||
return color.colorize("@*{%s}" % fmt)
|
return color.colorize(f"@*{{{fmt}}}")
|
||||||
elif spec in removed:
|
elif spec in removed:
|
||||||
return color.colorize("@K{%s}" % fmt)
|
return color.colorize(f"@K{{{fmt}}}")
|
||||||
else:
|
else:
|
||||||
return "%s" % fmt
|
return fmt
|
||||||
|
|
||||||
return decorator, added, roots, removed
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def display_env(env, args, decorator, results):
|
def display_env(env, args, decorator, results):
|
||||||
|
@ -223,28 +225,51 @@ def display_env(env, args, decorator, results):
|
||||||
"""
|
"""
|
||||||
tty.msg("In environment %s" % env.name)
|
tty.msg("In environment %s" % env.name)
|
||||||
|
|
||||||
if not env.user_specs:
|
num_roots = len(env.user_specs) or "No"
|
||||||
tty.msg("No root specs")
|
tty.msg(f"{num_roots} root specs")
|
||||||
else:
|
|
||||||
tty.msg("Root specs")
|
|
||||||
|
|
||||||
# Root specs cannot be displayed with prefixes, since those are not
|
concrete_specs = {
|
||||||
# set for abstract specs. Same for hashes
|
root: concrete_root
|
||||||
root_args = copy.copy(args)
|
for root, concrete_root in zip(env.concretized_user_specs, env.concrete_roots())
|
||||||
root_args.paths = False
|
}
|
||||||
|
|
||||||
# Roots are displayed with variants, etc. so that we can see
|
def root_decorator(spec, string):
|
||||||
# specifically what the user asked for.
|
"""Decorate root specs with their install status if needed"""
|
||||||
|
concrete = concrete_specs.get(spec)
|
||||||
|
if concrete:
|
||||||
|
status = color.colorize(concrete.install_status().value)
|
||||||
|
hash = concrete.dag_hash()
|
||||||
|
else:
|
||||||
|
status = color.colorize(spack.spec.InstallStatus.absent.value)
|
||||||
|
hash = "-" * 32
|
||||||
|
|
||||||
|
# TODO: status has two extra spaces on the end of it, but fixing this and other spec
|
||||||
|
# TODO: space format idiosyncrasies is complicated. Fix this eventually
|
||||||
|
status = status[:-2]
|
||||||
|
|
||||||
|
if args.long or args.very_long:
|
||||||
|
hash = color.colorize(f"@K{{{hash[: 7 if args.long else None]}}}")
|
||||||
|
return f"{status} {hash} {string}"
|
||||||
|
else:
|
||||||
|
return f"{status} {string}"
|
||||||
|
|
||||||
|
with spack.store.STORE.db.read_transaction():
|
||||||
cmd.display_specs(
|
cmd.display_specs(
|
||||||
env.user_specs,
|
env.user_specs,
|
||||||
root_args,
|
args,
|
||||||
decorator=lambda s, f: color.colorize("@*{%s}" % f),
|
# these are overrides of CLI args
|
||||||
|
paths=False,
|
||||||
|
long=False,
|
||||||
|
very_long=False,
|
||||||
|
# these enforce details in the root specs to show what the user asked for
|
||||||
namespaces=True,
|
namespaces=True,
|
||||||
show_flags=True,
|
show_flags=True,
|
||||||
show_full_compiler=True,
|
show_full_compiler=True,
|
||||||
|
decorator=root_decorator,
|
||||||
variants=True,
|
variants=True,
|
||||||
)
|
)
|
||||||
print()
|
|
||||||
|
print()
|
||||||
|
|
||||||
if args.show_concretized:
|
if args.show_concretized:
|
||||||
tty.msg("Concretized roots")
|
tty.msg("Concretized roots")
|
||||||
|
@ -254,7 +279,7 @@ def display_env(env, args, decorator, results):
|
||||||
# Display a header for the installed packages section IF there are installed
|
# Display a header for the installed packages section IF there are installed
|
||||||
# packages. If there aren't any, we'll just end up printing "0 installed packages"
|
# packages. If there aren't any, we'll just end up printing "0 installed packages"
|
||||||
# later.
|
# later.
|
||||||
if results:
|
if results and not args.only_roots:
|
||||||
tty.msg("Installed packages")
|
tty.msg("Installed packages")
|
||||||
|
|
||||||
|
|
||||||
|
@ -263,9 +288,10 @@ def find(parser, args):
|
||||||
results = args.specs(**q_args)
|
results = args.specs(**q_args)
|
||||||
|
|
||||||
env = ev.active_environment()
|
env = ev.active_environment()
|
||||||
decorator = lambda s, f: f
|
if not env and args.only_roots:
|
||||||
if env:
|
tty.die("-r / --only-roots requires an active environment")
|
||||||
decorator, _, roots, _ = setup_env(env)
|
|
||||||
|
decorator = make_env_decorator(env) if env else lambda s, f: f
|
||||||
|
|
||||||
# use groups by default except with format.
|
# use groups by default except with format.
|
||||||
if args.groups is None:
|
if args.groups is None:
|
||||||
|
@ -292,9 +318,12 @@ def find(parser, args):
|
||||||
if env:
|
if env:
|
||||||
display_env(env, args, decorator, results)
|
display_env(env, args, decorator, results)
|
||||||
|
|
||||||
cmd.display_specs(results, args, decorator=decorator, all_headers=True)
|
count_suffix = " (not shown)"
|
||||||
|
if not args.only_roots:
|
||||||
|
cmd.display_specs(results, args, decorator=decorator, all_headers=True)
|
||||||
|
count_suffix = ""
|
||||||
|
|
||||||
# print number of installed packages last (as the list may be long)
|
# print number of installed packages last (as the list may be long)
|
||||||
if sys.stdout.isatty() and args.groups:
|
if sys.stdout.isatty() and args.groups:
|
||||||
pkg_type = "loaded" if args.loaded else "installed"
|
pkg_type = "loaded" if args.loaded else "installed"
|
||||||
spack.cmd.print_how_many_pkgs(results, pkg_type)
|
spack.cmd.print_how_many_pkgs(results, pkg_type, suffix=count_suffix)
|
||||||
|
|
|
@ -1197,7 +1197,7 @@ _spack_fetch() {
|
||||||
_spack_find() {
|
_spack_find() {
|
||||||
if $list_options
|
if $list_options
|
||||||
then
|
then
|
||||||
SPACK_COMPREPLY="-h --help --format -H --hashes --json -d --deps -p --paths --groups --no-groups -l --long -L --very-long -t --tag -N --namespaces -c --show-concretized -f --show-flags --show-full-compiler -x --explicit -X --implicit -u --unknown -m --missing -v --variants --loaded -M --only-missing --deprecated --only-deprecated --install-tree --start-date --end-date"
|
SPACK_COMPREPLY="-h --help --format -H --hashes --json -d --deps -p --paths --groups --no-groups -l --long -L --very-long -t --tag -N --namespaces -r --only-roots -c --show-concretized -f --show-flags --show-full-compiler -x --explicit -X --implicit -u --unknown -m --missing -v --variants --loaded -M --only-missing --deprecated --only-deprecated --install-tree --start-date --end-date"
|
||||||
else
|
else
|
||||||
_installed_packages
|
_installed_packages
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1743,7 +1743,7 @@ complete -c spack -n '__fish_spack_using_command fetch' -l deprecated -f -a conf
|
||||||
complete -c spack -n '__fish_spack_using_command fetch' -l deprecated -d 'allow concretizer to select deprecated versions'
|
complete -c spack -n '__fish_spack_using_command fetch' -l deprecated -d 'allow concretizer to select deprecated versions'
|
||||||
|
|
||||||
# spack find
|
# spack find
|
||||||
set -g __fish_spack_optspecs_spack_find h/help format= H/hashes json d/deps p/paths groups no-groups l/long L/very-long t/tag= N/namespaces c/show-concretized f/show-flags show-full-compiler x/explicit X/implicit u/unknown m/missing v/variants loaded M/only-missing deprecated only-deprecated install-tree= start-date= end-date=
|
set -g __fish_spack_optspecs_spack_find h/help format= H/hashes json d/deps p/paths groups no-groups l/long L/very-long t/tag= N/namespaces r/only-roots c/show-concretized f/show-flags show-full-compiler x/explicit X/implicit u/unknown m/missing v/variants loaded M/only-missing deprecated only-deprecated install-tree= start-date= end-date=
|
||||||
complete -c spack -n '__fish_spack_using_command_pos_remainder 0 find' -f -a '(__fish_spack_installed_specs)'
|
complete -c spack -n '__fish_spack_using_command_pos_remainder 0 find' -f -a '(__fish_spack_installed_specs)'
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s h -l help -f -a help
|
complete -c spack -n '__fish_spack_using_command find' -s h -l help -f -a help
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s h -l help -d 'show this help message and exit'
|
complete -c spack -n '__fish_spack_using_command find' -s h -l help -d 'show this help message and exit'
|
||||||
|
@ -1769,6 +1769,8 @@ complete -c spack -n '__fish_spack_using_command find' -s t -l tag -r -f -a tags
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s t -l tag -r -d 'filter a package query by tag (multiple use allowed)'
|
complete -c spack -n '__fish_spack_using_command find' -s t -l tag -r -d 'filter a package query by tag (multiple use allowed)'
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s N -l namespaces -f -a namespaces
|
complete -c spack -n '__fish_spack_using_command find' -s N -l namespaces -f -a namespaces
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s N -l namespaces -d 'show fully qualified package names'
|
complete -c spack -n '__fish_spack_using_command find' -s N -l namespaces -d 'show fully qualified package names'
|
||||||
|
complete -c spack -n '__fish_spack_using_command find' -s r -l only-roots -f -a only_roots
|
||||||
|
complete -c spack -n '__fish_spack_using_command find' -s r -l only-roots -d 'don\'t show full list of installed specs in an environment'
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s c -l show-concretized -f -a show_concretized
|
complete -c spack -n '__fish_spack_using_command find' -s c -l show-concretized -f -a show_concretized
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s c -l show-concretized -d 'show concretized specs in an environment'
|
complete -c spack -n '__fish_spack_using_command find' -s c -l show-concretized -d 'show concretized specs in an environment'
|
||||||
complete -c spack -n '__fish_spack_using_command find' -s f -l show-flags -f -a show_flags
|
complete -c spack -n '__fish_spack_using_command find' -s f -l show-flags -f -a show_flags
|
||||||
|
|
Loading…
Reference in a new issue