Use spack commands --format=bash to generate shell completion (#14393)
				
					
				
			This PR adds a `--format=bash` option to `spack commands` to auto-generate the Bash programmable tab completion script. It can be extended to work for other shells. Progress: - [x] Fix bug in superclass initialization in `ArgparseWriter` - [x] Refactor `ArgparseWriter` (see below) - [x] Ensure that output of old `--format` options remains the same - [x] Add `ArgparseCompletionWriter` and `BashCompletionWriter` - [x] Add `--aliases` option to add command aliases - [x] Standardize positional argument names - [x] Tests for `spack commands --format=bash` coverage - [x] Tests to make sure `spack-completion.bash` stays up-to-date - [x] Tests for `spack-completion.bash` coverage - [x] Speed up `spack-completion.bash` by caching subroutine calls This PR also necessitates a significant refactoring of `ArgparseWriter`. Previously, `ArgparseWriter` was mostly a single `_write` method which handled everything from extracting the information we care about from the parser to formatting the output. Now, `_write` only handles recursion, while the information extraction is split into a separate `parse` method, and the formatting is handled by `format`. This allows subclasses to completely redefine how the format will appear without overriding all of `_write`. Co-Authored-by: Todd Gamblin <tgamblin@llnl.gov>
This commit is contained in:
		
				
					committed by
					
						
						Todd Gamblin
					
				
			
			
				
	
			
			
			
						parent
						
							8011fedd9c
						
					
				
				
					commit
					11f2b61261
				
			@@ -4,134 +4,162 @@
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
import argparse
 | 
			
		||||
import errno
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from six import StringIO
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command(object):
 | 
			
		||||
    """Parsed representation of a command from argparse.
 | 
			
		||||
 | 
			
		||||
    This is a single command from an argparse parser. ``ArgparseWriter``
 | 
			
		||||
    creates these and returns them from ``parse()``, and it passes one of
 | 
			
		||||
    these to each call to ``format()`` so that we can take an action for
 | 
			
		||||
    a single command.
 | 
			
		||||
 | 
			
		||||
    Parts of a Command:
 | 
			
		||||
      - prog: command name (str)
 | 
			
		||||
      - description: command description (str)
 | 
			
		||||
      - usage: command usage (str)
 | 
			
		||||
      - positionals: list of positional arguments (list)
 | 
			
		||||
      - optionals: list of optional arguments (list)
 | 
			
		||||
      - subcommands: list of subcommand parsers (list)
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, prog, description, usage,
 | 
			
		||||
                 positionals, optionals, subcommands):
 | 
			
		||||
        self.prog = prog
 | 
			
		||||
        self.description = description
 | 
			
		||||
        self.usage = usage
 | 
			
		||||
        self.positionals = positionals
 | 
			
		||||
        self.optionals = optionals
 | 
			
		||||
        self.subcommands = subcommands
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# NOTE: The only reason we subclass argparse.HelpFormatter is to get access
 | 
			
		||||
# to self._expand_help(), ArgparseWriter is not intended to be used as a
 | 
			
		||||
# formatter_class.
 | 
			
		||||
class ArgparseWriter(argparse.HelpFormatter):
 | 
			
		||||
    """Analyzes an argparse ArgumentParser for easy generation of help."""
 | 
			
		||||
    def __init__(self, out=sys.stdout):
 | 
			
		||||
        super(ArgparseWriter, self).__init__(out)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, prog, out=sys.stdout, aliases=False):
 | 
			
		||||
        """Initializes a new ArgparseWriter instance.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            prog (str): the program name
 | 
			
		||||
            out (file object): the file to write to
 | 
			
		||||
            aliases (bool): whether or not to include subparsers for aliases
 | 
			
		||||
        """
 | 
			
		||||
        super(ArgparseWriter, self).__init__(prog)
 | 
			
		||||
        self.level = 0
 | 
			
		||||
        self.prog = prog
 | 
			
		||||
        self.out = out
 | 
			
		||||
        self.aliases = aliases
 | 
			
		||||
 | 
			
		||||
    def _write(self, parser, root=True, level=0):
 | 
			
		||||
    def parse(self, parser, prog):
 | 
			
		||||
        """Parses the parser object and returns the relavent components.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            parser (argparse.ArgumentParser): the parser
 | 
			
		||||
            prog (str): the command name
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            (Command) information about the command from the parser
 | 
			
		||||
        """
 | 
			
		||||
        self.parser = parser
 | 
			
		||||
        self.level = level
 | 
			
		||||
 | 
			
		||||
        split_prog = parser.prog.split(' ')
 | 
			
		||||
        split_prog[-1] = prog
 | 
			
		||||
        prog = ' '.join(split_prog)
 | 
			
		||||
        description = parser.description
 | 
			
		||||
 | 
			
		||||
        fmt = parser._get_formatter()
 | 
			
		||||
        actions = parser._actions
 | 
			
		||||
        groups = parser._mutually_exclusive_groups
 | 
			
		||||
        usage = fmt._format_usage(None, actions, groups, '').strip()
 | 
			
		||||
 | 
			
		||||
        # allow root level to be flattened with rest of commands
 | 
			
		||||
        if type(root) == int:
 | 
			
		||||
            self.level = root
 | 
			
		||||
            root = True
 | 
			
		||||
 | 
			
		||||
        # go through actions and split them into optionals, positionals,
 | 
			
		||||
        # Go through actions and split them into optionals, positionals,
 | 
			
		||||
        # and subcommands
 | 
			
		||||
        optionals = []
 | 
			
		||||
        positionals = []
 | 
			
		||||
        subcommands = []
 | 
			
		||||
        for action in actions:
 | 
			
		||||
            if action.option_strings:
 | 
			
		||||
                optionals.append(action)
 | 
			
		||||
                flags = action.option_strings
 | 
			
		||||
                dest_flags = fmt._format_action_invocation(action)
 | 
			
		||||
                help = self._expand_help(action) if action.help else ''
 | 
			
		||||
                help = help.replace('\n', ' ')
 | 
			
		||||
                optionals.append((flags, dest_flags, help))
 | 
			
		||||
            elif isinstance(action, argparse._SubParsersAction):
 | 
			
		||||
                for subaction in action._choices_actions:
 | 
			
		||||
                    subparser = action._name_parser_map[subaction.dest]
 | 
			
		||||
                    subcommands.append(subparser)
 | 
			
		||||
                    subcommands.append((subparser, subaction.dest))
 | 
			
		||||
 | 
			
		||||
                    # Look for aliases of the form 'name (alias, ...)'
 | 
			
		||||
                    if self.aliases:
 | 
			
		||||
                        match = re.match(r'(.*) \((.*)\)', subaction.metavar)
 | 
			
		||||
                        if match:
 | 
			
		||||
                            aliases = match.group(2).split(', ')
 | 
			
		||||
                            for alias in aliases:
 | 
			
		||||
                                subparser = action._name_parser_map[alias]
 | 
			
		||||
                                subcommands.append((subparser, alias))
 | 
			
		||||
            else:
 | 
			
		||||
                positionals.append(action)
 | 
			
		||||
 | 
			
		||||
        groups = parser._mutually_exclusive_groups
 | 
			
		||||
        fmt = parser._get_formatter()
 | 
			
		||||
        description = parser.description
 | 
			
		||||
 | 
			
		||||
        def action_group(function, actions):
 | 
			
		||||
            for action in actions:
 | 
			
		||||
                arg = fmt._format_action_invocation(action)
 | 
			
		||||
                args = fmt._format_action_invocation(action)
 | 
			
		||||
                help = self._expand_help(action) if action.help else ''
 | 
			
		||||
                function(arg, re.sub('\n', ' ', help))
 | 
			
		||||
                help = help.replace('\n', ' ')
 | 
			
		||||
                positionals.append((args, help))
 | 
			
		||||
 | 
			
		||||
        if root:
 | 
			
		||||
            self.begin_command(parser.prog)
 | 
			
		||||
        return Command(
 | 
			
		||||
            prog, description, usage, positionals, optionals, subcommands)
 | 
			
		||||
 | 
			
		||||
            if description:
 | 
			
		||||
                self.description(parser.description)
 | 
			
		||||
    def format(self, cmd):
 | 
			
		||||
        """Returns the string representation of a single node in the
 | 
			
		||||
        parser tree.
 | 
			
		||||
 | 
			
		||||
            usage = fmt._format_usage(None, actions, groups, '').strip()
 | 
			
		||||
            self.usage(usage)
 | 
			
		||||
        Override this in subclasses to define how each subcommand
 | 
			
		||||
        should be displayed.
 | 
			
		||||
 | 
			
		||||
            if positionals:
 | 
			
		||||
                self.begin_positionals()
 | 
			
		||||
                action_group(self.positional, positionals)
 | 
			
		||||
                self.end_positionals()
 | 
			
		||||
        Parameters:
 | 
			
		||||
            (Command): parsed information about a command or subcommand
 | 
			
		||||
 | 
			
		||||
            if optionals:
 | 
			
		||||
                self.begin_optionals()
 | 
			
		||||
                action_group(self.optional, optionals)
 | 
			
		||||
                self.end_optionals()
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the string representation of this subcommand
 | 
			
		||||
        """
 | 
			
		||||
        raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
        if subcommands:
 | 
			
		||||
            self.begin_subcommands(subcommands)
 | 
			
		||||
            for subparser in subcommands:
 | 
			
		||||
                self._write(subparser, root=True, level=level + 1)
 | 
			
		||||
            self.end_subcommands(subcommands)
 | 
			
		||||
    def _write(self, parser, prog, level=0):
 | 
			
		||||
        """Recursively writes a parser.
 | 
			
		||||
 | 
			
		||||
        if root:
 | 
			
		||||
            self.end_command(parser.prog)
 | 
			
		||||
        Parameters:
 | 
			
		||||
            parser (argparse.ArgumentParser): the parser
 | 
			
		||||
            prog (str): the command name
 | 
			
		||||
            level (int): the current level
 | 
			
		||||
        """
 | 
			
		||||
        self.level = level
 | 
			
		||||
 | 
			
		||||
    def write(self, parser, root=True):
 | 
			
		||||
        cmd = self.parse(parser, prog)
 | 
			
		||||
        self.out.write(self.format(cmd))
 | 
			
		||||
 | 
			
		||||
        for subparser, prog in cmd.subcommands:
 | 
			
		||||
            self._write(subparser, prog, level=level + 1)
 | 
			
		||||
 | 
			
		||||
    def write(self, parser):
 | 
			
		||||
        """Write out details about an ArgumentParser.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            parser (ArgumentParser): an ``argparse`` parser
 | 
			
		||||
            root (bool or int): if bool, whether to include the root parser;
 | 
			
		||||
                or ``1`` to flatten the root parser with first-level
 | 
			
		||||
                subcommands
 | 
			
		||||
            parser (argparse.ArgumentParser): the parser
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            self._write(parser, root, level=0)
 | 
			
		||||
            self._write(parser, self.prog)
 | 
			
		||||
        except IOError as e:
 | 
			
		||||
            # swallow pipe errors
 | 
			
		||||
            # Swallow pipe errors
 | 
			
		||||
            # Raises IOError in Python 2 and BrokenPipeError in Python 3
 | 
			
		||||
            if e.errno != errno.EPIPE:
 | 
			
		||||
                raise
 | 
			
		||||
 | 
			
		||||
    def begin_command(self, prog):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def end_command(self, prog):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def description(self, description):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def usage(self, usage):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def begin_positionals(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def positional(self, name, help):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def end_positionals(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def begin_optionals(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def optional(self, option, help):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def end_optionals(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def begin_subcommands(self, subcommands):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def end_subcommands(self, subcommands):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_rst_levels = ['=', '-', '^', '~', ':', '`']
 | 
			
		||||
 | 
			
		||||
@@ -139,66 +167,213 @@ def end_subcommands(self, subcommands):
 | 
			
		||||
class ArgparseRstWriter(ArgparseWriter):
 | 
			
		||||
    """Write argparse output as rst sections."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, out=sys.stdout, rst_levels=_rst_levels,
 | 
			
		||||
                 strip_root_prog=True):
 | 
			
		||||
    def __init__(self, prog, out=sys.stdout, aliases=False,
 | 
			
		||||
                 rst_levels=_rst_levels):
 | 
			
		||||
        """Create a new ArgparseRstWriter.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
        Parameters:
 | 
			
		||||
            prog (str): program name
 | 
			
		||||
            out (file object): file to write to
 | 
			
		||||
            aliases (bool): whether or not to include subparsers for aliases
 | 
			
		||||
            rst_levels (list of str): list of characters
 | 
			
		||||
                for rst section headings
 | 
			
		||||
            strip_root_prog (bool): if ``True``, strip the base command name
 | 
			
		||||
                from subcommands in output
 | 
			
		||||
        """
 | 
			
		||||
        super(ArgparseRstWriter, self).__init__(out)
 | 
			
		||||
        super(ArgparseRstWriter, self).__init__(prog, out, aliases)
 | 
			
		||||
        self.rst_levels = rst_levels
 | 
			
		||||
        self.strip_root_prog = strip_root_prog
 | 
			
		||||
 | 
			
		||||
    def line(self, string=''):
 | 
			
		||||
        self.out.write('%s\n' % string)
 | 
			
		||||
    def format(self, cmd):
 | 
			
		||||
        string = StringIO()
 | 
			
		||||
        string.write(self.begin_command(cmd.prog))
 | 
			
		||||
 | 
			
		||||
        if cmd.description:
 | 
			
		||||
            string.write(self.description(cmd.description))
 | 
			
		||||
 | 
			
		||||
        string.write(self.usage(cmd.usage))
 | 
			
		||||
 | 
			
		||||
        if cmd.positionals:
 | 
			
		||||
            string.write(self.begin_positionals())
 | 
			
		||||
            for args, help in cmd.positionals:
 | 
			
		||||
                string.write(self.positional(args, help))
 | 
			
		||||
            string.write(self.end_positionals())
 | 
			
		||||
 | 
			
		||||
        if cmd.optionals:
 | 
			
		||||
            string.write(self.begin_optionals())
 | 
			
		||||
            for flags, dest_flags, help in cmd.optionals:
 | 
			
		||||
                string.write(self.optional(dest_flags, help))
 | 
			
		||||
            string.write(self.end_optionals())
 | 
			
		||||
 | 
			
		||||
        if cmd.subcommands:
 | 
			
		||||
            string.write(self.begin_subcommands(cmd.subcommands))
 | 
			
		||||
 | 
			
		||||
        return string.getvalue()
 | 
			
		||||
 | 
			
		||||
    def begin_command(self, prog):
 | 
			
		||||
        self.line()
 | 
			
		||||
        self.line('----')
 | 
			
		||||
        self.line()
 | 
			
		||||
        self.line('.. _%s:\n' % prog.replace(' ', '-'))
 | 
			
		||||
        self.line('%s' % prog)
 | 
			
		||||
        self.line(self.rst_levels[self.level] * len(prog) + '\n')
 | 
			
		||||
        return """
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
.. _{0}:
 | 
			
		||||
 | 
			
		||||
{1}
 | 
			
		||||
{2}
 | 
			
		||||
 | 
			
		||||
""".format(prog.replace(' ', '-'), prog,
 | 
			
		||||
           self.rst_levels[self.level] * len(prog))
 | 
			
		||||
 | 
			
		||||
    def description(self, description):
 | 
			
		||||
        self.line('%s\n' % description)
 | 
			
		||||
        return description + '\n\n'
 | 
			
		||||
 | 
			
		||||
    def usage(self, usage):
 | 
			
		||||
        self.line('.. code-block:: console\n')
 | 
			
		||||
        self.line('    %s\n' % usage)
 | 
			
		||||
        return """\
 | 
			
		||||
.. code-block:: console
 | 
			
		||||
 | 
			
		||||
    {0}
 | 
			
		||||
 | 
			
		||||
""".format(usage)
 | 
			
		||||
 | 
			
		||||
    def begin_positionals(self):
 | 
			
		||||
        self.line()
 | 
			
		||||
        self.line('**Positional arguments**\n')
 | 
			
		||||
        return '\n**Positional arguments**\n\n'
 | 
			
		||||
 | 
			
		||||
    def positional(self, name, help):
 | 
			
		||||
        self.line(name)
 | 
			
		||||
        self.line('  %s\n' % help)
 | 
			
		||||
        return """\
 | 
			
		||||
{0}
 | 
			
		||||
  {1}
 | 
			
		||||
 | 
			
		||||
""".format(name, help)
 | 
			
		||||
 | 
			
		||||
    def end_positionals(self):
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def begin_optionals(self):
 | 
			
		||||
        self.line()
 | 
			
		||||
        self.line('**Optional arguments**\n')
 | 
			
		||||
        return '\n**Optional arguments**\n\n'
 | 
			
		||||
 | 
			
		||||
    def optional(self, opts, help):
 | 
			
		||||
        self.line('``%s``' % opts)
 | 
			
		||||
        self.line('  %s\n' % help)
 | 
			
		||||
        return """\
 | 
			
		||||
``{0}``
 | 
			
		||||
  {1}
 | 
			
		||||
 | 
			
		||||
""".format(opts, help)
 | 
			
		||||
 | 
			
		||||
    def end_optionals(self):
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def begin_subcommands(self, subcommands):
 | 
			
		||||
        self.line()
 | 
			
		||||
        self.line('**Subcommands**\n')
 | 
			
		||||
        self.line('.. hlist::')
 | 
			
		||||
        self.line('   :columns: 4\n')
 | 
			
		||||
        string = """
 | 
			
		||||
**Subcommands**
 | 
			
		||||
 | 
			
		||||
        for cmd in subcommands:
 | 
			
		||||
            prog = cmd.prog
 | 
			
		||||
            if self.strip_root_prog:
 | 
			
		||||
                prog = re.sub(r'^[^ ]* ', '', prog)
 | 
			
		||||
.. hlist::
 | 
			
		||||
   :columns: 4
 | 
			
		||||
 | 
			
		||||
            self.line('   * :ref:`%s <%s>`'
 | 
			
		||||
                      % (prog, cmd.prog.replace(' ', '-')))
 | 
			
		||||
        self.line()
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
        for cmd, _ in subcommands:
 | 
			
		||||
            prog = re.sub(r'^[^ ]* ', '', cmd.prog)
 | 
			
		||||
            string += '   * :ref:`{0} <{1}>`\n'.format(
 | 
			
		||||
                prog, cmd.prog.replace(' ', '-'))
 | 
			
		||||
 | 
			
		||||
        return string + '\n'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ArgparseCompletionWriter(ArgparseWriter):
 | 
			
		||||
    """Write argparse output as shell programmable tab completion functions."""
 | 
			
		||||
 | 
			
		||||
    def format(self, cmd):
 | 
			
		||||
        """Returns the string representation of a single node in the
 | 
			
		||||
        parser tree.
 | 
			
		||||
 | 
			
		||||
        Override this in subclasses to define how each subcommand
 | 
			
		||||
        should be displayed.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            (Command): parsed information about a command or subcommand
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the string representation of this subcommand
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        assert cmd.optionals  # we should always at least have -h, --help
 | 
			
		||||
        assert not (cmd.positionals and cmd.subcommands)  # one or the other
 | 
			
		||||
 | 
			
		||||
        # We only care about the arguments/flags, not the help messages
 | 
			
		||||
        positionals = []
 | 
			
		||||
        if cmd.positionals:
 | 
			
		||||
            positionals, _ = zip(*cmd.positionals)
 | 
			
		||||
        optionals, _, _ = zip(*cmd.optionals)
 | 
			
		||||
        subcommands = []
 | 
			
		||||
        if cmd.subcommands:
 | 
			
		||||
            _, subcommands = zip(*cmd.subcommands)
 | 
			
		||||
 | 
			
		||||
        # Flatten lists of lists
 | 
			
		||||
        optionals = [x for xx in optionals for x in xx]
 | 
			
		||||
 | 
			
		||||
        return (self.start_function(cmd.prog) +
 | 
			
		||||
                self.body(positionals, optionals, subcommands) +
 | 
			
		||||
                self.end_function(cmd.prog))
 | 
			
		||||
 | 
			
		||||
    def start_function(self, prog):
 | 
			
		||||
        """Returns the syntax needed to begin a function definition.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            prog (str): the command name
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the function definition beginning
 | 
			
		||||
        """
 | 
			
		||||
        name = prog.replace('-', '_').replace(' ', '_')
 | 
			
		||||
        return '\n_{0}() {{'.format(name)
 | 
			
		||||
 | 
			
		||||
    def end_function(self, prog=None):
 | 
			
		||||
        """Returns the syntax needed to end a function definition.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            prog (str, optional): the command name
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the function definition ending
 | 
			
		||||
        """
 | 
			
		||||
        return '}\n'
 | 
			
		||||
 | 
			
		||||
    def body(self, positionals, optionals, subcommands):
 | 
			
		||||
        """Returns the body of the function.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            positionals (list): list of positional arguments
 | 
			
		||||
            optionals (list): list of optional arguments
 | 
			
		||||
            subcommands (list): list of subcommand parsers
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the function body
 | 
			
		||||
        """
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def positionals(self, positionals):
 | 
			
		||||
        """Returns the syntax for reporting positional arguments.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            positionals (list): list of positional arguments
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the syntax for positional arguments
 | 
			
		||||
        """
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def optionals(self, optionals):
 | 
			
		||||
        """Returns the syntax for reporting optional flags.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            optionals (list): list of optional arguments
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the syntax for optional flags
 | 
			
		||||
        """
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def subcommands(self, subcommands):
 | 
			
		||||
        """Returns the syntax for reporting subcommands.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
            subcommands (list): list of subcommand parsers
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            str: the syntax for subcommand parsers
 | 
			
		||||
        """
 | 
			
		||||
        return ''
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,10 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.environment as ev
 | 
			
		||||
from spack.filesystem_view import YamlFilesystemView
 | 
			
		||||
 | 
			
		||||
@@ -23,9 +22,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-v', '--view', metavar='VIEW', type=str,
 | 
			
		||||
        help="the view to operate on")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="spec of package extension to activate")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['installed_spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def activate(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,10 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.environment as ev
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -20,8 +19,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument('-l', '--list-name',
 | 
			
		||||
                           dest='list_name', default='specs',
 | 
			
		||||
                           help="name of the list to add specs to")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER, help="specs of packages to add")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ def setup_parser(subparser):
 | 
			
		||||
        help='show git blame output instead of summary')
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'package_name', help='name of package to show contributions for, '
 | 
			
		||||
        'package_or_file', help='name of package to show contributions for, '
 | 
			
		||||
        'or path to a file in the spack repo')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -47,13 +47,13 @@ def blame(parser, args):
 | 
			
		||||
 | 
			
		||||
    # Get name of file to blame
 | 
			
		||||
    blame_file = None
 | 
			
		||||
    if os.path.isfile(args.package_name):
 | 
			
		||||
        path = os.path.realpath(args.package_name)
 | 
			
		||||
    if os.path.isfile(args.package_or_file):
 | 
			
		||||
        path = os.path.realpath(args.package_or_file)
 | 
			
		||||
        if path.startswith(spack.paths.prefix):
 | 
			
		||||
            blame_file = path
 | 
			
		||||
 | 
			
		||||
    if not blame_file:
 | 
			
		||||
        pkg = spack.repo.get(args.package_name)
 | 
			
		||||
        pkg = spack.repo.get(args.package_or_file)
 | 
			
		||||
        blame_file = pkg.module.__file__.rstrip('c')  # .pyc -> .py
 | 
			
		||||
 | 
			
		||||
    # get git blame for the package
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        metavar='spec [--] [cmd]...',
 | 
			
		||||
        help="specs of package environment to emulate")
 | 
			
		||||
        help="spec of package environment to emulate")
 | 
			
		||||
    subparser.epilog\
 | 
			
		||||
        = 'If a command is not specified, the environment will be printed ' \
 | 
			
		||||
        'to standard output (cf /usr/bin/env) unless --dump and/or --pickle ' \
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import sys
 | 
			
		||||
@@ -61,11 +60,9 @@ def setup_parser(subparser):
 | 
			
		||||
                                            "building package(s)")
 | 
			
		||||
    create.add_argument('-y', '--spec-yaml', default=None,
 | 
			
		||||
                        help='Create buildcache entry for spec from yaml file')
 | 
			
		||||
    create.add_argument(
 | 
			
		||||
        'packages', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to create buildcache for")
 | 
			
		||||
    create.add_argument('--no-deps', action='store_true', default='false',
 | 
			
		||||
                        help='Create buildcache entry wo/ dependencies')
 | 
			
		||||
    arguments.add_common_arguments(create, ['specs'])
 | 
			
		||||
    create.set_defaults(func=createtarball)
 | 
			
		||||
 | 
			
		||||
    install = subparsers.add_parser('install', help=installtarball.__doc__)
 | 
			
		||||
@@ -79,9 +76,7 @@ def setup_parser(subparser):
 | 
			
		||||
    install.add_argument('-u', '--unsigned', action='store_true',
 | 
			
		||||
                         help="install unsigned buildcache" +
 | 
			
		||||
                              " tarballs for testing")
 | 
			
		||||
    install.add_argument(
 | 
			
		||||
        'packages', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to install buildcache for")
 | 
			
		||||
    arguments.add_common_arguments(install, ['specs'])
 | 
			
		||||
    install.set_defaults(func=installtarball)
 | 
			
		||||
 | 
			
		||||
    listcache = subparsers.add_parser('list', help=listspecs.__doc__)
 | 
			
		||||
@@ -92,9 +87,7 @@ def setup_parser(subparser):
 | 
			
		||||
                           help='show variants in output (can be long)')
 | 
			
		||||
    listcache.add_argument('-f', '--force', action='store_true',
 | 
			
		||||
                           help="force new download of specs")
 | 
			
		||||
    listcache.add_argument(
 | 
			
		||||
        'packages', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to search for")
 | 
			
		||||
    arguments.add_common_arguments(listcache, ['specs'])
 | 
			
		||||
    listcache.set_defaults(func=listspecs)
 | 
			
		||||
 | 
			
		||||
    dlkeys = subparsers.add_parser('keys', help=getkeys.__doc__)
 | 
			
		||||
@@ -113,10 +106,9 @@ def setup_parser(subparser):
 | 
			
		||||
        help='analyzes an installed spec and reports whether '
 | 
			
		||||
             'executables and libraries are relocatable'
 | 
			
		||||
    )
 | 
			
		||||
    preview_parser.add_argument(
 | 
			
		||||
        'packages', nargs='+', help='list of installed packages'
 | 
			
		||||
    )
 | 
			
		||||
    arguments.add_common_arguments(preview_parser, ['installed_specs'])
 | 
			
		||||
    preview_parser.set_defaults(func=preview)
 | 
			
		||||
 | 
			
		||||
    # Check if binaries need to be rebuilt on remote mirror
 | 
			
		||||
    check = subparsers.add_parser('check', help=check_binaries.__doc__)
 | 
			
		||||
    check.add_argument(
 | 
			
		||||
@@ -313,8 +305,10 @@ def _createtarball(env, spec_yaml, packages, directory, key, no_deps, force,
 | 
			
		||||
            tty.debug(yaml_text)
 | 
			
		||||
            s = Spec.from_yaml(yaml_text)
 | 
			
		||||
            packages.add('/{0}'.format(s.dag_hash()))
 | 
			
		||||
 | 
			
		||||
    elif packages:
 | 
			
		||||
        packages = packages
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
        tty.die("build cache file creation requires at least one" +
 | 
			
		||||
                " installed package argument or else path to a" +
 | 
			
		||||
@@ -378,17 +372,17 @@ def createtarball(args):
 | 
			
		||||
    # restrict matching to current environment if one is active
 | 
			
		||||
    env = ev.get_env(args, 'buildcache create')
 | 
			
		||||
 | 
			
		||||
    _createtarball(env, args.spec_yaml, args.packages, args.directory,
 | 
			
		||||
    _createtarball(env, args.spec_yaml, args.specs, args.directory,
 | 
			
		||||
                   args.key, args.no_deps, args.force, args.rel, args.unsigned,
 | 
			
		||||
                   args.allow_root, args.no_rebuild_index)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def installtarball(args):
 | 
			
		||||
    """install from a binary package"""
 | 
			
		||||
    if not args.packages:
 | 
			
		||||
    if not args.specs:
 | 
			
		||||
        tty.die("build cache file installation requires" +
 | 
			
		||||
                " at least one package spec argument")
 | 
			
		||||
    pkgs = set(args.packages)
 | 
			
		||||
    pkgs = set(args.specs)
 | 
			
		||||
    matches = match_downloaded_specs(pkgs, args.multiple, args.force)
 | 
			
		||||
 | 
			
		||||
    for match in matches:
 | 
			
		||||
@@ -422,8 +416,8 @@ def install_tarball(spec, args):
 | 
			
		||||
def listspecs(args):
 | 
			
		||||
    """list binary packages available from mirrors"""
 | 
			
		||||
    specs = bindist.get_specs(args.force)
 | 
			
		||||
    if args.packages:
 | 
			
		||||
        constraints = set(args.packages)
 | 
			
		||||
    if args.specs:
 | 
			
		||||
        constraints = set(args.specs)
 | 
			
		||||
        specs = [s for s in specs if any(s.satisfies(c) for c in constraints)]
 | 
			
		||||
    display_specs(specs, args, all_headers=True)
 | 
			
		||||
 | 
			
		||||
@@ -440,7 +434,7 @@ def preview(args):
 | 
			
		||||
    Args:
 | 
			
		||||
        args: command line arguments
 | 
			
		||||
    """
 | 
			
		||||
    specs = find_matching_specs(args.packages, allow_multiple_matches=True)
 | 
			
		||||
    specs = find_matching_specs(args.specs, allow_multiple_matches=True)
 | 
			
		||||
 | 
			
		||||
    # Cycle over the specs that match
 | 
			
		||||
    for spec in specs:
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.repo
 | 
			
		||||
import spack.stage
 | 
			
		||||
import spack.util.crypto
 | 
			
		||||
@@ -22,12 +23,10 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'package',
 | 
			
		||||
        help='package to checksum versions for')
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '--keep-stage', action='store_true',
 | 
			
		||||
        help="don't clean up staging area when command completes")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['package'])
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'versions', nargs=argparse.REMAINDER,
 | 
			
		||||
        help='versions to generate checksums for')
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
 | 
			
		||||
import spack.caches
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.repo
 | 
			
		||||
import spack.stage
 | 
			
		||||
from spack.paths import lib_path, var_path
 | 
			
		||||
@@ -43,11 +44,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-a', '--all', action=AllClean, help="equivalent to -sdmp", nargs=0
 | 
			
		||||
    )
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'specs',
 | 
			
		||||
        nargs=argparse.REMAINDER,
 | 
			
		||||
        help="removes the build stages and tarballs for specs"
 | 
			
		||||
    )
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clean(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,16 @@
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import argparse
 | 
			
		||||
import copy
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import argparse
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
from llnl.util.argparsewriter import ArgparseWriter, ArgparseRstWriter
 | 
			
		||||
from llnl.util.argparsewriter import (
 | 
			
		||||
    ArgparseWriter, ArgparseRstWriter, ArgparseCompletionWriter
 | 
			
		||||
)
 | 
			
		||||
from llnl.util.tty.colify import colify
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
@@ -35,6 +38,8 @@ def formatter(func):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-a', '--aliases', action='store_true', help='include command aliases')
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '--format', default='names', choices=formatters,
 | 
			
		||||
        help='format to be used to print the output (default: names)')
 | 
			
		||||
@@ -52,29 +57,97 @@ def setup_parser(subparser):
 | 
			
		||||
class SpackArgparseRstWriter(ArgparseRstWriter):
 | 
			
		||||
    """RST writer tailored for spack documentation."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, documented_commands, out=sys.stdout):
 | 
			
		||||
        super(SpackArgparseRstWriter, self).__init__(out)
 | 
			
		||||
        self.documented = documented_commands if documented_commands else []
 | 
			
		||||
    def __init__(self, prog, out=sys.stdout, aliases=False,
 | 
			
		||||
                 documented_commands=[],
 | 
			
		||||
                 rst_levels=['-', '-', '^', '~', ':', '`']):
 | 
			
		||||
        super(SpackArgparseRstWriter, self).__init__(
 | 
			
		||||
            prog, out, aliases, rst_levels)
 | 
			
		||||
        self.documented = documented_commands
 | 
			
		||||
 | 
			
		||||
    def usage(self, *args):
 | 
			
		||||
        super(SpackArgparseRstWriter, self).usage(*args)
 | 
			
		||||
        cmd = re.sub(' ', '-', self.parser.prog)
 | 
			
		||||
        string = super(SpackArgparseRstWriter, self).usage(*args)
 | 
			
		||||
 | 
			
		||||
        cmd = self.parser.prog.replace(' ', '-')
 | 
			
		||||
        if cmd in self.documented:
 | 
			
		||||
            self.line()
 | 
			
		||||
            self.line(':ref:`More documentation <cmd-%s>`' % cmd)
 | 
			
		||||
            string += '\n:ref:`More documentation <cmd-{0}>`\n'.format(cmd)
 | 
			
		||||
 | 
			
		||||
        return string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SubcommandWriter(ArgparseWriter):
 | 
			
		||||
    def begin_command(self, prog):
 | 
			
		||||
        self.out.write('    ' * self.level + prog)
 | 
			
		||||
        self.out.write('\n')
 | 
			
		||||
    def format(self, cmd):
 | 
			
		||||
        return '    ' * self.level + cmd.prog + '\n'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_positional_to_subroutine = {
 | 
			
		||||
    'package': '_all_packages',
 | 
			
		||||
    'spec': '_all_packages',
 | 
			
		||||
    'filter': '_all_packages',
 | 
			
		||||
    'installed': '_installed_packages',
 | 
			
		||||
    'compiler': '_installed_compilers',
 | 
			
		||||
    'section': '_config_sections',
 | 
			
		||||
    'env': '_environments',
 | 
			
		||||
    'extendable': '_extensions',
 | 
			
		||||
    'keys': '_keys',
 | 
			
		||||
    'help_command': '_subcommands',
 | 
			
		||||
    'mirror': '_mirrors',
 | 
			
		||||
    'virtual': '_providers',
 | 
			
		||||
    'namespace': '_repos',
 | 
			
		||||
    'hash': '_all_resource_hashes',
 | 
			
		||||
    'pytest': '_tests',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BashCompletionWriter(ArgparseCompletionWriter):
 | 
			
		||||
    """Write argparse output as bash programmable tab completion."""
 | 
			
		||||
 | 
			
		||||
    def body(self, positionals, optionals, subcommands):
 | 
			
		||||
        if positionals:
 | 
			
		||||
            return """
 | 
			
		||||
    if $list_options
 | 
			
		||||
    then
 | 
			
		||||
        {0}
 | 
			
		||||
    else
 | 
			
		||||
        {1}
 | 
			
		||||
    fi
 | 
			
		||||
""".format(self.optionals(optionals), self.positionals(positionals))
 | 
			
		||||
        elif subcommands:
 | 
			
		||||
            return """
 | 
			
		||||
    if $list_options
 | 
			
		||||
    then
 | 
			
		||||
        {0}
 | 
			
		||||
    else
 | 
			
		||||
        {1}
 | 
			
		||||
    fi
 | 
			
		||||
""".format(self.optionals(optionals), self.subcommands(subcommands))
 | 
			
		||||
        else:
 | 
			
		||||
            return """
 | 
			
		||||
    {0}
 | 
			
		||||
""".format(self.optionals(optionals))
 | 
			
		||||
 | 
			
		||||
    def positionals(self, positionals):
 | 
			
		||||
        # If match found, return function name
 | 
			
		||||
        for positional in positionals:
 | 
			
		||||
            for key, value in _positional_to_subroutine.items():
 | 
			
		||||
                if positional.startswith(key):
 | 
			
		||||
                    return value
 | 
			
		||||
 | 
			
		||||
        # If no matches found, return empty list
 | 
			
		||||
        return 'SPACK_COMPREPLY=""'
 | 
			
		||||
 | 
			
		||||
    def optionals(self, optionals):
 | 
			
		||||
        return 'SPACK_COMPREPLY="{0}"'.format(' '.join(optionals))
 | 
			
		||||
 | 
			
		||||
    def subcommands(self, subcommands):
 | 
			
		||||
        return 'SPACK_COMPREPLY="{0}"'.format(' '.join(subcommands))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@formatter
 | 
			
		||||
def subcommands(args, out):
 | 
			
		||||
    parser = spack.main.make_argument_parser()
 | 
			
		||||
    spack.main.add_all_commands(parser)
 | 
			
		||||
    SubcommandWriter(out).write(parser)
 | 
			
		||||
    writer = SubcommandWriter(parser.prog, out, args.aliases)
 | 
			
		||||
    writer.write(parser)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rst_index(out):
 | 
			
		||||
@@ -124,12 +197,28 @@ def rst(args, out):
 | 
			
		||||
    out.write('\n')
 | 
			
		||||
 | 
			
		||||
    # print sections for each command and subcommand
 | 
			
		||||
    SpackArgparseRstWriter(documented_commands, out).write(parser, root=1)
 | 
			
		||||
    writer = SpackArgparseRstWriter(
 | 
			
		||||
        parser.prog, out, args.aliases, documented_commands)
 | 
			
		||||
    writer.write(parser)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@formatter
 | 
			
		||||
def names(args, out):
 | 
			
		||||
    colify(spack.cmd.all_commands(), output=out)
 | 
			
		||||
    commands = copy.copy(spack.cmd.all_commands())
 | 
			
		||||
 | 
			
		||||
    if args.aliases:
 | 
			
		||||
        commands.extend(spack.main.aliases.keys())
 | 
			
		||||
 | 
			
		||||
    colify(commands, output=out)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@formatter
 | 
			
		||||
def bash(args, out):
 | 
			
		||||
    parser = spack.main.make_argument_parser()
 | 
			
		||||
    spack.main.add_all_commands(parser)
 | 
			
		||||
 | 
			
		||||
    writer = BashCompletionWriter(parser.prog, out, args.aliases)
 | 
			
		||||
    writer.write(parser)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def prepend_header(args, out):
 | 
			
		||||
@@ -148,12 +237,14 @@ def commands(parser, args):
 | 
			
		||||
        tty.die("No such file: '%s'" % args.header)
 | 
			
		||||
 | 
			
		||||
    # if we're updating an existing file, only write output if a command
 | 
			
		||||
    # is newer than the file.
 | 
			
		||||
    # or the header is newer than the file.
 | 
			
		||||
    if args.update:
 | 
			
		||||
        if os.path.exists(args.update):
 | 
			
		||||
            files = [
 | 
			
		||||
                spack.cmd.get_module(command).__file__.rstrip('c')  # pyc -> py
 | 
			
		||||
                for command in spack.cmd.all_commands()]
 | 
			
		||||
            if args.header:
 | 
			
		||||
                files.append(args.header)
 | 
			
		||||
            last_update = os.path.getmtime(args.update)
 | 
			
		||||
            if not any(os.path.getmtime(f) > last_update for f in files):
 | 
			
		||||
                tty.msg('File is up to date: %s' % args.update)
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,7 @@ def default(self, value):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DeptypeAction(argparse.Action):
 | 
			
		||||
    """Creates a tuple of valid dependency tpyes from a deptype argument."""
 | 
			
		||||
    """Creates a tuple of valid dependency types from a deptype argument."""
 | 
			
		||||
    def __call__(self, parser, namespace, values, option_string=None):
 | 
			
		||||
        deptype = dep.all_deptypes
 | 
			
		||||
        if values:
 | 
			
		||||
@@ -132,11 +132,53 @@ def __call__(self, parser, namespace, values, option_string=None):
 | 
			
		||||
        setattr(namespace, self.dest, deptype)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# TODO: merge constraint and installed_specs
 | 
			
		||||
@arg
 | 
			
		||||
def constraint():
 | 
			
		||||
    return Args(
 | 
			
		||||
        'constraint', nargs=argparse.REMAINDER, action=ConstraintAction,
 | 
			
		||||
        help='constraint to select a subset of installed packages')
 | 
			
		||||
        help='constraint to select a subset of installed packages',
 | 
			
		||||
        metavar='installed_specs')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@arg
 | 
			
		||||
def package():
 | 
			
		||||
    return Args('package', help='package name')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@arg
 | 
			
		||||
def packages():
 | 
			
		||||
    return Args(
 | 
			
		||||
        'packages', nargs='+', help='one or more package names',
 | 
			
		||||
        metavar='package')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Specs must use `nargs=argparse.REMAINDER` because a single spec can
 | 
			
		||||
# contain spaces, and contain variants like '-mpi' that argparse thinks
 | 
			
		||||
# are a collection of optional flags.
 | 
			
		||||
@arg
 | 
			
		||||
def spec():
 | 
			
		||||
    return Args('spec', nargs=argparse.REMAINDER, help='package spec')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@arg
 | 
			
		||||
def specs():
 | 
			
		||||
    return Args(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER, help='one or more package specs')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@arg
 | 
			
		||||
def installed_spec():
 | 
			
		||||
    return Args(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER, help='installed package spec',
 | 
			
		||||
        metavar='installed_spec')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@arg
 | 
			
		||||
def installed_specs():
 | 
			
		||||
    return Args(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER,
 | 
			
		||||
        help='one or more installed package specs', metavar='installed_specs')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@arg
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ def setup_parser(subparser):
 | 
			
		||||
                            help="configuration section to print. "
 | 
			
		||||
                                 "options: %(choices)s",
 | 
			
		||||
                            nargs='?',
 | 
			
		||||
                            metavar='SECTION',
 | 
			
		||||
                            metavar='section',
 | 
			
		||||
                            choices=spack.config.section_schemas)
 | 
			
		||||
 | 
			
		||||
    blame_parser = sp.add_parser(
 | 
			
		||||
@@ -42,14 +42,14 @@ def setup_parser(subparser):
 | 
			
		||||
    blame_parser.add_argument('section',
 | 
			
		||||
                              help="configuration section to print. "
 | 
			
		||||
                              "options: %(choices)s",
 | 
			
		||||
                              metavar='SECTION',
 | 
			
		||||
                              metavar='section',
 | 
			
		||||
                              choices=spack.config.section_schemas)
 | 
			
		||||
 | 
			
		||||
    edit_parser = sp.add_parser('edit', help='edit configuration file')
 | 
			
		||||
    edit_parser.add_argument('section',
 | 
			
		||||
                             help="configuration section to edit. "
 | 
			
		||||
                                  "options: %(choices)s",
 | 
			
		||||
                             metavar='SECTION',
 | 
			
		||||
                             metavar='section',
 | 
			
		||||
                             nargs='?',
 | 
			
		||||
                             choices=spack.config.section_schemas)
 | 
			
		||||
    edit_parser.add_argument(
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.cmd.install as inst
 | 
			
		||||
 | 
			
		||||
from spack.build_systems.autotools import AutotoolsPackage
 | 
			
		||||
@@ -36,16 +37,12 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'package',
 | 
			
		||||
        nargs=argparse.REMAINDER,
 | 
			
		||||
        help="spec of the package to install"
 | 
			
		||||
    )
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-v', '--verbose',
 | 
			
		||||
        action='store_true',
 | 
			
		||||
        help="print additional output during builds"
 | 
			
		||||
    )
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _stop_at_phase_during_install(args, calling_fn, phase_mapping):
 | 
			
		||||
@@ -64,15 +61,15 @@ def _stop_at_phase_during_install(args, calling_fn, phase_mapping):
 | 
			
		||||
        # Install package dependencies if needed
 | 
			
		||||
        parser = argparse.ArgumentParser()
 | 
			
		||||
        inst.setup_parser(parser)
 | 
			
		||||
        tty.msg('Checking dependencies for {0}'.format(args.package[0]))
 | 
			
		||||
        tty.msg('Checking dependencies for {0}'.format(args.spec[0]))
 | 
			
		||||
        cli_args = ['-v'] if args.verbose else []
 | 
			
		||||
        install_args = parser.parse_args(cli_args + ['--only=dependencies'])
 | 
			
		||||
        install_args.package = args.package
 | 
			
		||||
        install_args.spec = args.spec
 | 
			
		||||
        inst.install(parser, install_args)
 | 
			
		||||
        # Install package and stop at the given phase
 | 
			
		||||
        cli_args = ['-v'] if args.verbose else []
 | 
			
		||||
        install_args = parser.parse_args(cli_args + ['--only=package'])
 | 
			
		||||
        install_args.package = args.package
 | 
			
		||||
        install_args.spec = args.spec
 | 
			
		||||
        inst.install(parser, install_args, stop_at=phase)
 | 
			
		||||
    except IndexError:
 | 
			
		||||
        tty.error(
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,10 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.environment as ev
 | 
			
		||||
import spack.store
 | 
			
		||||
from spack.filesystem_view import YamlFilesystemView
 | 
			
		||||
@@ -28,9 +28,7 @@ def setup_parser(subparser):
 | 
			
		||||
        '-a', '--all', action='store_true',
 | 
			
		||||
        help="deactivate all extensions of an extendable package, or "
 | 
			
		||||
        "deactivate an extension AND its dependencies")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="spec of package extension to deactivate")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['installed_spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def deactivate(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
from llnl.util.tty.colify import colify
 | 
			
		||||
 | 
			
		||||
@@ -31,8 +29,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-V', '--no-expand-virtuals', action='store_false', default=True,
 | 
			
		||||
        dest="expand_virtuals", help="do not expand virtual dependencies")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER, help="spec or package name")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def dependencies(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,15 +3,14 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
from llnl.util.tty.colify import colify
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.environment as ev
 | 
			
		||||
import spack.repo
 | 
			
		||||
import spack.store
 | 
			
		||||
import spack.cmd
 | 
			
		||||
 | 
			
		||||
description = "show packages that depend on another"
 | 
			
		||||
section = "basic"
 | 
			
		||||
@@ -26,8 +25,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-t', '--transitive', action='store_true', default=False,
 | 
			
		||||
        help="Show all transitive dependents.")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER, help="spec or package name")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def inverted_dependencies():
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,13 @@
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.config
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.repo
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.repo
 | 
			
		||||
from spack.stage import DIYStage
 | 
			
		||||
 | 
			
		||||
description = "developer build: build from code in current working directory"
 | 
			
		||||
@@ -41,9 +40,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-u', '--until', type=str, dest='until', default=None,
 | 
			
		||||
        help="phase to stop after when installing (default None)")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs to use for install. must contain package AND version")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['spec'])
 | 
			
		||||
 | 
			
		||||
    cd_group = subparser.add_mutually_exclusive_group()
 | 
			
		||||
    arguments.add_common_arguments(cd_group, ['clean', 'dirty'])
 | 
			
		||||
 
 | 
			
		||||
@@ -84,12 +84,11 @@ def setup_parser(subparser):
 | 
			
		||||
        help="namespace of package to edit")
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'name', nargs='?', default=None,
 | 
			
		||||
        help="name of package to edit")
 | 
			
		||||
        'package', nargs='?', default=None, help="package name")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def edit(parser, args):
 | 
			
		||||
    name = args.name
 | 
			
		||||
    name = args.package
 | 
			
		||||
 | 
			
		||||
    # By default, edit package files
 | 
			
		||||
    path = spack.paths.packages_path
 | 
			
		||||
 
 | 
			
		||||
@@ -157,7 +157,7 @@ def env_deactivate(args):
 | 
			
		||||
def env_create_setup_parser(subparser):
 | 
			
		||||
    """create a new environment"""
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'create_env', metavar='ENV', help='name of environment to create')
 | 
			
		||||
        'create_env', metavar='env', help='name of environment to create')
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-d', '--dir', action='store_true',
 | 
			
		||||
        help='create an environment in a specific directory')
 | 
			
		||||
@@ -221,7 +221,7 @@ def _env_create(name_or_path, init_file=None, dir=False, with_view=None):
 | 
			
		||||
def env_remove_setup_parser(subparser):
 | 
			
		||||
    """remove an existing environment"""
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'rm_env', metavar='ENV', nargs='+',
 | 
			
		||||
        'rm_env', metavar='env', nargs='+',
 | 
			
		||||
        help='environment(s) to remove')
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['yes_to_all'])
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ def setup_parser(subparser):
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help='spec of package to list extensions for')
 | 
			
		||||
        help='spec of package to list extensions for', metavar='extendable')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def extensions(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,14 +3,12 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.config
 | 
			
		||||
import spack.repo
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
 | 
			
		||||
description = "fetch archives for packages"
 | 
			
		||||
section = "build"
 | 
			
		||||
@@ -25,19 +23,17 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-D', '--dependencies', action='store_true',
 | 
			
		||||
        help="also fetch all dependencies")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'packages', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to fetch")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fetch(parser, args):
 | 
			
		||||
    if not args.packages:
 | 
			
		||||
    if not args.specs:
 | 
			
		||||
        tty.die("fetch requires at least one package argument")
 | 
			
		||||
 | 
			
		||||
    if args.no_checksum:
 | 
			
		||||
        spack.config.set('config:checksum', False, scope='command_line')
 | 
			
		||||
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.packages, concretize=True)
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.specs, concretize=True)
 | 
			
		||||
    for spec in specs:
 | 
			
		||||
        if args.missing or args.dependencies:
 | 
			
		||||
            for s in spec.traverse():
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
import os
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.paths
 | 
			
		||||
from spack.util.gpg import Gpg
 | 
			
		||||
 | 
			
		||||
@@ -19,8 +20,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparsers = subparser.add_subparsers(help='GPG sub-commands')
 | 
			
		||||
 | 
			
		||||
    verify = subparsers.add_parser('verify', help=gpg_verify.__doc__)
 | 
			
		||||
    verify.add_argument('package', type=str,
 | 
			
		||||
                        help='the package to verify')
 | 
			
		||||
    arguments.add_common_arguments(verify, ['installed_spec'])
 | 
			
		||||
    verify.add_argument('signature', type=str, nargs='?',
 | 
			
		||||
                        help='the signature file')
 | 
			
		||||
    verify.set_defaults(func=gpg_verify)
 | 
			
		||||
@@ -44,8 +44,7 @@ def setup_parser(subparser):
 | 
			
		||||
                      help='the key to use for signing')
 | 
			
		||||
    sign.add_argument('--clearsign', action='store_true',
 | 
			
		||||
                      help='if specified, create a clearsign signature')
 | 
			
		||||
    sign.add_argument('package', type=str,
 | 
			
		||||
                      help='the package to sign')
 | 
			
		||||
    arguments.add_common_arguments(sign, ['installed_spec'])
 | 
			
		||||
    sign.set_defaults(func=gpg_sign)
 | 
			
		||||
 | 
			
		||||
    create = subparsers.add_parser('create', help=gpg_create.__doc__)
 | 
			
		||||
@@ -122,9 +121,9 @@ def gpg_sign(args):
 | 
			
		||||
                               'please choose one')
 | 
			
		||||
    output = args.output
 | 
			
		||||
    if not output:
 | 
			
		||||
        output = args.package + '.asc'
 | 
			
		||||
        output = args.spec[0] + '.asc'
 | 
			
		||||
    # TODO: Support the package format Spack creates.
 | 
			
		||||
    Gpg.sign(key, args.package, output, args.clearsign)
 | 
			
		||||
    Gpg.sign(key, ' '.join(args.spec), output, args.clearsign)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gpg_trust(args):
 | 
			
		||||
@@ -155,8 +154,8 @@ def gpg_verify(args):
 | 
			
		||||
    # TODO: Support the package format Spack creates.
 | 
			
		||||
    signature = args.signature
 | 
			
		||||
    if signature is None:
 | 
			
		||||
        signature = args.package + '.asc'
 | 
			
		||||
    Gpg.verify(signature, args.package)
 | 
			
		||||
        signature = args.spec[0] + '.asc'
 | 
			
		||||
    Gpg.verify(signature, ' '.join(args.spec))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gpg(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
@@ -38,11 +37,7 @@ def setup_parser(subparser):
 | 
			
		||||
        '-i', '--installed', action='store_true',
 | 
			
		||||
        help="graph all installed specs in dot format (implies --dot)")
 | 
			
		||||
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['deptype'])
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to graph")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['deptype', 'specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def graph(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
import llnl.util.tty.color as color
 | 
			
		||||
from llnl.util.tty.colify import colify
 | 
			
		||||
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.repo
 | 
			
		||||
import spack.spec
 | 
			
		||||
import spack.fetch_strategy as fs
 | 
			
		||||
@@ -36,8 +37,7 @@ def pad(string):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'name', metavar='PACKAGE', help='name of package to get info for')
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['package'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def section_title(s):
 | 
			
		||||
@@ -237,5 +237,5 @@ def print_text_info(pkg):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def info(parser, args):
 | 
			
		||||
    pkg = spack.repo.get(args.name)
 | 
			
		||||
    pkg = spack.repo.get(args.package)
 | 
			
		||||
    print_text_info(pkg)
 | 
			
		||||
 
 | 
			
		||||
@@ -122,11 +122,6 @@ def setup_parser(subparser):
 | 
			
		||||
    cd_group = subparser.add_mutually_exclusive_group()
 | 
			
		||||
    arguments.add_common_arguments(cd_group, ['clean', 'dirty'])
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'package',
 | 
			
		||||
        nargs=argparse.REMAINDER,
 | 
			
		||||
        help="spec of the package to install"
 | 
			
		||||
    )
 | 
			
		||||
    testing = subparser.add_mutually_exclusive_group()
 | 
			
		||||
    testing.add_argument(
 | 
			
		||||
        '--test', default=None,
 | 
			
		||||
@@ -157,7 +152,7 @@ def setup_parser(subparser):
 | 
			
		||||
        help="Show usage instructions for CDash reporting"
 | 
			
		||||
    )
 | 
			
		||||
    add_cdash_args(subparser, False)
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['yes_to_all'])
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['yes_to_all', 'spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_cdash_args(subparser, add_help):
 | 
			
		||||
@@ -258,7 +253,7 @@ def install(parser, args, **kwargs):
 | 
			
		||||
        parser.print_help()
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if not args.package and not args.specfiles:
 | 
			
		||||
    if not args.spec and not args.specfiles:
 | 
			
		||||
        # if there are no args but an active environment or spack.yaml file
 | 
			
		||||
        # then install the packages from it.
 | 
			
		||||
        env = ev.get_env(args, 'install')
 | 
			
		||||
@@ -293,7 +288,7 @@ def install(parser, args, **kwargs):
 | 
			
		||||
    if args.log_file:
 | 
			
		||||
        reporter.filename = args.log_file
 | 
			
		||||
 | 
			
		||||
    abstract_specs = spack.cmd.parse_specs(args.package)
 | 
			
		||||
    abstract_specs = spack.cmd.parse_specs(args.spec)
 | 
			
		||||
    tests = False
 | 
			
		||||
    if args.test == 'all' or args.run_tests:
 | 
			
		||||
        tests = True
 | 
			
		||||
@@ -303,7 +298,7 @@ def install(parser, args, **kwargs):
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        specs = spack.cmd.parse_specs(
 | 
			
		||||
            args.package, concretize=True, tests=tests)
 | 
			
		||||
            args.spec, concretize=True, tests=tests)
 | 
			
		||||
    except SpackError as e:
 | 
			
		||||
        tty.debug(e)
 | 
			
		||||
        reporter.concretization_report(e.message)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
from spack.cmd.common import print_module_placeholder_help, arguments
 | 
			
		||||
 | 
			
		||||
description = "add package to environment using `module load`"
 | 
			
		||||
@@ -14,11 +13,8 @@
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    """Parser is only constructed so that this prints a nice help
 | 
			
		||||
       message with -h. """
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="spec of package to load with modules "
 | 
			
		||||
    )
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['recurse_dependencies'])
 | 
			
		||||
    arguments.add_common_arguments(
 | 
			
		||||
        subparser, ['recurse_dependencies', 'installed_spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,11 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import argparse
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.environment as ev
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.environment
 | 
			
		||||
import spack.paths
 | 
			
		||||
import spack.repo
 | 
			
		||||
@@ -55,9 +55,7 @@ def setup_parser(subparser):
 | 
			
		||||
        '-e', '--env', action='store',
 | 
			
		||||
        help="location of an environment managed by spack")
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="spec of package to fetch directory for")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def location(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ def setup_parser(subparser):
 | 
			
		||||
 | 
			
		||||
    # options for commands that take package arguments
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'pkg_or_user', nargs=argparse.REMAINDER,
 | 
			
		||||
        'package_or_user', nargs=argparse.REMAINDER,
 | 
			
		||||
        help='names of packages or users to get info for')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -104,31 +104,31 @@ def maintainers(parser, args):
 | 
			
		||||
 | 
			
		||||
    if args.all:
 | 
			
		||||
        if args.by_user:
 | 
			
		||||
            maintainers = maintainers_to_packages(args.pkg_or_user)
 | 
			
		||||
            maintainers = maintainers_to_packages(args.package_or_user)
 | 
			
		||||
            for user, packages in sorted(maintainers.items()):
 | 
			
		||||
                color.cprint('@c{%s}: %s'
 | 
			
		||||
                             % (user, ', '.join(sorted(packages))))
 | 
			
		||||
            return 0 if maintainers else 1
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            packages = packages_to_maintainers(args.pkg_or_user)
 | 
			
		||||
            packages = packages_to_maintainers(args.package_or_user)
 | 
			
		||||
            for pkg, maintainers in sorted(packages.items()):
 | 
			
		||||
                color.cprint('@c{%s}: %s'
 | 
			
		||||
                             % (pkg, ', '.join(sorted(maintainers))))
 | 
			
		||||
            return 0 if packages else 1
 | 
			
		||||
 | 
			
		||||
    if args.by_user:
 | 
			
		||||
        if not args.pkg_or_user:
 | 
			
		||||
        if not args.package_or_user:
 | 
			
		||||
            tty.die('spack maintainers --by-user requires a user or --all')
 | 
			
		||||
 | 
			
		||||
        packages = union_values(maintainers_to_packages(args.pkg_or_user))
 | 
			
		||||
        packages = union_values(maintainers_to_packages(args.package_or_user))
 | 
			
		||||
        colify(packages)
 | 
			
		||||
        return 0 if packages else 1
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
        if not args.pkg_or_user:
 | 
			
		||||
        if not args.package_or_user:
 | 
			
		||||
            tty.die('spack maintainers requires a package or --all')
 | 
			
		||||
 | 
			
		||||
        users = union_values(packages_to_maintainers(args.pkg_or_user))
 | 
			
		||||
        users = union_values(packages_to_maintainers(args.package_or_user))
 | 
			
		||||
        colify(users)
 | 
			
		||||
        return 0 if users else 1
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
from llnl.util.tty.colify import colify
 | 
			
		||||
 | 
			
		||||
@@ -39,9 +38,6 @@ def setup_parser(subparser):
 | 
			
		||||
    create_parser.add_argument('-d', '--directory', default=None,
 | 
			
		||||
                               help="directory in which to create mirror")
 | 
			
		||||
 | 
			
		||||
    create_parser.add_argument(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to put in mirror")
 | 
			
		||||
    create_parser.add_argument(
 | 
			
		||||
        '-a', '--all', action='store_true',
 | 
			
		||||
        help="mirror all versions of all packages in Spack, or all packages"
 | 
			
		||||
@@ -57,6 +53,7 @@ def setup_parser(subparser):
 | 
			
		||||
        '-n', '--versions-per-spec',
 | 
			
		||||
        help="the number of versions to fetch for each spec, choose 'all' to"
 | 
			
		||||
             " retrieve all versions of each package")
 | 
			
		||||
    arguments.add_common_arguments(create_parser, ['specs'])
 | 
			
		||||
 | 
			
		||||
    # used to construct scope arguments below
 | 
			
		||||
    scopes = spack.config.scopes()
 | 
			
		||||
@@ -64,7 +61,8 @@ def setup_parser(subparser):
 | 
			
		||||
 | 
			
		||||
    # Add
 | 
			
		||||
    add_parser = sp.add_parser('add', help=mirror_add.__doc__)
 | 
			
		||||
    add_parser.add_argument('name', help="mnemonic name for mirror")
 | 
			
		||||
    add_parser.add_argument(
 | 
			
		||||
        'name', help="mnemonic name for mirror", metavar="mirror")
 | 
			
		||||
    add_parser.add_argument(
 | 
			
		||||
        'url', help="url of mirror directory from 'spack mirror create'")
 | 
			
		||||
    add_parser.add_argument(
 | 
			
		||||
@@ -75,7 +73,8 @@ def setup_parser(subparser):
 | 
			
		||||
    # Remove
 | 
			
		||||
    remove_parser = sp.add_parser('remove', aliases=['rm'],
 | 
			
		||||
                                  help=mirror_remove.__doc__)
 | 
			
		||||
    remove_parser.add_argument('name')
 | 
			
		||||
    remove_parser.add_argument(
 | 
			
		||||
        'name', help="mnemonic name for mirror", metavar="mirror")
 | 
			
		||||
    remove_parser.add_argument(
 | 
			
		||||
        '--scope', choices=scopes, metavar=scopes_metavar,
 | 
			
		||||
        default=spack.config.default_modify_scope(),
 | 
			
		||||
@@ -83,7 +82,8 @@ def setup_parser(subparser):
 | 
			
		||||
 | 
			
		||||
    # Set-Url
 | 
			
		||||
    set_url_parser = sp.add_parser('set-url', help=mirror_set_url.__doc__)
 | 
			
		||||
    set_url_parser.add_argument('name', help="mnemonic name for mirror")
 | 
			
		||||
    set_url_parser.add_argument(
 | 
			
		||||
        'name', help="mnemonic name for mirror", metavar="mirror")
 | 
			
		||||
    set_url_parser.add_argument(
 | 
			
		||||
        'url', help="url of mirror directory from 'spack mirror create'")
 | 
			
		||||
    set_url_parser.add_argument(
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.repo
 | 
			
		||||
@@ -18,20 +16,17 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['no_checksum'])
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'packages', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to stage")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['no_checksum', 'specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def patch(parser, args):
 | 
			
		||||
    if not args.packages:
 | 
			
		||||
        tty.die("patch requires at least one package argument")
 | 
			
		||||
    if not args.specs:
 | 
			
		||||
        tty.die("patch requires at least one spec argument")
 | 
			
		||||
 | 
			
		||||
    if args.no_checksum:
 | 
			
		||||
        spack.config.set('config:checksum', False, scope='command_line')
 | 
			
		||||
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.packages, concretize=True)
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.specs, concretize=True)
 | 
			
		||||
    for spec in specs:
 | 
			
		||||
        package = spack.repo.get(spec)
 | 
			
		||||
        package.do_patch()
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import argparse
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
@@ -14,6 +13,7 @@
 | 
			
		||||
from llnl.util.filesystem import working_dir
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.paths
 | 
			
		||||
import spack.repo
 | 
			
		||||
from spack.util.executable import which
 | 
			
		||||
@@ -28,8 +28,7 @@ def setup_parser(subparser):
 | 
			
		||||
        metavar='SUBCOMMAND', dest='pkg_command')
 | 
			
		||||
 | 
			
		||||
    add_parser = sp.add_parser('add', help=pkg_add.__doc__)
 | 
			
		||||
    add_parser.add_argument('packages', nargs=argparse.REMAINDER,
 | 
			
		||||
                            help="names of packages to add to git repo")
 | 
			
		||||
    arguments.add_common_arguments(add_parser, ['packages'])
 | 
			
		||||
 | 
			
		||||
    list_parser = sp.add_parser('list', help=pkg_list.__doc__)
 | 
			
		||||
    list_parser.add_argument('rev', default='HEAD', nargs='?',
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,10 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.environment as ev
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -26,8 +25,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-f', '--force', action='store_true',
 | 
			
		||||
        help="remove concretized spec (if any) immediately")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER, help="specs to be removed")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def remove(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -51,8 +51,8 @@ def setup_parser(subparser):
 | 
			
		||||
    remove_parser = sp.add_parser(
 | 
			
		||||
        'remove', help=repo_remove.__doc__, aliases=['rm'])
 | 
			
		||||
    remove_parser.add_argument(
 | 
			
		||||
        'path_or_namespace',
 | 
			
		||||
        help="path or namespace of a Spack package repository")
 | 
			
		||||
        'namespace_or_path',
 | 
			
		||||
        help="namespace or path of a Spack package repository")
 | 
			
		||||
    remove_parser.add_argument(
 | 
			
		||||
        '--scope', choices=scopes, metavar=scopes_metavar,
 | 
			
		||||
        default=spack.config.default_modify_scope(),
 | 
			
		||||
@@ -101,10 +101,10 @@ def repo_add(args):
 | 
			
		||||
def repo_remove(args):
 | 
			
		||||
    """Remove a repository from Spack's configuration."""
 | 
			
		||||
    repos = spack.config.get('repos', scope=args.scope)
 | 
			
		||||
    path_or_namespace = args.path_or_namespace
 | 
			
		||||
    namespace_or_path = args.namespace_or_path
 | 
			
		||||
 | 
			
		||||
    # If the argument is a path, remove that repository from config.
 | 
			
		||||
    canon_path = canonicalize_path(path_or_namespace)
 | 
			
		||||
    canon_path = canonicalize_path(namespace_or_path)
 | 
			
		||||
    for repo_path in repos:
 | 
			
		||||
        repo_canon_path = canonicalize_path(repo_path)
 | 
			
		||||
        if canon_path == repo_canon_path:
 | 
			
		||||
@@ -117,7 +117,7 @@ def repo_remove(args):
 | 
			
		||||
    for path in repos:
 | 
			
		||||
        try:
 | 
			
		||||
            repo = Repo(path)
 | 
			
		||||
            if repo.namespace == path_or_namespace:
 | 
			
		||||
            if repo.namespace == namespace_or_path:
 | 
			
		||||
                repos.remove(path)
 | 
			
		||||
                spack.config.set('repos', repos, args.scope)
 | 
			
		||||
                tty.msg("Removed repository %s with namespace '%s'."
 | 
			
		||||
@@ -127,7 +127,7 @@ def repo_remove(args):
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
    tty.die("No repository with path or namespace: %s"
 | 
			
		||||
            % path_or_namespace)
 | 
			
		||||
            % namespace_or_path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def repo_list(args):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,10 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.repo
 | 
			
		||||
 | 
			
		||||
description = "revert checked out package source code"
 | 
			
		||||
@@ -16,15 +15,14 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument('packages', nargs=argparse.REMAINDER,
 | 
			
		||||
                           help="specs of packages to restage")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def restage(parser, args):
 | 
			
		||||
    if not args.packages:
 | 
			
		||||
    if not args.specs:
 | 
			
		||||
        tty.die("spack restage requires at least one package spec.")
 | 
			
		||||
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.packages, concretize=True)
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.specs, concretize=True)
 | 
			
		||||
    for spec in specs:
 | 
			
		||||
        package = spack.repo.get(spec)
 | 
			
		||||
        package.do_restage()
 | 
			
		||||
 
 | 
			
		||||
@@ -30,13 +30,10 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-i', '--ignore-dependencies', action='store_true', dest='ignore_deps',
 | 
			
		||||
        help="do not try to install dependencies of requested packages")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['no_checksum'])
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['no_checksum', 'spec'])
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-v', '--verbose', action='store_true', dest='verbose',
 | 
			
		||||
        help="display verbose build output while installing")
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs to use for install. must contain package AND version")
 | 
			
		||||
 | 
			
		||||
    cd_group = subparser.add_mutually_exclusive_group()
 | 
			
		||||
    arguments.add_common_arguments(cd_group, ['clean', 'dirty'])
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import contextlib
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
@@ -47,8 +46,7 @@ def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-t', '--types', action='store_true', default=False,
 | 
			
		||||
        help='show dependency types')
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER, help="specs of packages")
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['specs'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextlib.contextmanager
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.environment as ev
 | 
			
		||||
@@ -18,14 +16,11 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['no_checksum'])
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['no_checksum', 'specs'])
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-p', '--path', dest='path',
 | 
			
		||||
        help="path to stage package, does not add to spack tree")
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'specs', nargs=argparse.REMAINDER, help="specs of packages to stage")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def stage(parser, args):
 | 
			
		||||
    if not args.specs:
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
@@ -38,17 +37,13 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_common_arguments(subparser):
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-f', '--force', action='store_true', dest='force',
 | 
			
		||||
        help="remove regardless of whether other packages or environments "
 | 
			
		||||
        "depend on this one")
 | 
			
		||||
    arguments.add_common_arguments(
 | 
			
		||||
        subparser, ['recurse_dependents', 'yes_to_all'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    add_common_arguments(subparser)
 | 
			
		||||
        subparser, ['recurse_dependents', 'yes_to_all', 'installed_specs'])
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        '-a', '--all', action='store_true', dest='all',
 | 
			
		||||
        help="USE CAREFULLY. Remove ALL installed packages that match each "
 | 
			
		||||
@@ -58,11 +53,6 @@ def setup_parser(subparser):
 | 
			
		||||
        "If used in an environment, all packages in the environment "
 | 
			
		||||
        "will be uninstalled.")
 | 
			
		||||
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'packages',
 | 
			
		||||
        nargs=argparse.REMAINDER,
 | 
			
		||||
        help="specs of packages to uninstall")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_matching_specs(env, specs, allow_multiple_matches=False, force=False):
 | 
			
		||||
    """Returns a list of specs matching the not necessarily
 | 
			
		||||
@@ -351,10 +341,10 @@ def confirm_removal(specs):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def uninstall(parser, args):
 | 
			
		||||
    if not args.packages and not args.all:
 | 
			
		||||
    if not args.specs and not args.all:
 | 
			
		||||
        tty.die('uninstall requires at least one package argument.',
 | 
			
		||||
                '  Use `spack uninstall --all` to uninstall ALL packages.')
 | 
			
		||||
 | 
			
		||||
    # [any] here handles the --all case by forcing all specs to be returned
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.packages) if args.packages else [any]
 | 
			
		||||
    specs = spack.cmd.parse_specs(args.specs) if args.specs else [any]
 | 
			
		||||
    uninstall_specs(args, specs)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,7 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
from spack.cmd.common import print_module_placeholder_help
 | 
			
		||||
from spack.cmd.common import print_module_placeholder_help, arguments
 | 
			
		||||
 | 
			
		||||
description = "remove package from environment using `module unload`"
 | 
			
		||||
section = "modules"
 | 
			
		||||
@@ -14,9 +13,7 @@
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    """Parser is only constructed so that this prints a nice help
 | 
			
		||||
       message with -h. """
 | 
			
		||||
    subparser.add_argument(
 | 
			
		||||
        'spec', nargs=argparse.REMAINDER,
 | 
			
		||||
        help='spec of package to unload with modules')
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['installed_spec'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def unload(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@ def setup_parser(subparser):
 | 
			
		||||
                           help="Ouptut json-formatted errors")
 | 
			
		||||
    subparser.add_argument('-a', '--all', action='store_true',
 | 
			
		||||
                           help="Verify all packages")
 | 
			
		||||
    subparser.add_argument('files_or_specs', nargs=argparse.REMAINDER,
 | 
			
		||||
                           help="Files or specs to verify")
 | 
			
		||||
    subparser.add_argument('specs_or_files', nargs=argparse.REMAINDER,
 | 
			
		||||
                           help="Specs or files to verify")
 | 
			
		||||
 | 
			
		||||
    type = subparser.add_mutually_exclusive_group()
 | 
			
		||||
    type.add_argument(
 | 
			
		||||
@@ -47,7 +47,7 @@ def verify(parser, args):
 | 
			
		||||
            setup_parser.parser.print_help()
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
        for file in args.files_or_specs:
 | 
			
		||||
        for file in args.specs_or_files:
 | 
			
		||||
            results = spack.verify.check_file_manifest(file)
 | 
			
		||||
            if results.has_errors():
 | 
			
		||||
                if args.json:
 | 
			
		||||
@@ -57,21 +57,21 @@ def verify(parser, args):
 | 
			
		||||
 | 
			
		||||
        return 0
 | 
			
		||||
    else:
 | 
			
		||||
        spec_args = spack.cmd.parse_specs(args.files_or_specs)
 | 
			
		||||
        spec_args = spack.cmd.parse_specs(args.specs_or_files)
 | 
			
		||||
 | 
			
		||||
    if args.all:
 | 
			
		||||
        query = spack.store.db.query_local if local else spack.store.db.query
 | 
			
		||||
 | 
			
		||||
        # construct spec list
 | 
			
		||||
        if spec_args:
 | 
			
		||||
            spec_list = spack.cmd.parse_specs(args.files_or_specs)
 | 
			
		||||
            spec_list = spack.cmd.parse_specs(args.specs_or_files)
 | 
			
		||||
            specs = []
 | 
			
		||||
            for spec in spec_list:
 | 
			
		||||
                specs += query(spec, installed=True)
 | 
			
		||||
        else:
 | 
			
		||||
            specs = query(installed=True)
 | 
			
		||||
 | 
			
		||||
    elif args.files_or_specs:
 | 
			
		||||
    elif args.specs_or_files:
 | 
			
		||||
        # construct disambiguated spec list
 | 
			
		||||
        env = ev.get_env(args, 'verify')
 | 
			
		||||
        specs = list(map(lambda x: spack.cmd.disambiguate_spec(x, env,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,13 @@
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from llnl.util.tty.colify import colify
 | 
			
		||||
import llnl.util.tty as tty
 | 
			
		||||
 | 
			
		||||
import spack.cmd.common.arguments as arguments
 | 
			
		||||
import spack.repo
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
description = "list available versions of a package"
 | 
			
		||||
section = "packaging"
 | 
			
		||||
@@ -17,10 +19,9 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_parser(subparser):
 | 
			
		||||
    subparser.add_argument('package', metavar='PACKAGE',
 | 
			
		||||
                           help='package to list versions for')
 | 
			
		||||
    subparser.add_argument('-s', '--safe-only', action='store_true',
 | 
			
		||||
                           help='only list safe versions of the package')
 | 
			
		||||
    arguments.add_common_arguments(subparser, ['package'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def versions(parser, args):
 | 
			
		||||
 
 | 
			
		||||
@@ -72,8 +72,8 @@ def __init__(self, args):
 | 
			
		||||
            tty.verbose("Using CDash auth token from environment")
 | 
			
		||||
            self.authtoken = os.environ.get('SPACK_CDASH_AUTH_TOKEN')
 | 
			
		||||
 | 
			
		||||
        if args.package:
 | 
			
		||||
            packages = args.package
 | 
			
		||||
        if args.spec:
 | 
			
		||||
            packages = args.spec
 | 
			
		||||
        else:
 | 
			
		||||
            packages = []
 | 
			
		||||
            for file in args.specfiles:
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,17 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
import filecmp
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
from llnl.util.argparsewriter import ArgparseWriter
 | 
			
		||||
 | 
			
		||||
import spack.cmd
 | 
			
		||||
from spack.cmd.commands import _positional_to_subroutine
 | 
			
		||||
import spack.main
 | 
			
		||||
import spack.paths
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
commands = spack.main.SpackCommand('commands')
 | 
			
		||||
 | 
			
		||||
@@ -17,38 +21,64 @@
 | 
			
		||||
spack.main.add_all_commands(parser)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_commands_by_name():
 | 
			
		||||
def test_names():
 | 
			
		||||
    """Test default output of spack commands."""
 | 
			
		||||
    out = commands()
 | 
			
		||||
    assert out.strip().split('\n') == sorted(spack.cmd.all_commands())
 | 
			
		||||
    out1 = commands().strip().split('\n')
 | 
			
		||||
    assert out1 == spack.cmd.all_commands()
 | 
			
		||||
    assert 'rm' not in out1
 | 
			
		||||
 | 
			
		||||
    out2 = commands('--aliases').strip().split('\n')
 | 
			
		||||
    assert out1 != out2
 | 
			
		||||
    assert 'rm' in out2
 | 
			
		||||
 | 
			
		||||
    out3 = commands('--format=names').strip().split('\n')
 | 
			
		||||
    assert out1 == out3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_subcommands():
 | 
			
		||||
    """Test subcommand traversal."""
 | 
			
		||||
    out = commands('--format=subcommands')
 | 
			
		||||
    assert 'spack mirror create' in out
 | 
			
		||||
    assert 'spack buildcache list' in out
 | 
			
		||||
    assert 'spack repo add' in out
 | 
			
		||||
    assert 'spack pkg diff' in out
 | 
			
		||||
    assert 'spack url parse' in out
 | 
			
		||||
    assert 'spack view symlink' in out
 | 
			
		||||
    out1 = commands('--format=subcommands')
 | 
			
		||||
    assert 'spack mirror create' in out1
 | 
			
		||||
    assert 'spack buildcache list' in out1
 | 
			
		||||
    assert 'spack repo add' in out1
 | 
			
		||||
    assert 'spack pkg diff' in out1
 | 
			
		||||
    assert 'spack url parse' in out1
 | 
			
		||||
    assert 'spack view symlink' in out1
 | 
			
		||||
    assert 'spack rm' not in out1
 | 
			
		||||
    assert 'spack compiler add' not in out1
 | 
			
		||||
 | 
			
		||||
    class Subcommands(ArgparseWriter):
 | 
			
		||||
        def begin_command(self, prog):
 | 
			
		||||
            assert prog in out
 | 
			
		||||
 | 
			
		||||
    Subcommands().write(parser)
 | 
			
		||||
    out2 = commands('--aliases', '--format=subcommands')
 | 
			
		||||
    assert 'spack mirror create' in out2
 | 
			
		||||
    assert 'spack buildcache list' in out2
 | 
			
		||||
    assert 'spack repo add' in out2
 | 
			
		||||
    assert 'spack pkg diff' in out2
 | 
			
		||||
    assert 'spack url parse' in out2
 | 
			
		||||
    assert 'spack view symlink' in out2
 | 
			
		||||
    assert 'spack rm' in out2
 | 
			
		||||
    assert 'spack compiler add' in out2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_rst():
 | 
			
		||||
    """Do some simple sanity checks of the rst writer."""
 | 
			
		||||
    out = commands('--format=rst')
 | 
			
		||||
    out1 = commands('--format=rst')
 | 
			
		||||
    assert 'spack mirror create' in out1
 | 
			
		||||
    assert 'spack buildcache list' in out1
 | 
			
		||||
    assert 'spack repo add' in out1
 | 
			
		||||
    assert 'spack pkg diff' in out1
 | 
			
		||||
    assert 'spack url parse' in out1
 | 
			
		||||
    assert 'spack view symlink' in out1
 | 
			
		||||
    assert 'spack rm' not in out1
 | 
			
		||||
    assert 'spack compiler add' not in out1
 | 
			
		||||
 | 
			
		||||
    class Subcommands(ArgparseWriter):
 | 
			
		||||
        def begin_command(self, prog):
 | 
			
		||||
            assert prog in out
 | 
			
		||||
            assert re.sub(r' ', '-', prog) in out
 | 
			
		||||
    Subcommands().write(parser)
 | 
			
		||||
    out2 = commands('--aliases', '--format=rst')
 | 
			
		||||
    assert 'spack mirror create' in out2
 | 
			
		||||
    assert 'spack buildcache list' in out2
 | 
			
		||||
    assert 'spack repo add' in out2
 | 
			
		||||
    assert 'spack pkg diff' in out2
 | 
			
		||||
    assert 'spack url parse' in out2
 | 
			
		||||
    assert 'spack view symlink' in out2
 | 
			
		||||
    assert 'spack rm' in out2
 | 
			
		||||
    assert 'spack compiler add' in out2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_rst_with_input_files(tmpdir):
 | 
			
		||||
@@ -109,3 +139,91 @@ def test_rst_update(tmpdir):
 | 
			
		||||
    assert update_file.exists()
 | 
			
		||||
    with update_file.open() as f:
 | 
			
		||||
        assert f.read() == 'empty\n'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_update_with_header(tmpdir):
 | 
			
		||||
    update_file = tmpdir.join('output')
 | 
			
		||||
 | 
			
		||||
    # not yet created when commands is run
 | 
			
		||||
    commands('--update', str(update_file))
 | 
			
		||||
    assert update_file.exists()
 | 
			
		||||
    with update_file.open() as f:
 | 
			
		||||
        assert f.read()
 | 
			
		||||
    fake_header = 'this is a header!\n\n'
 | 
			
		||||
 | 
			
		||||
    filename = tmpdir.join('header.txt')
 | 
			
		||||
    with filename.open('w') as f:
 | 
			
		||||
        f.write(fake_header)
 | 
			
		||||
 | 
			
		||||
    # created, newer than commands, but older than header
 | 
			
		||||
    commands('--update', str(update_file), '--header', str(filename))
 | 
			
		||||
 | 
			
		||||
    # newer than commands and header
 | 
			
		||||
    commands('--update', str(update_file), '--header', str(filename))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.xfail
 | 
			
		||||
def test_no_pipe_error():
 | 
			
		||||
    """Make sure we don't see any pipe errors when piping output."""
 | 
			
		||||
 | 
			
		||||
    proc = subprocess.Popen(
 | 
			
		||||
        ['spack', 'commands', '--format=rst'],
 | 
			
		||||
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 | 
			
		||||
 | 
			
		||||
    # Call close() on stdout to cause a broken pipe
 | 
			
		||||
    proc.stdout.close()
 | 
			
		||||
    proc.wait()
 | 
			
		||||
    stderr = proc.stderr.read().decode('utf-8')
 | 
			
		||||
 | 
			
		||||
    assert 'Broken pipe' not in stderr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_bash_completion():
 | 
			
		||||
    """Test the bash completion writer."""
 | 
			
		||||
    out1 = commands('--format=bash')
 | 
			
		||||
 | 
			
		||||
    # Make sure header not included
 | 
			
		||||
    assert '_bash_completion_spack() {' not in out1
 | 
			
		||||
    assert '_all_packages() {' not in out1
 | 
			
		||||
 | 
			
		||||
    # Make sure subcommands appear
 | 
			
		||||
    assert '_spack_remove() {' in out1
 | 
			
		||||
    assert '_spack_compiler_find() {' in out1
 | 
			
		||||
 | 
			
		||||
    # Make sure aliases don't appear
 | 
			
		||||
    assert '_spack_rm() {' not in out1
 | 
			
		||||
    assert '_spack_compiler_add() {' not in out1
 | 
			
		||||
 | 
			
		||||
    # Make sure options appear
 | 
			
		||||
    assert '-h --help' in out1
 | 
			
		||||
 | 
			
		||||
    # Make sure subcommands are called
 | 
			
		||||
    for function in _positional_to_subroutine.values():
 | 
			
		||||
        assert function in out1
 | 
			
		||||
 | 
			
		||||
    out2 = commands('--aliases', '--format=bash')
 | 
			
		||||
 | 
			
		||||
    # Make sure aliases appear
 | 
			
		||||
    assert '_spack_rm() {' in out2
 | 
			
		||||
    assert '_spack_compiler_add() {' in out2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_updated_completion_scripts(tmpdir):
 | 
			
		||||
    """Make sure our shell tab completion scripts remain up-to-date."""
 | 
			
		||||
 | 
			
		||||
    msg = ("It looks like Spack's command-line interface has been modified. "
 | 
			
		||||
           "Please update Spack's shell tab completion scripts by running:\n\n"
 | 
			
		||||
           "    share/spack/qa/update-completion-scripts.sh\n\n"
 | 
			
		||||
           "and adding the changed files to your pull request.")
 | 
			
		||||
 | 
			
		||||
    for shell in ['bash']:  # 'zsh', 'fish']:
 | 
			
		||||
        header = os.path.join(
 | 
			
		||||
            spack.paths.share_path, shell, 'spack-completion.in')
 | 
			
		||||
        script = 'spack-completion.{0}'.format(shell)
 | 
			
		||||
        old_script = os.path.join(spack.paths.share_path, script)
 | 
			
		||||
        new_script = str(tmpdir.join(script))
 | 
			
		||||
 | 
			
		||||
        commands('--aliases', '--format', shell,
 | 
			
		||||
                 '--header', header, '--update', new_script)
 | 
			
		||||
 | 
			
		||||
        assert filecmp.cmp(old_script, new_script), msg
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								lib/spack/spack/test/llnl/util/argparsewriter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lib/spack/spack/test/llnl/util/argparsewriter.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
# Copyright 2013-2020 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)
 | 
			
		||||
 | 
			
		||||
"""Tests for ``llnl/util/argparsewriter.py``
 | 
			
		||||
 | 
			
		||||
These tests are fairly minimal, and ArgparseWriter is more extensively
 | 
			
		||||
tested in ``cmd/commands.py``.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
import llnl.util.argparsewriter as aw
 | 
			
		||||
 | 
			
		||||
import spack.main
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
parser = spack.main.make_argument_parser()
 | 
			
		||||
spack.main.add_all_commands(parser)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_format_not_overridden():
 | 
			
		||||
    writer = aw.ArgparseWriter('spack')
 | 
			
		||||
 | 
			
		||||
    with pytest.raises(NotImplementedError):
 | 
			
		||||
        writer.write(parser)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_completion_format_not_overridden():
 | 
			
		||||
    writer = aw.ArgparseCompletionWriter('spack')
 | 
			
		||||
 | 
			
		||||
    assert writer.positionals([]) == ''
 | 
			
		||||
    assert writer.optionals([]) == ''
 | 
			
		||||
    assert writer.subcommands([]) == ''
 | 
			
		||||
 | 
			
		||||
    writer.write(parser)
 | 
			
		||||
		Reference in New Issue
	
	Block a user