|  | .. _west-extensions: | 
|  |  | 
|  | Extensions | 
|  | ########## | 
|  |  | 
|  | West is "pluggable": you can add your own commands to west without editing its | 
|  | source code. These are called **west extension commands**, or just "extensions" | 
|  | for short. Extensions show up in the ``west --help`` output in a special | 
|  | section for the project which defines them. This page provides general | 
|  | information on west extension commands, and has a tutorial for writing your | 
|  | own. | 
|  |  | 
|  | Some commands you can run when using west with Zephyr, like the ones used to | 
|  | :ref:`build, flash, and debug <west-build-flash-debug>` and the | 
|  | :ref:`ones described here <west-zephyr-ext-cmds>` , are extensions. That's why | 
|  | help for them shows up like this in ``west --help``: | 
|  |  | 
|  | .. code-block:: none | 
|  |  | 
|  | commands from project at "zephyr": | 
|  | completion:           display shell completion scripts | 
|  | boards:               display information about supported boards | 
|  | build:                compile a Zephyr application | 
|  | sign:                 sign a Zephyr binary for bootloader chain-loading | 
|  | flash:                flash and run a binary on a board | 
|  | debug:                flash and interactively debug a Zephyr application | 
|  | debugserver:          connect to board and launch a debug server | 
|  | attach:               interactively debug a board | 
|  |  | 
|  | See :file:`zephyr/scripts/west-commands.yml` and the | 
|  | :file:`zephyr/scripts/west_commands` directory for the implementation details. | 
|  |  | 
|  | Disabling Extension Commands | 
|  | **************************** | 
|  |  | 
|  | To disable support for extension commands, set the ``commands.allow_extensions`` | 
|  | :ref:`configuration <west-config>` option to ``false``. To set this | 
|  | globally for whenever you run west, use: | 
|  |  | 
|  | .. code-block:: console | 
|  |  | 
|  | west config --global commands.allow_extensions false | 
|  |  | 
|  | If you want to, you can then re-enable them in a particular :term:`west | 
|  | workspace` with: | 
|  |  | 
|  | .. code-block:: console | 
|  |  | 
|  | west config --local commands.allow_extensions true | 
|  |  | 
|  | Note that the files containing extension commands are not imported by west | 
|  | unless the commands are explicitly run. See below for details. | 
|  |  | 
|  | Adding a West Extension | 
|  | *********************** | 
|  |  | 
|  | There are three steps to adding your own extension: | 
|  |  | 
|  | #. Write the code implementing the command. | 
|  | #. Add information about it to a :file:`west-commands.yml` file. | 
|  | #. Make sure the :file:`west-commands.yml` file is referenced in the | 
|  | :term:`west manifest`. | 
|  |  | 
|  | Note that west ignores extension commands whose names are the same as a | 
|  | built-in command. | 
|  |  | 
|  | Step 1: Implement Your Command | 
|  | ============================== | 
|  |  | 
|  | Create a Python file to contain your command implementation (see the "Meta > | 
|  | Requires" information on the `west PyPI page`_ for details on the currently | 
|  | supported versions of Python). You can put it in anywhere in any project | 
|  | tracked by your :term:`west manifest`, or the manifest repository itself. | 
|  | This file must contain a subclass of the ``west.commands.WestCommand`` class; | 
|  | this class will be instantiated and used when your extension is run. | 
|  |  | 
|  | Here is a basic skeleton you can use to get started. It contains a subclass of | 
|  | ``WestCommand``, with implementations for all the abstract methods. For more | 
|  | details on the west APIs you can use, see :ref:`west-apis`. | 
|  |  | 
|  | .. code-block:: py | 
|  |  | 
|  | '''my_west_extension.py | 
|  |  | 
|  | Basic example of a west extension.''' | 
|  |  | 
|  | from textwrap import dedent            # just for nicer code indentation | 
|  |  | 
|  | from west.commands import WestCommand  # your extension must subclass this | 
|  |  | 
|  | class MyCommand(WestCommand): | 
|  |  | 
|  | def __init__(self): | 
|  | super().__init__( | 
|  | 'my-command-name',  # gets stored as self.name | 
|  | 'one-line help for what my-command-name does',  # self.help | 
|  | # self.description: | 
|  | dedent(''' | 
|  | A multi-line description of my-command. | 
|  |  | 
|  | You can split this up into multiple paragraphs and they'll get | 
|  | reflowed for you. You can also pass | 
|  | formatter_class=argparse.RawDescriptionHelpFormatter when calling | 
|  | parser_adder.add_parser() below if you want to keep your line | 
|  | endings.''')) | 
|  |  | 
|  | def do_add_parser(self, parser_adder): | 
|  | # This is a bit of boilerplate, which allows you full control over the | 
|  | # type of argparse handling you want. The "parser_adder" argument is | 
|  | # the return value of an argparse.ArgumentParser.add_subparsers() call. | 
|  | parser = parser_adder.add_parser(self.name, | 
|  | help=self.help, | 
|  | description=self.description) | 
|  |  | 
|  | # Add some example options using the standard argparse module API. | 
|  | parser.add_argument('-o', '--optional', help='an optional argument') | 
|  | parser.add_argument('required', help='a required argument') | 
|  |  | 
|  | return parser           # gets stored as self.parser | 
|  |  | 
|  | def do_run(self, args, unknown_args): | 
|  | # This gets called when the user runs the command, e.g.: | 
|  | # | 
|  | #   $ west my-command-name -o FOO BAR | 
|  | #   --optional is FOO | 
|  | #   required is BAR | 
|  | self.inf('--optional is', args.optional) | 
|  | self.inf('required is', args.required) | 
|  |  | 
|  | You can ignore the second argument to ``do_run()`` (``unknown_args`` above), as | 
|  | ``WestCommand`` will reject unknown arguments by default. If you want to be | 
|  | passed a list of unknown arguments instead, add ``accepts_unknown_args=True`` | 
|  | to the ``super().__init__()`` arguments. | 
|  |  | 
|  | Step 2: Add or Update Your :file:`west-commands.yml` | 
|  | ==================================================== | 
|  |  | 
|  | You now need to add a :file:`west-commands.yml` file to your project which | 
|  | describes your extension to west. | 
|  |  | 
|  | Here is an example for the above class definition, assuming it's in | 
|  | :file:`my_west_extension.py` at the project root directory: | 
|  |  | 
|  | .. code-block:: yaml | 
|  |  | 
|  | west-commands: | 
|  | - file: my_west_extension.py | 
|  | commands: | 
|  | - name: my-command-name | 
|  | class: MyCommand | 
|  | help: one-line help for what my-command-name does | 
|  |  | 
|  | The top level of this YAML file is a map with a ``west-commands`` key.  The | 
|  | key's value is a sequence of "command descriptors".  Each command descriptor | 
|  | gives the location of a file implementing west extensions, along with the names | 
|  | of those extensions, and optionally the names of the classes which define them | 
|  | (if not given, the ``class`` value defaults to the same thing as ``name``). | 
|  |  | 
|  | Some information in this file is redundant with definitions in the Python code. | 
|  | This is because west won't import :file:`my_west_extension.py` until the user | 
|  | runs ``west my-command-name``, since: | 
|  |  | 
|  | - It allows users to run ``west update`` with a manifest from an untrusted | 
|  | source, then use other west commands without your code being imported along | 
|  | the way. Since importing a Python module is shell-equivalent, this provides | 
|  | some peace of mind. | 
|  |  | 
|  | - It's a small optimization, since your code will only be imported if it is | 
|  | needed. | 
|  |  | 
|  | So, unless your command is explicitly run, west will just load the | 
|  | :file:`west-commands.yml` file to get the basic information it needs to display | 
|  | information about your extension to the user in ``west --help`` output, etc. | 
|  |  | 
|  | If you have multiple extensions, or want to split your extensions across | 
|  | multiple files, your :file:`west-commands.yml` will look something like this: | 
|  |  | 
|  | .. code-block:: yaml | 
|  |  | 
|  | west-commands: | 
|  | - file: my_west_extension.py | 
|  | commands: | 
|  | - name: my-command-name | 
|  | class: MyCommand | 
|  | help: one-line help for what my-command-name does | 
|  | - file: another_file.py | 
|  | commands: | 
|  | - name: command2 | 
|  | help: another cool west extension | 
|  | - name: a-third-command | 
|  | class: ThirdCommand | 
|  | help: a third command in the same file as command2 | 
|  |  | 
|  | Above: | 
|  |  | 
|  | - :file:`my_west_extension.py` defines extension ``my-command-name`` | 
|  | with class ``MyCommand`` | 
|  | - :file:`another_file.py` defines two extensions: | 
|  |  | 
|  | #. ``command2`` with class ``command2`` | 
|  | #. ``a-third-command`` with class ``ThirdCommand`` | 
|  |  | 
|  | See the file :file:`west-commands-schema.yml` in the `west repository`_ for a | 
|  | schema describing the contents of a :file:`west-commands.yml`. | 
|  |  | 
|  | Step 3: Update Your Manifest | 
|  | ============================ | 
|  |  | 
|  | Finally, you need to specify the location of the :file:`west-commands.yml` you | 
|  | just edited in your west manifest. If your extension is in a project, add it | 
|  | like this: | 
|  |  | 
|  | .. code-block:: yaml | 
|  |  | 
|  | manifest: | 
|  | # [... other contents ...] | 
|  |  | 
|  | projects: | 
|  | - name: your-project | 
|  | west-commands: path/to/west-commands.yml | 
|  | # [... other projects ...] | 
|  |  | 
|  | Where :file:`path/to/west-commands.yml` is relative to the root of the project. | 
|  | Note that the name :file:`west-commands.yml`, while encouraged, is just a | 
|  | convention; you can name the file something else if you need to. | 
|  |  | 
|  | Alternatively, if your extension is in the manifest repository, just do the | 
|  | same thing in the manifest's ``self`` section, like this: | 
|  |  | 
|  | .. code-block:: yaml | 
|  |  | 
|  | manifest: | 
|  | # [... other contents ...] | 
|  |  | 
|  | self: | 
|  | west-commands: path/to/west-commands.yml | 
|  |  | 
|  | That's it; you can now run ``west my-command-name``. Your command's name, help, | 
|  | and the project which contains its code will now also show up in the ``west | 
|  | --help`` output.  If you share the updated repositories with others, they'll be | 
|  | able to use it, too. | 
|  |  | 
|  | .. _west PyPI page: | 
|  | https://pypi.org/project/west/ | 
|  |  | 
|  | .. _west repository: | 
|  | https://github.com/zephyrproject-rtos/west/ |