servo: Merge #4080 - Update mach to latest changes from mozilla-central (from mttr:mach_update); r=jdm

Updates the way mach mixes unrecognized arguments and predefined arguments (see [mozilla bug 1076649](https://bugzilla.mozilla.org/show_bug.cgi?id=1076649) for details on this change), and also adds support for argument groupings (see [1077272](https://bugzilla.mozilla.org/show_bug.cgi?id=1077272)).

I was running into some annoyances when trying to implement a change that would allow a `--debug` flag to be used in `./mach run` for running Servo through a debugger (basically, the same behavior described under "How do I run Mozilla under gdb?" [here](https://developer.mozilla.org/en-US/docs/Debugging_Mozilla_with_gdb)). This change should make those annoyances go away, and as far as I can tell, it doesn't have an impact on the way any of the existing commands are used.

Source-Repo: https://github.com/servo/servo
Source-Revision: 4a257bc27152bef98068cfdc1433549aab05c5de
This commit is contained in:
Matthew Rasmus 2014-11-23 22:48:32 -07:00
Родитель a60d53dbbf
Коммит 361a2f04e2
6 изменённых файлов: 119 добавлений и 45 удалений

Просмотреть файл

@ -77,9 +77,6 @@ class MethodHandler(object):
# Description of the purpose of this command.
'description',
# Whether to allow all arguments from the parser.
'allow_all_arguments',
# Functions used to 'skip' commands if they don't meet the conditions
# in a given context.
'conditions',
@ -91,20 +88,23 @@ class MethodHandler(object):
# Arguments added to this command's parser. This is a 2-tuple of
# positional and named arguments, respectively.
'arguments',
# Argument groups added to this command's parser.
'argument_group_names',
)
def __init__(self, cls, method, name, category=None, description=None,
allow_all_arguments=False, conditions=None, parser=None, arguments=None,
pass_context=False):
conditions=None, parser=None, arguments=None,
argument_group_names=None, pass_context=False):
self.cls = cls
self.method = method
self.name = name
self.category = category
self.description = description
self.allow_all_arguments = allow_all_arguments
self.conditions = conditions or []
self.parser = parser
self.arguments = arguments or []
self.argument_group_names = argument_group_names or []
self.pass_context = pass_context

Просмотреть файл

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import argparse
import collections
import inspect
import types
@ -56,8 +57,8 @@ def CommandProvider(cls):
if not isinstance(value, types.FunctionType):
continue
command_name, category, description, allow_all, conditions, parser = getattr(
value, '_mach_command', (None, None, None, None, None, None))
command_name, category, description, conditions, parser = getattr(
value, '_mach_command', (None, None, None, None, None))
if command_name is None:
continue
@ -81,9 +82,11 @@ def CommandProvider(cls):
arguments = getattr(value, '_mach_command_args', None)
argument_group_names = getattr(value, '_mach_command_arg_group_names', None)
handler = MethodHandler(cls, attr, command_name, category=category,
description=description, allow_all_arguments=allow_all,
conditions=conditions, parser=parser, arguments=arguments,
description=description, conditions=conditions, parser=parser,
arguments=arguments, argument_group_names=argument_group_names,
pass_context=pass_context)
Registrar.register_command_handler(handler)
@ -102,9 +105,6 @@ class Command(object):
description -- A brief description of what the command does.
allow_all_args -- Bool indicating whether to allow unknown arguments
through to the command.
parser -- an optional argparse.ArgumentParser instance to use as
the basis for the command arguments.
@ -114,18 +114,17 @@ class Command(object):
def foo(self):
pass
"""
def __init__(self, name, category=None, description=None,
allow_all_args=False, conditions=None, parser=None):
def __init__(self, name, category=None, description=None, conditions=None,
parser=None):
self._name = name
self._category = category
self._description = description
self._allow_all_args = allow_all_args
self._conditions = conditions
self._parser = parser
def __call__(self, func):
func._mach_command = (self._name, self._category, self._description,
self._allow_all_args, self._conditions, self._parser)
self._conditions, self._parser)
return func
@ -145,6 +144,11 @@ class CommandArgument(object):
pass
"""
def __init__(self, *args, **kwargs):
if kwargs.get('nargs') == argparse.REMAINDER:
# These are the assertions we make in dispatcher.py about
# those types of CommandArguments.
assert len(args) == 1
assert all(k in ('default', 'nargs', 'help', 'group') for k in kwargs)
self._command_args = (args, kwargs)
def __call__(self, func):
@ -157,6 +161,39 @@ class CommandArgument(object):
return func
class CommandArgumentGroup(object):
"""Decorator for additional argument groups to mach subcommands.
This decorator should be used to add arguments groups to mach commands.
Arguments to the decorator are proxied to
ArgumentParser.add_argument_group().
For example:
@Command('foo', helps='Run the foo action')
@CommandArgumentGroup('group1')
@CommandArgument('-b', '--bar', group='group1', action='store_true',
default=False, help='Enable bar mode.')
def foo(self):
pass
The name should be chosen so that it makes sense as part of the phrase
'Command Arguments for <name>' because that's how it will be shown in the
help message.
"""
def __init__(self, group_name):
self._group_name = group_name
def __call__(self, func):
command_arg_group_names = getattr(func, '_mach_command_arg_group_names', [])
command_arg_group_names.insert(0, self._group_name)
func._mach_command_arg_group_names = command_arg_group_names
return func
def SettingsProvider(cls):
"""Class decorator to denote that this class provides Mach settings.

Просмотреть файл

@ -135,19 +135,32 @@ class CommandAction(argparse.Action):
parser_args = {
'add_help': False,
'usage': '%(prog)s [global arguments] ' + command +
' command arguments]',
' [command arguments]',
}
if handler.allow_all_arguments:
parser_args['prefix_chars'] = '+'
if handler.parser:
subparser = handler.parser
else:
subparser = argparse.ArgumentParser(**parser_args)
remainder = None
for arg in handler.arguments:
subparser.add_argument(*arg[0], **arg[1])
# Remove our group keyword; it's not needed here.
group_name = arg[1].get('group')
if group_name:
del arg[1]['group']
if arg[1].get('nargs') == argparse.REMAINDER:
# parse_known_args expects all argparse.REMAINDER ('...')
# arguments to be all stuck together. Instead, we want them to
# pick any extra argument, wherever they are.
# Assume a limited CommandArgument for those arguments.
assert len(arg[0]) == 1
assert all(k in ('default', 'nargs', 'help') for k in arg[1])
remainder = arg
else:
subparser.add_argument(*arg[0], **arg[1])
# We define the command information on the main parser result so as to
# not interfere with arguments passed to the command.
@ -156,7 +169,32 @@ class CommandAction(argparse.Action):
command_namespace, extra = subparser.parse_known_args(args)
setattr(namespace, 'command_args', command_namespace)
if extra:
if remainder:
(name,), options = remainder
# parse_known_args usefully puts all arguments after '--' in
# extra, but also puts '--' there. We don't want to pass it down
# to the command handler. Note that if multiple '--' are on the
# command line, only the first one is removed, so that subsequent
# ones are passed down.
if '--' in extra:
extra.remove('--')
# Commands with argparse.REMAINDER arguments used to force the
# other arguments to be '+' prefixed. If a user now passes such
# an argument, if will silently end up in extra. So, check if any
# of the allowed arguments appear in a '+' prefixed form, and error
# out if that's the case.
for args, _ in handler.arguments:
for arg in args:
arg = arg.replace('-', '+', 1)
if arg in extra:
raise UnrecognizedArgumentError(command, [arg])
if extra:
setattr(command_namespace, name, extra)
else:
setattr(command_namespace, name, options.get('default', []))
elif extra:
raise UnrecognizedArgumentError(command, extra)
def _handle_main_help(self, parser, verbose):
@ -234,9 +272,6 @@ class CommandAction(argparse.Action):
'add_help': False,
}
if handler.allow_all_arguments:
parser_args['prefix_chars'] = '+'
if handler.parser:
c_parser = handler.parser
c_parser.formatter_class = NoUsageFormatter
@ -258,7 +293,18 @@ class CommandAction(argparse.Action):
c_parser = argparse.ArgumentParser(**parser_args)
group = c_parser.add_argument_group('Command Arguments')
extra_groups = {}
for group_name in handler.argument_group_names:
group_full_name = 'Command Arguments for ' + group_name
extra_groups[group_name] = \
c_parser.add_argument_group(group_full_name)
for arg in handler.arguments:
# Apply our group keyword.
group_name = arg[1].get('group')
if group_name:
del arg[1]['group']
group = extra_groups[group_name]
group.add_argument(*arg[0], **arg[1])
# This will print the description of the command below the usage.

Просмотреть файл

@ -16,8 +16,7 @@ from servo.command_base import CommandBase, cd
class MachCommands(CommandBase):
@Command('cargo',
description='Run Cargo',
category='devenv',
allow_all_args=True)
category='devenv')
@CommandArgument(
'params', default=None, nargs='...',
help="Command-line arguments to be passed through to Cargo")
@ -27,8 +26,7 @@ class MachCommands(CommandBase):
@Command('update-cargo',
description='Update Cargo dependencies',
category='devenv',
allow_all_args=True)
category='devenv')
@CommandArgument(
'params', default=None, nargs='...',
help='Command-line arguments to be passed through to cargo update')
@ -45,8 +43,7 @@ class MachCommands(CommandBase):
@Command('rustc',
description='Run the Rust compiler',
category='devenv',
allow_all_args=True)
category='devenv')
@CommandArgument(
'params', default=None, nargs='...',
help="Command-line arguments to be passed through to rustc")

Просмотреть файл

@ -20,10 +20,9 @@ from servo.command_base import CommandBase
class MachCommands(CommandBase):
@Command('run',
description='Run Servo',
category='post-build',
allow_all_args=True)
category='post-build')
@CommandArgument(
'params', default=None, nargs='...',
'params', nargs='...',
help="Command-line arguments to be passed through to Servo")
def run(self, params):
env = self.build_env()
@ -33,8 +32,7 @@ class MachCommands(CommandBase):
@Command('doc',
description='Generate documentation',
category='post-build',
allow_all_args=True)
category='post-build')
@CommandArgument(
'params', default=None, nargs='...',
help="Command-line arguments to be passed through to cargo doc")
@ -45,8 +43,7 @@ class MachCommands(CommandBase):
@Command('serve-docs',
description='Locally serve Servo and Rust documentation',
category='post-build',
allow_all_args=True)
category='post-build')
@CommandArgument(
'port', default=8888, nargs='?', type=int, metavar='PORT',
help="Port to serve documentation at (default is 8888)")

Просмотреть файл

@ -55,8 +55,7 @@ class MachCommands(CommandBase):
@Command('test-unit',
description='Run unit tests',
category='testing',
allow_all_args=True)
category='testing')
@CommandArgument('test_name', default=None, nargs="...",
help="Only run tests that match this pattern")
def test_unit(self, test_name=None):
@ -113,8 +112,7 @@ class MachCommands(CommandBase):
@Command('test-content',
description='Run the content tests',
category='testing',
allow_all_args=True)
category='testing')
@CommandArgument('test_name', default=None, nargs="?",
help="Only run tests that match this pattern")
def test_content(self, test_name=None):
@ -142,8 +140,7 @@ class MachCommands(CommandBase):
@Command('test-wpt',
description='Run the web platform tests',
category='testing',
allow_all_args=True)
category='testing')
@CommandArgument(
'params', default=None, nargs='...',
help="Command-line arguments to be passed through to wpt/run.sh")