Better spack spec (#2238)

* Add options for hashes, tree depth, and YAML to `spack spec`.

- Can now display hashes with `spack spec`, like `spack find`.
  - Removed the old "ids" argument to `spack spec` (which
    printed numerical values)b

- Can output YAML spec from `spack spec` with `-y`

- Can control depth of DAG traversal with --cover=[nodes|edges|paths]

- Can print install status (installed, missing, not installed) with -I

* Don't use YAML aliases in specs.

- Force Spack's YAML dumper to ignore aliases.
- aliases cause non-canonical YAML to be used in DAG hash, and result in
  redundant hashes.
- add a test to ensure this behavior stays
This commit is contained in:
Todd Gamblin 2016-11-04 11:47:57 -07:00 committed by GitHub
parent 9a585e6c6a
commit 2536029ea9
6 changed files with 85 additions and 25 deletions

View File

@ -89,3 +89,11 @@ def _specs(self, **kwargs):
_arguments['dirty'] = Args(
'--dirty', action='store_true', dest='dirty',
help='Do NOT clean environment before installing.')
_arguments['long'] = Args(
'-l', '--long', action='store_true',
help='Show dependency hashes as well as versions.')
_arguments['very_long'] = Args(
'-L', '--very-long', action='store_true',
help='Show full dependency hashes as well as versions.')

View File

@ -52,14 +52,8 @@ def setup_parser(subparser):
const='deps',
help='Show full dependency DAG of installed packages')
subparser.add_argument('-l', '--long',
action='store_true',
dest='long',
help='Show dependency hashes as well as versions.')
subparser.add_argument('-L', '--very-long',
action='store_true',
dest='very_long',
help='Show dependency hashes as well as versions.')
arguments.add_common_arguments(subparser, ['long', 'very_long'])
subparser.add_argument('-f', '--show-flags',
action='store_true',
dest='show_flags',

View File

@ -23,36 +23,57 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import argparse
import spack.cmd
import spack
import spack.cmd
import spack.cmd.common.arguments as arguments
description = "print out abstract and concrete versions of a spec."
def setup_parser(subparser):
subparser.add_argument('-i', '--ids', action='store_true',
help="show numerical ids for dependencies.")
arguments.add_common_arguments(subparser, ['long', 'very_long'])
subparser.add_argument(
'-y', '--yaml', action='store_true', default=False,
help='Print concrete spec as YAML.')
subparser.add_argument(
'-c', '--cover', action='store',
default='nodes', choices=['nodes', 'edges', 'paths'],
help='How extensively to traverse the DAG. (default: nodes).')
subparser.add_argument(
'-I', '--install-status', action='store_true', default=False,
help='Show install status of packages. Packages can be: '
'installed [+], missing and needed by an installed package [-], '
'or not installed (no annotation).')
subparser.add_argument(
'specs', nargs=argparse.REMAINDER, help="specs of packages")
def spec(parser, args):
kwargs = {'ids': args.ids,
'indent': 2,
'color': True}
kwargs = {'color': True,
'cover': args.cover,
'install_status': args.install_status,
'hashes': args.long or args.very_long,
'hashlen': None if args.very_long else 7}
for spec in spack.cmd.parse_specs(args.specs):
# With -y, just print YAML to output.
if args.yaml:
spec.concretize()
print spec.to_yaml()
continue
# Print some diagnostic info by default.
print "Input spec"
print "------------------------------"
print "--------------------------------"
print spec.tree(**kwargs)
print "Normalized"
print "------------------------------"
print "--------------------------------"
spec.normalize()
print spec.tree(**kwargs)
print "Concretized"
print "------------------------------"
print "--------------------------------"
spec.concretize()
print spec.tree(**kwargs)

View File

@ -2396,12 +2396,24 @@ def __cmp__(self, other):
def __str__(self):
return self.format() + self.dep_string()
def _install_status(self):
"""Helper for tree to print DB install status."""
if not self.concrete:
return None
try:
record = spack.store.db.get_record(self)
return record.installed
except KeyError:
return None
def tree(self, **kwargs):
"""Prints out this spec and its dependencies, tree-formatted
with indentation."""
color = kwargs.pop('color', False)
depth = kwargs.pop('depth', False)
showid = kwargs.pop('ids', False)
hashes = kwargs.pop('hashes', True)
hlen = kwargs.pop('hashlen', None)
install_status = kwargs.pop('install_status', True)
cover = kwargs.pop('cover', 'nodes')
indent = kwargs.pop('indent', 0)
fmt = kwargs.pop('format', '$_$@$%@+$+$=')
@ -2410,8 +2422,6 @@ def tree(self, **kwargs):
check_kwargs(kwargs, self.tree)
out = ""
cur_id = 0
ids = {}
for d, node in self.traverse(
order='pre', cover=cover, depth=True, deptypes=deptypes):
if prefix is not None:
@ -2419,11 +2429,17 @@ def tree(self, **kwargs):
out += " " * indent
if depth:
out += "%-4d" % d
if not id(node) in ids:
cur_id += 1
ids[id(node)] = cur_id
if showid:
out += "%-4d" % ids[id(node)]
if install_status:
status = node._install_status()
if status is None:
out += " " # Package isn't installed
elif status:
out += colorize("@g{[+]} ", color=color) # installed
else:
out += colorize("@r{[-]} ", color=color) # missing
if hashes:
out += colorize('@K{%s} ', color=color) % node.dag_hash(hlen)
out += (" " * d)
if d > 0:
out += "^"

View File

@ -90,3 +90,19 @@ def check(obj, start_line, end_line):
check(self.data['config_file']['some_list'][2], 8, 8)
check(self.data['config_file']['another_list'], 10, 10)
check(self.data['config_file']['some_key'], 11, 11)
def test_yaml_aliases(self):
aliased_list_1 = ['foo']
aliased_list_2 = []
dict_with_aliases = {
'a': aliased_list_1,
'b': aliased_list_1,
'c': aliased_list_1,
'd': aliased_list_2,
'e': aliased_list_2,
'f': aliased_list_2,
}
string = syaml.dump(dict_with_aliases)
# ensure no YAML aliases appear in syaml dumps.
self.assertFalse('*id' in string)

View File

@ -202,6 +202,11 @@ def represent_mapping(self, tag, mapping, flow_style=None):
node.flow_style = best_style
return node
def ignore_aliases(self, _data):
"""Make the dumper NEVER print YAML aliases."""
return True
# Make our special objects look like normal YAML ones.
OrderedLineDumper.add_representer(syaml_dict, OrderedLineDumper.represent_dict)
OrderedLineDumper.add_representer(syaml_list, OrderedLineDumper.represent_list)