xref: /aosp_15_r20/external/pigweed/pw_cli/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_cli:
2
3======
4pw_cli
5======
6.. pigweed-module::
7   :name: pw_cli
8
9   - **Add some flair**: Build out consistent experiences with building blocks
10     for command-line utilities.
11   - **Accelerate workflows**: Create custom ``pw`` commands to streamline
12     common developer workflows.
13
14.. pw_cli-nav-start
15
16.. grid:: 1
17
18   .. grid-item-card:: :octicon:`code-square` API reference
19      :link: module-pw_cli-api
20      :link-type: ref
21      :class-item: sales-pitch-cta-primary
22
23      Reference details about the ``pw_cli`` Python API
24
25.. pw_cli-nav-end
26
27This directory contains the ``pw`` command line interface (CLI) that facilitates
28working with Pigweed. The CLI module adds several subcommands prefixed with
29``pw``, and provides a mechanism for other Pigweed modules to behave as
30"plugins" and register themselves as ``pw`` commands as well. After activating
31the Pigweed environment, these commands will be available for use.
32
33``pw`` includes the following commands by default:
34
35.. code-block:: text
36
37   doctor   Check that the environment is set up correctly for Pigweed.
38   format   Check and fix formatting for source files.
39   help     Display detailed information about pw commands.
40   ide      Configure editors and IDEs to work best with Pigweed.
41   logdemo  Show how logs look at various levels.
42   module   Utilities for managing modules.
43   test     Run Pigweed unit tests built using GN.
44   watch    Watch files for changes and rebuild.
45
46To see an up-to-date list of ``pw`` subcommands, run ``pw --help``.
47
48----------------
49Invoking  ``pw``
50----------------
51``pw`` subcommands are invoked by providing the command name. Arguments prior to
52the command are interpreted by ``pw`` itself; all arguments after the command
53name are interpreted by the command.
54
55Here are some example invocations of ``pw``:
56
57.. code-block:: text
58
59   # Run the doctor command
60   $ pw doctor
61
62   # Run format --fix with debug-level logs
63   $ pw --loglevel debug format --fix
64
65   # Display help for the pw command
66   $ pw -h watch
67
68   # Display help for the watch command
69   $ pw watch -h
70
71--------------------------
72Registering ``pw`` plugins
73--------------------------
74Projects can register their own Python scripts as ``pw`` commands. ``pw``
75plugins are registered by providing the command name, module, and function in
76the ``pigweed.json`` file. ``pigweed.json`` files can add new commands or
77override built-in commands. Since they are accessed by module name, plugins must
78be defined in Python packages that are installed in the Pigweed virtual
79environment.
80
81pigweed.json file format
82========================
83``pigweed.json`` contains plugin entries in the following format:
84
85.. code-block::
86
87   {
88     "pw": {
89       "pw_cli": {
90         "plugins": {
91           "<plugin name>": {
92             "module": "<module containing plugin>",
93             "function": "<entry point for plugin>"
94           },
95           ...
96         }
97       }
98     }
99   }
100
101The following example registers three commands:
102
103.. code-block::
104
105   {
106     "pw": {
107       "pw_cli": {
108         "plugins": {
109           "presubmit": {
110             "module": "my_cool_project.tools",
111             "function": "run_presubmit"
112           },
113           "test": {
114             "module": "my_cool_project.testing",
115             "function": "run_test"
116           },
117           "flash": {
118             "module": "my_cool_project.flash",
119             "function": "main"
120           }
121         }
122       }
123     }
124   }
125
126Defining a plugin function
127==========================
128Any function without required arguments may be used as a plugin function. The
129function should return an int, which the ``pw`` uses as the exit code. The
130``pw`` tool uses the function docstring as the help string for the command.
131
132Typically, ``pw`` commands parse their arguments with the ``argparse`` module.
133``pw`` sets ``sys.argv`` so it contains only the arguments for the plugin,
134so plugins can behave the same whether they are executed independently or
135through ``pw``.
136
137Example
138-------
139This example shows a function that is registered as a ``pw`` plugin.
140
141.. code-block:: python
142
143   # my_package/my_module.py
144
145   def _do_something(device):
146       ...
147
148   def main() -> int:
149       """Do something to a connected device."""
150
151       parser = argparse.ArgumentParser(description=__doc__)
152       parser.add_argument('--device', help='Set which device to target')
153       return _do_something(**vars(parser.parse_args()))
154
155
156   if __name__ == '__main__':
157       logging.basicConfig(format='%(message)s', level=logging.INFO)
158       sys.exit(main())
159
160This plugin is registered in a ``PW_PLUGINS`` file in the current working
161directory or a parent of it.
162
163.. code-block:: python
164
165   # Register my_commmand
166   my_command my_package.my_module main
167
168The function is now available through the ``pw`` command, and will be listed in
169``pw``'s help. Arguments after the command name are passed to the plugin.
170
171.. code-block:: text
172
173   $ pw
174
175    ▒█████▄   █▓  ▄███▒  ▒█    ▒█ ░▓████▒ ░▓████▒ ▒▓████▄
176     ▒█░  █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█  ▒█   ▀  ▒█   ▀  ▒█  ▀█▌
177     ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█  ▒███    ▒███    ░█   █▌
178     ▒█▀     ░█░ ▓█   █▓ ░█░ █ ▒█  ▒█   ▄  ▒█   ▄  ░█  ▄█▌
179     ▒█      ░█░ ░▓███▀   ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀
180
181   usage: pw [-h] [-C DIRECTORY] [-l LOGLEVEL] [--no-banner] [command] ...
182
183   The Pigweed command line interface (CLI).
184
185   ...
186
187   supported commands:
188     doctor        Check that the environment is set up correctly for Pigweed.
189     format        Check and fix formatting for source files.
190     help          Display detailed information about pw commands.
191     ...
192     my_command    Do something to a connected device.
193
194   $ pw my_command -h
195
196    ▒█████▄   █▓  ▄███▒  ▒█    ▒█ ░▓████▒ ░▓████▒ ▒▓████▄
197     ▒█░  █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█  ▒█   ▀  ▒█   ▀  ▒█  ▀█▌
198     ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█  ▒███    ▒███    ░█   █▌
199     ▒█▀     ░█░ ▓█   █▓ ░█░ █ ▒█  ▒█   ▄  ▒█   ▄  ░█  ▄█▌
200     ▒█      ░█░ ░▓███▀   ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀
201
202   usage: pw my_command [-h] [--device DEVICE]
203
204   Do something to a connected device.
205
206   optional arguments:
207     -h, --help       show this help message and exit
208     --device DEVICE  Set which device to target
209
210.. _module-pw_cli-aliases:
211
212Defining a simple alias
213=======================
214For simpler ``pw`` subcommands that effectively only need to act as command line
215aliases, the ``pw_cli.alias`` python module can be reused for simplicity.
216
217First, create a python module that defines the alias:
218
219.. code-block:: python
220
221   # my_package/aliases.py
222
223   from pw_cli.aliases import alias
224
225   foo = alias("bar", "baz")
226
227The remaining step needed is to add a new plugin to ``pigweed.json``:
228
229.. code-block::
230
231   {
232     "pw": {
233       "pw_cli": {
234         "plugins": {
235           "foo": {
236             "module": "my_package.aliases",
237             "function": "foo"
238           },
239           ...
240         }
241       }
242     }
243   }
244
245In this case, the command ``pw foo abc`` will effectively run ``bar baz abc``.
246
247--------------------------
248Branding Pigweed's tooling
249--------------------------
250An important part of starting a new project is picking a name, and in the case
251of Pigweed, designing a banner for the project. Pigweed supports configuring
252the banners by setting environment variables:
253
254* ``PW_BRANDING_BANNER`` - Absolute path to a filename containing a banner to
255  display when running the ``pw`` commands. See the example below.
256* ``PW_BRANDING_BANNER_COLOR`` - Color of the banner. Possible values include:
257  ``red``, ``bold_red``, ``yellow``, ``bold_yellow``, ``green``,
258  ``bold_green``, ``blue``, ``cyan``, ``magenta``, ``bold_white``,
259  ``black_on_white``. See ``pw_cli.colors`` for details.
260* ``PW_ENVSETUP_NO_BANNER`` - Suppress banner from being printed.
261
262The below example shows how to manually change the branding at the command
263line. However, these environment variables should be set in the project root's
264``bootstrap.sh`` before delegating to Pigweed's upstream ``bootstrap.sh``.
265
266.. code-block:: text
267
268   $ cat foo-banner.txt
269
270    ▒██████  ░▓██▓░  ░▓██▓░
271     ▒█░    ▒█   ▒█ ▒█   ▒█
272     ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█
273     ▒█▀    ▒█   ▒█ ▒█   ▒█
274     ▒█      ░▓██▓░  ░▓██▓░
275
276   $ export PW_BRANDING_BANNER="$(pwd)/foo-banner.txt"
277   $ export PW_BRANDING_BANNER_COLOR="bold_red"
278   $ pw logdemo
279
280    ▒██████  ░▓██▓░  ░▓██▓░
281     ▒█░    ▒█   ▒█ ▒█   ▒█
282     ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█
283     ▒█▀    ▒█   ▒█ ▒█   ▒█
284     ▒█      ░▓██▓░  ░▓██▓░
285
286   20200610 12:03:44 CRT This is a critical message
287   20200610 12:03:44 ERR There was an error on our last operation
288   20200610 12:03:44 WRN Looks like something is amiss; consider investigating
289   20200610 12:03:44 INF The operation went as expected
290   20200610 12:03:44 OUT Standard output of subprocess
291
292The branding is not purely visual; it serves to make it clear which project an
293engineer is working with.
294
295Making the ASCII / ANSI art
296===========================
297The most direct way to make the ASCII art is to create it with a text editor.
298However, there are some tools to make the process faster and easier.
299
300* `Patorjk's ASCII art generator <http://patorjk.com/software/taag/>`_ - A
301  great starting place, since you can copy and paste straight from the browser
302  into a file, and then point ``PW_BRANDING_BANNER`` at it.  Most of the fonts
303  use normal ASCII characters; and fonts with extended ASCII characters use the
304  Unicode versions of them (needed for modern terminals).
305
306There are other options, but these require additional work to put into Pigweed
307since they only export in the traditional ANS or ICE formats. The old ANS
308formats do not have a converter (contributions welcome!). Here are some of the
309options as of mid-2020:
310
311* `Playscii <http://vectorpoem.com/playscii/>`_ - Actively maintained.
312* `Moebius <https://github.com/blocktronics/moebius>`_ - Actively maintained.
313* `SyncDraw <http://syncdraw.bbsdev.net/>`_ - Actively maintained, in 2020, in
314  a CVS repository.
315* `PabloDraw <http://picoe.ca/products/pablodraw/>`_ - Works on most desktop
316  machines thanks to being written in .NET. Not maintained, but works well. Has
317  an impresive brush system for organic style drawing.
318* `TheDraw <https://en.wikipedia.org/wiki/TheDraw>`_ - One of the most popular
319  ANSI art editors back in the 90s. Requires DOSBox to run on modern machines,
320  but otherwise works. It has some of the most impressive capabilities,
321  including supporting full-color multi-character fonts.
322
323Future branding improvements
324============================
325Branding the ``pw`` tool is a great start, but more changes are planned:
326
327- Supporting branding the ``bootstrap/activate`` banner, which for technical
328  reasons is not the same code as the banner printing from the Python tooling.
329  These will use the same ``PW_BRANDING_BANNER`` and
330  ``PW_BRANDING_BANNER_COLOR`` environment variables.
331- Supporting renaming the ``pw`` command to something project specific, like
332  ``foo`` in this case.
333- Re-coloring the log headers from the ``pw`` tool.
334
335.. toctree::
336   :hidden:
337   :maxdepth: 1
338
339   api
340