Part 1
요약·해설
Extension command의 발견 방식과 비활성화 설정, WestCommand subclass 구현, west-commands.yml descriptor, 지연 import의 보안 경계, manifest project·self 등록을 설명합니다.
WestCommand · argparse · do_run
west-commands.yml
manifest project · self
Part 2
접을 수 있는 영어 원문 전체
영어 원문 전체 펼치기
A5C2C1A4A6CA6162A942B6E7D157A1154ED9CDB697BD78EE0B44091B76D02152
.. _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
from west import log # use this for user output
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
log.inf('--optional is', args.optional)
log.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/
Part 3
한국어 전문 번역
Extension
West는 plug-in 방식으로 확장할 수 있습니다. West source code를 수정하지 않고도 자신의 명령을 추가할 수 있으며, 이를 west extension command 또는 줄여서 extension이라고 합니다.
Extension은 west --help 출력에서 해당 명령을 정의한 project 전용 section에 나타납니다. 이 페이지는 west extension command의 일반 원리와 직접 작성하는 방법을 설명합니다.
Zephyr에서 west로 실행하는 build·flash·debug 명령과 Zephyr extension command도 extension입니다. 따라서 west --help에는 다음과 같이 표시됩니다.
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구현 세부 사항은 zephyr/scripts/west-commands.yml과 zephyr/scripts/west_commands 디렉터리에서 확인할 수 있습니다.
Extension command 비활성화
Extension command 지원을 끄려면 commands.allow_extensions 설정을 false로 지정합니다. West를 실행하는 모든 workspace에서 전역으로 끄려면:
west config --global commands.allow_extensions false필요하다면 특정 west workspace에서만 다시 켤 수 있습니다.
west config --local commands.allow_extensions trueWest extension 추가
자신의 extension을 추가하는 작업은 세 단계입니다.
- Command를 구현하는 code를 작성합니다.
west-commands.ymlfile에 command 정보를 추가합니다.west-commands.ymlfile이 west manifest에서 참조되게 합니다.
내장 명령과 이름이 같은 extension command는 west가 무시합니다.
1단계: command 구현
Command 구현을 담을 Python file을 만듭니다. 현재 지원하는 Python version은 west PyPI의 Meta > Requires에서 확인합니다.
File은 west manifest가 추적하는 어느 project에나 둘 수 있고 manifest repository 자체에 두어도 됩니다. 이 file에는 west.commands.WestCommand의 subclass가 반드시 있어야 하며, extension이 실행될 때 west가 이 class를 instantiate하여 사용합니다.
다음 skeleton은 WestCommand subclass와 모든 abstract method의 기본 구현을 포함합니다. 사용할 수 있는 API는 West API를 참고합니다.
'''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
from west import log # use this for user output
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
log.inf('--optional is', args.optional)
log.inf('required is', args.required)do_run()의 두 번째 argument인 unknown_args는 기본적으로 무시해도 됩니다. WestCommand가 알 수 없는 argument를 기본 거부하기 때문입니다.
알 수 없는 argument의 목록도 command가 직접 받으려면 super().__init__() argument에 accepts_unknown_args=True를 추가합니다.
2단계: west-commands.yml 추가 또는 갱신
이제 project에 extension을 설명하는 west-commands.yml file을 추가합니다.
앞의 class가 project root의 my_west_extension.py에 있다고 가정하면 다음과 같이 작성합니다.
west-commands:
- file: my_west_extension.py
commands:
- name: my-command-name
class: MyCommand
help: one-line help for what my-command-name doesYAML 최상위는 west-commands key를 가진 map입니다. 이 key의 값은 command descriptor의 sequence입니다.
각 descriptor는 west extension을 구현한 file 위치와 extension 이름을 지정합니다. Command를 정의한 class 이름은 선택 사항이며 class를 생략하면 기본값은 name과 같습니다.
이 file의 일부 정보는 Python code의 정의와 중복됩니다. West가 사용자가 west my-command-name을 실행하기 전에는 my_west_extension.py를 import하지 않기 때문입니다.
지연 import에는 다음 두 이유가 있습니다.
- 신뢰할 수 없는 source의 manifest로
west update를 실행한 뒤에도, 다른 west 명령을 쓰는 동안 그 source의 code가 저절로 import되지 않습니다. Python module import는 shell command 실행과 같은 수준의 권한을 가질 수 있으므로 안전성을 높입니다. - Extension code가 실제로 필요할 때만 import하므로 작은 성능 최적화가 됩니다.
따라서 command를 명시적으로 실행하지 않은 동안 west는 west-commands.yml만 읽고 west --help 등에 표시할 이름·도움말 같은 기본 정보만 얻습니다.
Extension이 여러 개이거나 여러 file로 나누고 싶다면 west-commands.yml을 다음처럼 작성합니다.
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위 예의 정의 관계는 다음과 같습니다.
my_west_extension.py는MyCommandclass로my-command-nameextension을 정의합니다.another_file.py는 두 extension을 정의합니다.command2는 class 이름도 기본값인command2입니다.a-third-command는ThirdCommandclass를 사용합니다.
west-commands.yml 내용의 schema는 west repository의 west-commands-schema.yml에서 확인합니다.
3단계: manifest 갱신
마지막으로 편집한 west-commands.yml의 위치를 west manifest에 지정합니다.
Extension이 일반 project에 있다면 그 project entry에 다음처럼 추가합니다.
manifest:
# [... other contents ...]
projects:
- name: your-project
west-commands: path/to/west-commands.yml
# [... other projects ...]path/to/west-commands.yml은 project root를 기준으로 한 상대 path입니다. west-commands.yml이라는 이름은 권장 관례일 뿐이며 필요하면 다른 이름을 사용할 수 있습니다.
Extension이 manifest repository에 있다면 manifest의 self section에 같은 방식으로 지정합니다.
manifest:
# [... other contents ...]
self:
west-commands: path/to/west-commands.yml이제 west my-command-name을 실행할 수 있습니다. Command 이름, 도움말, code가 들어 있는 project도 west --help output에 표시됩니다. 갱신한 repository를 다른 사람과 공유하면 그들도 같은 extension을 사용할 수 있습니다.
Source
출처
영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.