adding spack diff command (#22283)
A `spack diff` will take two specs, and then use the spack.solver.asp.SpackSolverSetup to generate lists of facts about each (e.g., nodes, variants, etc.) and then take a set difference between the two to show the user the differences. Example output: $ spack diff python@2.7.8 python@3.8.11 ==> Warning: This interface is subject to change. --- python@2.7.8/tsxdi6gl4lihp25qrm4d6nys3nypufbf +++ python@3.8.11/yjtseru4nbpllbaxb46q7wfkyxbuvzxx @@ variant_value @@ - python patches a8c52415a8b03c0e5f28b5d52ae498f7a7e602007db2b9554df28cd5685839b8 + python patches 0d98e93189bc278fbc37a50ed7f183bd8aaf249a8e1670a465f0db6bb4f8cf87 @@ version @@ - openssl Version(1.0.2u) + openssl Version(1.1.1k) - python Version(2.7.8) + python Version(3.8.11) Currently this uses diff-like output but we will attempt to improve on this in the future. One use case for `spack diff` is whenever a user has a disambiguate situation and cannot remember how two different installs are different. The command can also output `--json` in the case of a more analysis type use case where we want to save complete data with all diffs and the intersection. However, the command is really more intended for a command line use case, and we likely will have an analyzer more suited to saving data Signed-off-by: vsoch <vsoch@users.noreply.github.com> Co-authored-by: vsoch <vsoch@users.noreply.github.com> Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com> Co-authored-by: Todd Gamblin <tgamblin@llnl.gov>
This commit is contained in:
parent
e8f284bf52
commit
54e8e19a60
5 changed files with 448 additions and 6 deletions
|
@ -695,6 +695,136 @@ structured the way you want:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
``spack diff``
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
It's often the case that you have two versions of a spec that you need to
|
||||||
|
disambiguate. Let's say that we've installed two variants of zlib, one with
|
||||||
|
and one without the optimize variant:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ spack install zlib
|
||||||
|
$ spack install zlib -optimize
|
||||||
|
|
||||||
|
When we do ``spack find`` we see the two versions.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ spack find zlib
|
||||||
|
==> 2 installed packages
|
||||||
|
-- linux-ubuntu20.04-skylake / gcc@9.3.0 ------------------------
|
||||||
|
zlib@1.2.11 zlib@1.2.11
|
||||||
|
|
||||||
|
|
||||||
|
Let's now say that we want to uninstall zlib. We run the command, and hit a problem
|
||||||
|
real quickly since we have two!
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ spack uninstall zlib
|
||||||
|
==> Error: zlib matches multiple packages:
|
||||||
|
|
||||||
|
-- linux-ubuntu20.04-skylake / gcc@9.3.0 ------------------------
|
||||||
|
efzjziy zlib@1.2.11 sl7m27m zlib@1.2.11
|
||||||
|
|
||||||
|
==> Error: You can either:
|
||||||
|
a) use a more specific spec, or
|
||||||
|
b) specify the spec by its hash (e.g. `spack uninstall /hash`), or
|
||||||
|
c) use `spack uninstall --all` to uninstall ALL matching specs.
|
||||||
|
|
||||||
|
Oh no! We can see from the above that we have two different versions of zlib installed,
|
||||||
|
and the only difference between the two is the hash. This is a good use case for
|
||||||
|
``spack diff``, which can easily show us the "diff" or set difference
|
||||||
|
between properties for two packages. Let's try it out.
|
||||||
|
Since the only difference we see in the ``spack find`` view is the hash, let's use
|
||||||
|
``spack diff`` to look for more detail. We will provide the two hashes:
|
||||||
|
|
||||||
|
.. code-block::console
|
||||||
|
|
||||||
|
$ spack diff /efzjziy /sl7m27m
|
||||||
|
==> Warning: This interface is subject to change.
|
||||||
|
|
||||||
|
--- zlib@1.2.11efzjziyc3dmb5h5u5azsthgbgog5mj7g
|
||||||
|
+++ zlib@1.2.11sl7m27mzkbejtkrajigj3a3m37ygv4u2
|
||||||
|
@@ variant_value @@
|
||||||
|
- zlib optimize bool(False)
|
||||||
|
+ zlib optimize bool(True)
|
||||||
|
|
||||||
|
|
||||||
|
The output is colored, and written in the style of a git diff. This means that you
|
||||||
|
can copy paste it into a GitHub markdown as a code block with language "diff" and it
|
||||||
|
will render nicely! Here is an example:
|
||||||
|
|
||||||
|
.. code-block::markdown
|
||||||
|
|
||||||
|
```diff
|
||||||
|
--- zlib@1.2.11/efzjziyc3dmb5h5u5azsthgbgog5mj7g
|
||||||
|
+++ zlib@1.2.11/sl7m27mzkbejtkrajigj3a3m37ygv4u2
|
||||||
|
@@ variant_value @@
|
||||||
|
- zlib optimize bool(False)
|
||||||
|
+ zlib optimize bool(True)
|
||||||
|
```
|
||||||
|
|
||||||
|
Awesome! Now let's read the diff. It tells us that our first zlib was built without optimize (False)
|
||||||
|
and the second was built with optimize (True). You can't see it in the docs here, but
|
||||||
|
the output above is also colored based on the content being an addition (+) or subtraction (-).
|
||||||
|
|
||||||
|
This is a small example, but there are actually several kinds of differences that you can view, a variant value
|
||||||
|
being just one of them. The first package that you provide (A)
|
||||||
|
being diffed against B means that we see what is added to B but not in A (green) and what is present in A that is
|
||||||
|
removed in B (red). Here is another example with an additional difference type, ``VERSION``:
|
||||||
|
|
||||||
|
.. code-block::console
|
||||||
|
|
||||||
|
$ spack diff python@2.7.8 python@3.8.11
|
||||||
|
==> Warning: This interface is subject to change.
|
||||||
|
|
||||||
|
--- python@2.7.8/tsxdi6gl4lihp25qrm4d6nys3nypufbf
|
||||||
|
+++ python@3.8.11/yjtseru4nbpllbaxb46q7wfkyxbuvzxx
|
||||||
|
@@ variant_value @@
|
||||||
|
- python patches a8c52415a8b03c0e5f28b5d52ae498f7a7e602007db2b9554df28cd5685839b8
|
||||||
|
+ python patches 0d98e93189bc278fbc37a50ed7f183bd8aaf249a8e1670a465f0db6bb4f8cf87
|
||||||
|
@@ version @@
|
||||||
|
- openssl Version(1.0.2u)
|
||||||
|
+ openssl Version(1.1.1k)
|
||||||
|
- python Version(2.7.8)
|
||||||
|
+ python Version(3.8.11)
|
||||||
|
|
||||||
|
Let's say that we were only interested in one kind of attribute above, versions!
|
||||||
|
We can ask the command to only output this attribute. To do this, you'd add
|
||||||
|
the ``-a`` for attribute parameter, which defaults to all.
|
||||||
|
Here is how you would filter to show just versions:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ spack diff -a version python@2.7.8 python@3.8.11
|
||||||
|
==> Warning: This interface is subject to change.
|
||||||
|
|
||||||
|
--- python@2.7.8/tsxdi6gl4lihp25qrm4d6nys3nypufbf
|
||||||
|
+++ python@3.8.11/yjtseru4nbpllbaxb46q7wfkyxbuvzxx
|
||||||
|
@@ version @@
|
||||||
|
- openssl Version(1.0.2u)
|
||||||
|
+ openssl Version(1.1.1k)
|
||||||
|
- python Version(2.7.8)
|
||||||
|
+ python Version(3.8.11)
|
||||||
|
|
||||||
|
And you can add as many attributes as you'd like with multiple `-a`.
|
||||||
|
Finally, if you want to view the data as json (and possibly pipe into an output file)
|
||||||
|
just add ``--json``:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ spack diff --json python@2.7.8 python@3.8.11
|
||||||
|
|
||||||
|
|
||||||
|
This data will be much longer because along with the differences for A vs. B and
|
||||||
|
B vs. A, we also capture the intersection.
|
||||||
|
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
Using installed packages
|
Using installed packages
|
||||||
------------------------
|
------------------------
|
||||||
|
|
225
lib/spack/spack/cmd/diff.py
Normal file
225
lib/spack/spack/cmd/diff.py
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
# Copyright 2013-2021 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 sys
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
import llnl.util.tty.color as color
|
||||||
|
|
||||||
|
import spack.cmd
|
||||||
|
import spack.cmd.common.arguments as arguments
|
||||||
|
import spack.environment as ev
|
||||||
|
import spack.solver.asp as asp
|
||||||
|
import spack.util.environment
|
||||||
|
import spack.util.spack_json as sjson
|
||||||
|
|
||||||
|
description = "compare two specs"
|
||||||
|
section = "basic"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
|
def setup_parser(subparser):
|
||||||
|
arguments.add_common_arguments(
|
||||||
|
subparser, ['specs'])
|
||||||
|
|
||||||
|
subparser.add_argument(
|
||||||
|
'--json',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
dest='dump_json',
|
||||||
|
help="Dump json output instead of pretty printing."
|
||||||
|
)
|
||||||
|
subparser.add_argument(
|
||||||
|
'--first',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
dest='load_first',
|
||||||
|
help="load the first match if multiple packages match the spec"
|
||||||
|
)
|
||||||
|
subparser.add_argument(
|
||||||
|
'-a', '--attribute',
|
||||||
|
action='append',
|
||||||
|
help="select the attributes to show (defaults to all)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def boldblue(string):
|
||||||
|
"""
|
||||||
|
Make a header string bold and blue we can easily see it
|
||||||
|
"""
|
||||||
|
return color.colorize("@*b{%s}" % string)
|
||||||
|
|
||||||
|
|
||||||
|
def green(string):
|
||||||
|
return color.colorize("@G{%s}" % string)
|
||||||
|
|
||||||
|
|
||||||
|
def red(string):
|
||||||
|
return color.colorize("@R{%s}" % string)
|
||||||
|
|
||||||
|
|
||||||
|
def compare_specs(a, b, to_string=False, colorful=True):
|
||||||
|
"""
|
||||||
|
Generate a comparison, including diffs (for each side) and an intersection.
|
||||||
|
|
||||||
|
We can either print the result to the console, or parse
|
||||||
|
into a json object for the user to save. We return an object that shows
|
||||||
|
the differences, intersection, and names for a pair of specs a and b.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
a (spack.spec.Spec): the first spec to compare
|
||||||
|
b (spack.spec.Spec): the second spec to compare
|
||||||
|
a_name (str): the name of spec a
|
||||||
|
b_name (str): the name of spec b
|
||||||
|
to_string (bool): return an object that can be json dumped
|
||||||
|
colorful (bool): do not format the names for the console
|
||||||
|
"""
|
||||||
|
# Prepare a solver setup to parse differences
|
||||||
|
setup = asp.SpackSolverSetup()
|
||||||
|
|
||||||
|
a_facts = set(to_tuple(t) for t in setup.spec_clauses(a, body=True))
|
||||||
|
b_facts = set(to_tuple(t) for t in setup.spec_clauses(b, body=True))
|
||||||
|
|
||||||
|
# We want to present them to the user as simple key: values
|
||||||
|
intersect = list(a_facts.intersection(b_facts))
|
||||||
|
spec1_not_spec2 = list(a_facts.difference(b_facts))
|
||||||
|
spec2_not_spec1 = list(b_facts.difference(a_facts))
|
||||||
|
|
||||||
|
# Format the spec names to be colored
|
||||||
|
fmt = "{name}{@version}{/hash}"
|
||||||
|
a_name = a.format(fmt, color=color.get_color_when())
|
||||||
|
b_name = b.format(fmt, color=color.get_color_when())
|
||||||
|
|
||||||
|
# We want to show what is the same, and then difference for each
|
||||||
|
return {
|
||||||
|
"intersect": flatten(intersect) if to_string else intersect,
|
||||||
|
"a_not_b": flatten(spec1_not_spec2) if to_string else spec1_not_spec2,
|
||||||
|
"b_not_a": flatten(spec2_not_spec1) if to_string else spec2_not_spec1,
|
||||||
|
"a_name": a_name if colorful else a.format("{name}{@version}{/hash}"),
|
||||||
|
"b_name": b_name if colorful else b.format("{name}{@version}{/hash}")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def to_tuple(asp_function):
|
||||||
|
"""
|
||||||
|
Prepare tuples of objects.
|
||||||
|
|
||||||
|
If we need to save to json, convert to strings
|
||||||
|
See https://gist.github.com/tgamblin/83eba3c6d27f90d9fa3afebfc049ceaf
|
||||||
|
"""
|
||||||
|
args = []
|
||||||
|
for arg in asp_function.args:
|
||||||
|
if isinstance(arg, str):
|
||||||
|
args.append(arg)
|
||||||
|
continue
|
||||||
|
args.append("%s(%s)" % (type(arg).__name__, str(arg)))
|
||||||
|
return tuple([asp_function.name] + args)
|
||||||
|
|
||||||
|
|
||||||
|
def flatten(tuple_list):
|
||||||
|
"""
|
||||||
|
Given a list of tuples, convert into a list of key: value tuples.
|
||||||
|
|
||||||
|
We are squashing whatever is after the first index into one string for
|
||||||
|
easier parsing in the interface
|
||||||
|
"""
|
||||||
|
updated = []
|
||||||
|
for item in tuple_list:
|
||||||
|
updated.append([item[0], " ".join(item[1:])])
|
||||||
|
return updated
|
||||||
|
|
||||||
|
|
||||||
|
def print_difference(c, attributes="all", out=None):
|
||||||
|
"""
|
||||||
|
Print the difference.
|
||||||
|
|
||||||
|
Given a diffset for A and a diffset for B, print red/green diffs to show
|
||||||
|
the differences.
|
||||||
|
"""
|
||||||
|
# Default to standard out unless another stream is provided
|
||||||
|
out = out or sys.stdout
|
||||||
|
|
||||||
|
A = c['b_not_a']
|
||||||
|
B = c['a_not_b']
|
||||||
|
|
||||||
|
out.write(red("--- %s\n" % c["a_name"]))
|
||||||
|
out.write(green("+++ %s\n" % c["b_name"]))
|
||||||
|
|
||||||
|
# Cut out early if we don't have any differences!
|
||||||
|
if not A and not B:
|
||||||
|
print("No differences\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
def group_by_type(diffset):
|
||||||
|
grouped = {}
|
||||||
|
for entry in diffset:
|
||||||
|
if entry[0] not in grouped:
|
||||||
|
grouped[entry[0]] = []
|
||||||
|
grouped[entry[0]].append(entry[1])
|
||||||
|
|
||||||
|
# Sort by second value to make comparison slightly closer
|
||||||
|
for key, values in grouped.items():
|
||||||
|
values.sort()
|
||||||
|
return grouped
|
||||||
|
|
||||||
|
A = group_by_type(A)
|
||||||
|
B = group_by_type(B)
|
||||||
|
|
||||||
|
# print a directionally relevant diff
|
||||||
|
keys = list(A) + list(B)
|
||||||
|
|
||||||
|
category = None
|
||||||
|
for key in keys:
|
||||||
|
if "all" not in attributes and key not in attributes:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Write the attribute, B is subtraction A is addition
|
||||||
|
subtraction = [] if key not in B else B[key]
|
||||||
|
addition = [] if key not in A else A[key]
|
||||||
|
|
||||||
|
# Bail out early if we don't have any entries
|
||||||
|
if not subtraction and not addition:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If we have a new category, create a new section
|
||||||
|
if category != key:
|
||||||
|
category = key
|
||||||
|
|
||||||
|
# print category in bold, colorized
|
||||||
|
out.write(boldblue("@@ %s @@\n" % category))
|
||||||
|
|
||||||
|
# Print subtractions first
|
||||||
|
while subtraction:
|
||||||
|
out.write(red("- %s\n" % subtraction.pop(0)))
|
||||||
|
if addition:
|
||||||
|
out.write(green("+ %s\n" % addition.pop(0)))
|
||||||
|
|
||||||
|
# Any additions left?
|
||||||
|
while addition:
|
||||||
|
out.write(green("+ %s\n" % addition.pop(0)))
|
||||||
|
|
||||||
|
|
||||||
|
def diff(parser, args):
|
||||||
|
env = ev.get_env(args, 'diff')
|
||||||
|
|
||||||
|
if len(args.specs) != 2:
|
||||||
|
tty.die("You must provide two specs to diff.")
|
||||||
|
|
||||||
|
specs = [spack.cmd.disambiguate_spec(spec, env, first=args.load_first)
|
||||||
|
for spec in spack.cmd.parse_specs(args.specs)]
|
||||||
|
|
||||||
|
# Calculate the comparison (c)
|
||||||
|
c = compare_specs(specs[0], specs[1], to_string=True,
|
||||||
|
colorful=not args.dump_json)
|
||||||
|
|
||||||
|
# Default to all attributes
|
||||||
|
attributes = args.attribute or ["all"]
|
||||||
|
|
||||||
|
if args.dump_json:
|
||||||
|
print(sjson.dump(c))
|
||||||
|
else:
|
||||||
|
tty.warn("This interface is subject to change.\n")
|
||||||
|
print_difference(c, attributes)
|
|
@ -96,7 +96,10 @@ def _id(thing):
|
||||||
class AspFunction(AspObject):
|
class AspFunction(AspObject):
|
||||||
def __init__(self, name, args=None):
|
def __init__(self, name, args=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.args = [] if args is None else args
|
self.args = () if args is None else args
|
||||||
|
|
||||||
|
def _cmp_key(self):
|
||||||
|
return (self.name, self.args)
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
return AspFunction(self.name, args)
|
return AspFunction(self.name, args)
|
||||||
|
@ -112,10 +115,6 @@ def argify(arg):
|
||||||
return clingo.Function(
|
return clingo.Function(
|
||||||
self.name, [argify(arg) for arg in self.args], positive=positive)
|
self.name, [argify(arg) for arg in self.args], positive=positive)
|
||||||
|
|
||||||
def __getitem___(self, *args):
|
|
||||||
self.args[:] = args
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s(%s)" % (
|
return "%s(%s)" % (
|
||||||
self.name, ', '.join(str(_id(arg)) for arg in self.args))
|
self.name, ', '.join(str(_id(arg)) for arg in self.args))
|
||||||
|
|
79
lib/spack/spack/test/cmd/diff.py
Normal file
79
lib/spack/spack/test/cmd/diff.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Copyright 2013-2021 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 pytest
|
||||||
|
|
||||||
|
import spack.cmd.diff
|
||||||
|
import spack.config
|
||||||
|
import spack.main
|
||||||
|
import spack.store
|
||||||
|
import spack.util.spack_json as sjson
|
||||||
|
|
||||||
|
install = spack.main.SpackCommand('install')
|
||||||
|
diff = spack.main.SpackCommand('diff')
|
||||||
|
|
||||||
|
|
||||||
|
def test_diff(install_mockery, mock_fetch, mock_archive, mock_packages):
|
||||||
|
"""Test that we can install two packages and diff them"""
|
||||||
|
|
||||||
|
specA = spack.spec.Spec('mpileaks').concretized()
|
||||||
|
specB = spack.spec.Spec('mpileaks+debug').concretized()
|
||||||
|
|
||||||
|
# Specs should be the same as themselves
|
||||||
|
c = spack.cmd.diff.compare_specs(specA, specA, to_string=True)
|
||||||
|
assert len(c['a_not_b']) == 0
|
||||||
|
assert len(c['b_not_a']) == 0
|
||||||
|
|
||||||
|
# Calculate the comparison (c)
|
||||||
|
c = spack.cmd.diff.compare_specs(specA, specB, to_string=True)
|
||||||
|
assert len(c['a_not_b']) == 1
|
||||||
|
assert len(c['b_not_a']) == 1
|
||||||
|
assert c['a_not_b'][0] == ['variant_value', 'mpileaks debug bool(False)']
|
||||||
|
assert c['b_not_a'][0] == ['variant_value', 'mpileaks debug bool(True)']
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages):
|
||||||
|
"""Test with and without the --first option"""
|
||||||
|
install('mpileaks')
|
||||||
|
|
||||||
|
# Only one version of mpileaks will work
|
||||||
|
diff('mpileaks', 'mpileaks')
|
||||||
|
|
||||||
|
# 2 specs are required for a diff
|
||||||
|
with pytest.raises(spack.main.SpackCommandError):
|
||||||
|
diff('mpileaks')
|
||||||
|
with pytest.raises(spack.main.SpackCommandError):
|
||||||
|
diff('mpileaks', 'mpileaks', 'mpileaks')
|
||||||
|
|
||||||
|
# Ensure they are the same
|
||||||
|
assert "No differences" in diff('mpileaks', 'mpileaks')
|
||||||
|
output = diff('--json', 'mpileaks', 'mpileaks')
|
||||||
|
result = sjson.load(output)
|
||||||
|
|
||||||
|
assert len(result['a_not_b']) == 0
|
||||||
|
assert len(result['b_not_a']) == 0
|
||||||
|
|
||||||
|
assert 'mpileaks' in result['a_name']
|
||||||
|
assert 'mpileaks' in result['b_name']
|
||||||
|
assert "intersect" in result and len(result['intersect']) > 50
|
||||||
|
|
||||||
|
# After we install another version, it should ask us to disambiguate
|
||||||
|
install('mpileaks+debug')
|
||||||
|
|
||||||
|
# There are two versions of mpileaks
|
||||||
|
with pytest.raises(spack.main.SpackCommandError):
|
||||||
|
diff('mpileaks', 'mpileaks+debug')
|
||||||
|
|
||||||
|
# But if we tell it to use the first, it won't try to disambiguate
|
||||||
|
assert "variant" in diff('--first', 'mpileaks', 'mpileaks+debug')
|
||||||
|
|
||||||
|
# This matches them exactly
|
||||||
|
output = diff("--json", "mpileaks@2.3/ysubb76", "mpileaks@2.3/ft5qff3")
|
||||||
|
result = sjson.load(output)
|
||||||
|
|
||||||
|
assert len(result['a_not_b']) == 1
|
||||||
|
assert len(result['b_not_a']) == 1
|
||||||
|
assert result['a_not_b'][0] == ['variant_value', 'mpileaks debug bool(False)']
|
||||||
|
assert result['b_not_a'][0] == ['variant_value', 'mpileaks debug bool(True)']
|
|
@ -333,7 +333,7 @@ _spack() {
|
||||||
then
|
then
|
||||||
SPACK_COMPREPLY="-h --help -H --all-help --color -c --config -C --config-scope -d --debug --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars"
|
SPACK_COMPREPLY="-h --help -H --all-help --color -c --config -C --config-scope -d --debug --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars"
|
||||||
else
|
else
|
||||||
SPACK_COMPREPLY="activate add analyze arch audit blame bootstrap build-env buildcache cd checksum ci clean clone commands compiler compilers concretize config containerize create deactivate debug dependencies dependents deprecate dev-build develop docs edit env extensions external fetch find flake8 gc gpg graph help info install license list load location log-parse maintainers mark mirror module monitor patch pkg providers pydoc python reindex remove rm repo resource restage solve spec stage style test test-env tutorial undevelop uninstall unit-test unload url verify versions view"
|
SPACK_COMPREPLY="activate add analyze arch audit blame bootstrap build-env buildcache cd checksum ci clean clone commands compiler compilers concretize config containerize create deactivate debug dependencies dependents deprecate dev-build develop diff docs edit env extensions external fetch find flake8 gc gpg graph help info install license list load location log-parse maintainers mark mirror module monitor patch pkg providers pydoc python reindex remove rm repo resource restage solve spec stage style test test-env tutorial undevelop uninstall unit-test unload url verify versions view"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,6 +842,15 @@ _spack_develop() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_spack_diff() {
|
||||||
|
if $list_options
|
||||||
|
then
|
||||||
|
SPACK_COMPREPLY="-h --help --json --first -a --attribute"
|
||||||
|
else
|
||||||
|
_all_packages
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_spack_docs() {
|
_spack_docs() {
|
||||||
SPACK_COMPREPLY="-h --help"
|
SPACK_COMPREPLY="-h --help"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue