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:
parent
9a585e6c6a
commit
2536029ea9
@ -89,3 +89,11 @@ def _specs(self, **kwargs):
|
|||||||
_arguments['dirty'] = Args(
|
_arguments['dirty'] = Args(
|
||||||
'--dirty', action='store_true', dest='dirty',
|
'--dirty', action='store_true', dest='dirty',
|
||||||
help='Do NOT clean environment before installing.')
|
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.')
|
||||||
|
@ -52,14 +52,8 @@ def setup_parser(subparser):
|
|||||||
const='deps',
|
const='deps',
|
||||||
help='Show full dependency DAG of installed packages')
|
help='Show full dependency DAG of installed packages')
|
||||||
|
|
||||||
subparser.add_argument('-l', '--long',
|
arguments.add_common_arguments(subparser, ['long', 'very_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.')
|
|
||||||
subparser.add_argument('-f', '--show-flags',
|
subparser.add_argument('-f', '--show-flags',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
dest='show_flags',
|
dest='show_flags',
|
||||||
|
@ -23,36 +23,57 @@
|
|||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
##############################################################################
|
##############################################################################
|
||||||
import argparse
|
import argparse
|
||||||
import spack.cmd
|
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
import spack.cmd
|
||||||
|
import spack.cmd.common.arguments as arguments
|
||||||
|
|
||||||
description = "print out abstract and concrete versions of a spec."
|
description = "print out abstract and concrete versions of a spec."
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
subparser.add_argument('-i', '--ids', action='store_true',
|
arguments.add_common_arguments(subparser, ['long', 'very_long'])
|
||||||
help="show numerical ids for dependencies.")
|
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(
|
subparser.add_argument(
|
||||||
'specs', nargs=argparse.REMAINDER, help="specs of packages")
|
'specs', nargs=argparse.REMAINDER, help="specs of packages")
|
||||||
|
|
||||||
|
|
||||||
def spec(parser, args):
|
def spec(parser, args):
|
||||||
kwargs = {'ids': args.ids,
|
kwargs = {'color': True,
|
||||||
'indent': 2,
|
'cover': args.cover,
|
||||||
'color': True}
|
'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):
|
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 "Input spec"
|
||||||
print "------------------------------"
|
print "--------------------------------"
|
||||||
print spec.tree(**kwargs)
|
print spec.tree(**kwargs)
|
||||||
|
|
||||||
print "Normalized"
|
print "Normalized"
|
||||||
print "------------------------------"
|
print "--------------------------------"
|
||||||
spec.normalize()
|
spec.normalize()
|
||||||
print spec.tree(**kwargs)
|
print spec.tree(**kwargs)
|
||||||
|
|
||||||
print "Concretized"
|
print "Concretized"
|
||||||
print "------------------------------"
|
print "--------------------------------"
|
||||||
spec.concretize()
|
spec.concretize()
|
||||||
print spec.tree(**kwargs)
|
print spec.tree(**kwargs)
|
||||||
|
@ -2396,12 +2396,24 @@ def __cmp__(self, other):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.format() + self.dep_string()
|
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):
|
def tree(self, **kwargs):
|
||||||
"""Prints out this spec and its dependencies, tree-formatted
|
"""Prints out this spec and its dependencies, tree-formatted
|
||||||
with indentation."""
|
with indentation."""
|
||||||
color = kwargs.pop('color', False)
|
color = kwargs.pop('color', False)
|
||||||
depth = kwargs.pop('depth', 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')
|
cover = kwargs.pop('cover', 'nodes')
|
||||||
indent = kwargs.pop('indent', 0)
|
indent = kwargs.pop('indent', 0)
|
||||||
fmt = kwargs.pop('format', '$_$@$%@+$+$=')
|
fmt = kwargs.pop('format', '$_$@$%@+$+$=')
|
||||||
@ -2410,8 +2422,6 @@ def tree(self, **kwargs):
|
|||||||
check_kwargs(kwargs, self.tree)
|
check_kwargs(kwargs, self.tree)
|
||||||
|
|
||||||
out = ""
|
out = ""
|
||||||
cur_id = 0
|
|
||||||
ids = {}
|
|
||||||
for d, node in self.traverse(
|
for d, node in self.traverse(
|
||||||
order='pre', cover=cover, depth=True, deptypes=deptypes):
|
order='pre', cover=cover, depth=True, deptypes=deptypes):
|
||||||
if prefix is not None:
|
if prefix is not None:
|
||||||
@ -2419,11 +2429,17 @@ def tree(self, **kwargs):
|
|||||||
out += " " * indent
|
out += " " * indent
|
||||||
if depth:
|
if depth:
|
||||||
out += "%-4d" % d
|
out += "%-4d" % d
|
||||||
if not id(node) in ids:
|
if install_status:
|
||||||
cur_id += 1
|
status = node._install_status()
|
||||||
ids[id(node)] = cur_id
|
if status is None:
|
||||||
if showid:
|
out += " " # Package isn't installed
|
||||||
out += "%-4d" % ids[id(node)]
|
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)
|
out += (" " * d)
|
||||||
if d > 0:
|
if d > 0:
|
||||||
out += "^"
|
out += "^"
|
||||||
|
@ -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']['some_list'][2], 8, 8)
|
||||||
check(self.data['config_file']['another_list'], 10, 10)
|
check(self.data['config_file']['another_list'], 10, 10)
|
||||||
check(self.data['config_file']['some_key'], 11, 11)
|
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)
|
||||||
|
@ -202,6 +202,11 @@ def represent_mapping(self, tag, mapping, flow_style=None):
|
|||||||
node.flow_style = best_style
|
node.flow_style = best_style
|
||||||
return node
|
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.
|
# Make our special objects look like normal YAML ones.
|
||||||
OrderedLineDumper.add_representer(syaml_dict, OrderedLineDumper.represent_dict)
|
OrderedLineDumper.add_representer(syaml_dict, OrderedLineDumper.represent_dict)
|
||||||
OrderedLineDumper.add_representer(syaml_list, OrderedLineDumper.represent_list)
|
OrderedLineDumper.add_representer(syaml_list, OrderedLineDumper.represent_list)
|
||||||
|
Loading…
Reference in New Issue
Block a user