command: add spack find --json
This is another machine-readable version of `spack find`. Supplying the `--json` argument causes specs to be written out as json records, easily filered with tools like jq. e.g.: $ spack find --json python | jq -C ".[] | { name, version } " [ { "name": "python", "version": "2.7.16" }, { "name": "bzip2", "version": "1.0.8" } ]
This commit is contained in:
parent
64af0a9874
commit
1a1f5674df
4 changed files with 52 additions and 4 deletions
|
@ -21,6 +21,7 @@
|
|||
import spack.paths
|
||||
import spack.spec
|
||||
import spack.store
|
||||
import spack.util.spack_json as sjson
|
||||
from spack.error import SpackError
|
||||
|
||||
|
||||
|
@ -214,6 +215,24 @@ def display_formatted_specs(specs, format_string, deps=False):
|
|||
print(" " * depth, dep.format(format_string))
|
||||
|
||||
|
||||
def display_specs_as_json(specs, deps=False):
|
||||
"""Convert specs to a list of json records."""
|
||||
seen = set()
|
||||
records = []
|
||||
for spec in specs:
|
||||
if spec.dag_hash() not in seen:
|
||||
seen.add(spec.dag_hash())
|
||||
records.append(spec.to_record_dict())
|
||||
|
||||
if deps:
|
||||
for dep in spec.traverse():
|
||||
if dep.dag_hash() not in seen:
|
||||
seen.add(spec.dag_hash())
|
||||
records.append(dep.to_record_dict())
|
||||
|
||||
sjson.dump(records, sys.stdout)
|
||||
|
||||
|
||||
def display_specs(specs, args=None, **kwargs):
|
||||
"""Display human readable specs with customizable formatting.
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import llnl.util.tty as tty
|
||||
|
@ -12,7 +13,6 @@
|
|||
import spack.repo
|
||||
import spack.cmd as cmd
|
||||
import spack.cmd.common.arguments as arguments
|
||||
from spack.cmd import display_specs
|
||||
from spack.util.string import plural
|
||||
|
||||
description = "list and search installed packages"
|
||||
|
@ -36,6 +36,9 @@ def setup_parser(subparser):
|
|||
format_group.add_argument(
|
||||
"--format", action="store", default=None,
|
||||
help="output specs with the specified format string")
|
||||
format_group.add_argument(
|
||||
"--json", action="store_true", default=False,
|
||||
help="output specs as machine-readable json records")
|
||||
|
||||
# TODO: separate this entirely from the "mode" option -- it's
|
||||
# TODO: orthogonal, but changing it for all commands that use it with
|
||||
|
@ -159,14 +162,14 @@ def display_env(env, args, decorator):
|
|||
else:
|
||||
tty.msg('Root specs')
|
||||
# TODO: Change this to not print extraneous deps and variants
|
||||
display_specs(
|
||||
cmd.display_specs(
|
||||
env.user_specs, args,
|
||||
decorator=lambda s, f: color.colorize('@*{%s}' % f))
|
||||
print()
|
||||
|
||||
if args.show_concretized:
|
||||
tty.msg('Concretized roots')
|
||||
display_specs(
|
||||
cmd.display_specs(
|
||||
env.specs_by_hash.values(), args, decorator=decorator)
|
||||
print()
|
||||
|
||||
|
@ -205,4 +208,4 @@ def find(parser, args):
|
|||
if env:
|
||||
display_env(env, args, decorator)
|
||||
tty.msg("%s" % plural(len(results), 'installed package'))
|
||||
display_specs(results, args, decorator=decorator, all_headers=True)
|
||||
cmd.display_specs(results, args, decorator=decorator, all_headers=True)
|
||||
|
|
|
@ -952,6 +952,7 @@ def _shell_vars(self):
|
|||
('PKG_CONFIG_PATH', ['lib/pkgconfig', 'lib64/pkgconfig']),
|
||||
('CMAKE_PREFIX_PATH', ['']),
|
||||
]
|
||||
|
||||
path_updates = list()
|
||||
if default_view_name in self.views:
|
||||
for var, subdirs in updates:
|
||||
|
|
|
@ -1540,6 +1540,31 @@ def to_dict(self, hash=ht.dag_hash):
|
|||
|
||||
return syaml_dict([('spec', node_list)])
|
||||
|
||||
def to_record_dict(self):
|
||||
"""Return a "flat" dictionary with name and hash as top-level keys.
|
||||
|
||||
This is similar to ``to_node_dict()``, but the name and the hash
|
||||
are "flattened" into the dictionary for easiler parsing by tools
|
||||
like ``jq``. Instead of being keyed by name or hash, the
|
||||
dictionary "name" and "hash" fields, e.g.::
|
||||
|
||||
{
|
||||
"name": "openssl"
|
||||
"hash": "3ws7bsihwbn44ghf6ep4s6h4y2o6eznv"
|
||||
"version": "3.28.0",
|
||||
"arch": {
|
||||
...
|
||||
}
|
||||
|
||||
But is otherwise the same as ``to_node_dict()``.
|
||||
|
||||
"""
|
||||
dictionary = syaml_dict()
|
||||
dictionary["name"] = self.name
|
||||
dictionary["hash"] = self.dag_hash()
|
||||
dictionary.update(self.to_node_dict()[self.name])
|
||||
return dictionary
|
||||
|
||||
def to_yaml(self, stream=None, hash=ht.dag_hash):
|
||||
return syaml.dump(
|
||||
self.to_dict(hash), stream=stream, default_flow_style=False)
|
||||
|
|
Loading…
Reference in a new issue