forked from mirrors/gecko-dev
		
	 72383f27e2
			
		
	
	
		72383f27e2
		
	
	
	
	
		
			
			terminal.py had an ambiguous |import logging| that was importing mach.logging from Sphinx. We fix it. There was also a poorly formed link in the mach commands documentation. We fix it. --HG-- extra : rebase_source : 12783c69027989ac031d29e4ecbc1ee2f465ffa4 extra : histedit_source : 4283c6cdecc4de8aa7636d0c4cc566daf5142b50
		
			
				
	
	
		
			145 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. _mach_commands:
 | |
| 
 | |
| =====================
 | |
| Implementing Commands
 | |
| =====================
 | |
| 
 | |
| Mach commands are defined via Python decorators.
 | |
| 
 | |
| All the relevant decorators are defined in the *mach.decorators* module.
 | |
| The important decorators are as follows:
 | |
| 
 | |
| :py:func:`CommandProvider <mach.decorators.CommandProvider>`
 | |
|   A class decorator that denotes that a class contains mach
 | |
|   commands. The decorator takes no arguments.
 | |
| 
 | |
| :py:func:`Command <mach.decorators.Command>`
 | |
|   A method decorator that denotes that the method should be called when
 | |
|   the specified command is requested. The decorator takes a command name
 | |
|   as its first argument and a number of additional arguments to
 | |
|   configure the behavior of the command.
 | |
| 
 | |
| :py:func:`CommandArgument <mach.decorators.CommandArgument>`
 | |
|   A method decorator that defines an argument to the command. Its
 | |
|   arguments are essentially proxied to ArgumentParser.add_argument()
 | |
| 
 | |
| :py:func:`SubCommand <mach.decorators.SubCommand>`
 | |
|   A method decorator that denotes that the method should be a
 | |
|   sub-command to an existing ``@Command``. The decorator takes the
 | |
|   parent command name as its first argument and the sub-command name
 | |
|   as its second argument.
 | |
| 
 | |
|   ``@CommandArgument`` can be used on ``@SubCommand`` instances just
 | |
|   like they can on ``@Command`` instances.
 | |
| 
 | |
| Classes with the ``@CommandProvider`` decorator **must** have an
 | |
| ``__init__`` method that accepts 1 or 2 arguments. If it accepts 2
 | |
| arguments, the 2nd argument will be a
 | |
| :py:class:`mach.base.CommandContext` instance.
 | |
| 
 | |
| Here is a complete example:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|    from mach.decorators import (
 | |
|        CommandArgument,
 | |
|        CommandProvider,
 | |
|        Command,
 | |
|    )
 | |
| 
 | |
|    @CommandProvider
 | |
|    class MyClass(object):
 | |
|        @Command('doit', help='Do ALL OF THE THINGS.')
 | |
|        @CommandArgument('--force', '-f', action='store_true',
 | |
|            help='Force doing it.')
 | |
|        def doit(self, force=False):
 | |
|            # Do stuff here.
 | |
| 
 | |
| When the module is loaded, the decorators tell mach about all handlers.
 | |
| When mach runs, it takes the assembled metadata from these handlers and
 | |
| hooks it up to the command line driver. Under the hood, arguments passed
 | |
| to the decorators are being used to help mach parse command arguments,
 | |
| formulate arguments to the methods, etc. See the documentation in the
 | |
| :py:mod:`mach.base` module for more.
 | |
| 
 | |
| The Python modules defining mach commands do not need to live inside the
 | |
| main mach source tree.
 | |
| 
 | |
| Conditionally Filtering Commands
 | |
| ================================
 | |
| 
 | |
| Sometimes it might only make sense to run a command given a certain
 | |
| context. For example, running tests only makes sense if the product
 | |
| they are testing has been built, and said build is available. To make
 | |
| sure a command is only runnable from within a correct context, you can
 | |
| define a series of conditions on the
 | |
| :py:func:`Command <mach.decorators.Command>` decorator.
 | |
| 
 | |
| A condition is simply a function that takes an instance of the
 | |
| :py:func:`mach.decorators.CommandProvider` class as an argument, and
 | |
| returns ``True`` or ``False``. If any of the conditions defined on a
 | |
| command return ``False``, the command will not be runnable. The
 | |
| docstring of a condition function is used in error messages, to explain
 | |
| why the command cannot currently be run.
 | |
| 
 | |
| Here is an example:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|    from mach.decorators import (
 | |
|        CommandProvider,
 | |
|        Command,
 | |
|    )
 | |
| 
 | |
|    def build_available(cls):
 | |
|        """The build needs to be available."""
 | |
|        return cls.build_path is not None
 | |
| 
 | |
|     @CommandProvider
 | |
|    class MyClass(MachCommandBase):
 | |
|        def __init__(self, build_path=None):
 | |
|            self.build_path = build_path
 | |
| 
 | |
|        @Command('run_tests', conditions=[build_available])
 | |
|        def run_tests(self):
 | |
|            # Do stuff here.
 | |
| 
 | |
| It is important to make sure that any state needed by the condition is
 | |
| available to instances of the command provider.
 | |
| 
 | |
| By default all commands without any conditions applied will be runnable,
 | |
| but it is possible to change this behaviour by setting
 | |
| ``require_conditions`` to ``True``:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|    m = mach.main.Mach()
 | |
|    m.require_conditions = True
 | |
| 
 | |
| Minimizing Code in Commands
 | |
| ===========================
 | |
| 
 | |
| Mach command modules, classes, and methods work best when they are
 | |
| minimal dispatchers. The reason is import bloat. Currently, the mach
 | |
| core needs to import every Python file potentially containing mach
 | |
| commands for every command invocation. If you have dozens of commands or
 | |
| commands in modules that import a lot of Python code, these imports
 | |
| could slow mach down and waste memory.
 | |
| 
 | |
| It is thus recommended that mach modules, classes, and methods do as
 | |
| little work as possible. Ideally the module should only import from
 | |
| the :py:mod:`mach` package. If you need external modules, you should
 | |
| import them from within the command method.
 | |
| 
 | |
| To keep code size small, the body of a command method should be limited
 | |
| to:
 | |
| 
 | |
| 1. Obtaining user input (parsing arguments, prompting, etc)
 | |
| 2. Calling into some other Python package
 | |
| 3. Formatting output
 | |
| 
 | |
| Of course, these recommendations can be ignored if you want to risk
 | |
| slower performance.
 | |
| 
 | |
| In the future, the mach driver may cache the dispatching information or
 | |
| have it intelligently loaded to facilitate lazy loading.
 |