blob: 90197643fa2a03dac5eb6b44f8d0d88786d35da5 [file] [log] [blame]
Wyatt Heplerf9fb90f2020-09-30 18:59:33 -07001.. _module-pw_cli:
Armando Montanez0054a9b2020-03-13 13:06:24 -07002
3------
4pw_cli
5------
Wyatt Hepler8a562a02020-04-16 15:28:13 -07006This directory contains the ``pw`` command line interface (CLI) that facilitates
Armando Montanez0054a9b2020-03-13 13:06:24 -07007working with Pigweed. The CLI module adds several subcommands prefixed with
Wyatt Hepler8a562a02020-04-16 15:28:13 -07008``pw``, and provides a mechanism for other Pigweed modules to behave as
9"plugins" and register themselves as ``pw`` commands as well. After activating
10the Pigweed environment, these commands will be available for use.
Armando Montanez0054a9b2020-03-13 13:06:24 -070011
Wyatt Hepler8a562a02020-04-16 15:28:13 -070012``pw`` includes the following commands by default:
Armando Montanez0054a9b2020-03-13 13:06:24 -070013
Wyatt Hepler8a562a02020-04-16 15:28:13 -070014.. code-block:: text
Armando Montanez0054a9b2020-03-13 13:06:24 -070015
Wyatt Hepler8a562a02020-04-16 15:28:13 -070016 doctor Check that the environment is set up correctly for Pigweed.
17 format Check and fix formatting for source files.
18 help Display detailed information about pw commands.
19 logdemo Show how logs look at various levels.
20 module-check Check that a module matches Pigweed's module guidelines.
21 test Run Pigweed unit tests built using GN.
22 watch Watch files for changes and rebuild.
23
24To see an up-to-date list of ``pw`` subcommands, run ``pw --help``.
25
26Invoking ``pw``
27================
28``pw`` subcommands are invoked by providing the command name. Arguments prior to
29the command are interpreted by ``pw`` itself; all arguments after the command
30name are interpreted by the command.
31
32Here are some example invocations of ``pw``:
33
34.. code-block:: text
35
36 # Run the doctor command
Armando Montanez0054a9b2020-03-13 13:06:24 -070037 $ pw doctor
Armando Montanez0054a9b2020-03-13 13:06:24 -070038
Wyatt Hepler8a562a02020-04-16 15:28:13 -070039 # Run format --fix with debug-level logs
40 $ pw --loglevel debug format --fix
Armando Montanez0054a9b2020-03-13 13:06:24 -070041
Wyatt Hepler8a562a02020-04-16 15:28:13 -070042 # Display help for the pw command
43 $ pw -h watch
44
45 # Display help for the watch command
46 $ pw watch -h
47
48Registering ``pw`` plugins
49==========================
50Projects can register their own Python scripts as ``pw`` commands. ``pw``
51plugins are registered by providing the command name, module, and function in a
52``PW_PLUGINS`` file. ``PW_PLUGINS`` files can add new commands or override
53built-in commands. Since they are accessed by module name, plugins must be
54defined in Python packages that are installed in the Pigweed virtual
55environment.
56
Wyatt Hepler8a562a02020-04-16 15:28:13 -070057Plugin registrations in a ``PW_PLUGINS`` file apply to the their directory and
58all subdirectories, similarly to configuration files like ``.clang-format``.
59Registered plugins appear as commands in the ``pw`` tool when ``pw`` is run from
60those directories.
61
62Projects that wish to register commands might place a ``PW_PLUGINS`` file in the
63root of their repo. Multiple ``PW_PLUGINS`` files may be applied, but the ``pw``
64tool gives precedence to a ``PW_PLUGINS`` file in the current working directory
65or the nearest parent directory.
66
67PW_PLUGINS file format
68----------------------
69``PW_PLUGINS`` contains one plugin entry per line in the following format:
70
71.. code-block:: python
72
73 # Lines that start with a # are ignored.
74 <command name> <Python module> <function>
75
76The following example registers three commands:
77
78.. code-block:: python
79
80 # Register the presubmit script as pw presubmit
81 presubmit my_cool_project.tools run_presubmit
82
83 # Override the pw test command with a custom version
84 test my_cool_project.testing run_test
85
86 # Add a custom command
87 flash my_cool_project.flash main
88
89Defining a plugin function
90--------------------------
91Any function without required arguments may be used as a plugin function. The
92function should return an int, which the ``pw`` uses as the exit code. The
93``pw`` tool uses the function docstring as the help string for the command.
94
95Typically, ``pw`` commands parse their arguments with the ``argparse`` module.
96``pw`` sets ``sys.argv`` so it contains only the arguments for the plugin,
97so plugins can behave the same whether they are executed independently or
98through ``pw``.
99
100Example
101^^^^^^^
102This example shows a function that is registered as a ``pw`` plugin.
103
104.. code-block:: python
105
106 # my_package/my_module.py
107
108 def _do_something(device):
109 ...
110
111 def main() -> int:
112 """Do something to a connected device."""
113
114 parser = argparse.ArgumentParser(description=__doc__)
115 parser.add_argument('--device', help='Set which device to target')
116 return _do_something(**vars(parser.parse_args()))
117
118
119 if __name__ == '__main__':
120 logging.basicConfig(format='%(message)s', level=logging.INFO)
121 sys.exit(main())
122
123This plugin is registered in a ``PW_PLUGINS`` file in the current working
124directory or a parent of it.
125
126.. code-block:: python
127
128 # Register my_commmand
129 my_command my_package.my_module main
130
131The function is now available through the ``pw`` command, and will be listed in
132``pw``'s help. Arguments after the command name are passed to the plugin.
133
134.. code-block:: text
135
136 $ pw
137
138 ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄
139 ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌
140 ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌
141 ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌
142 ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀
143
144 usage: pw [-h] [-C DIRECTORY] [-l LOGLEVEL] [--no-banner] [command] ...
145
146 The Pigweed command line interface (CLI).
147
148 ...
149
150 supported commands:
151 doctor Check that the environment is set up correctly for Pigweed.
152 format Check and fix formatting for source files.
153 help Display detailed information about pw commands.
154 ...
155 my_command Do something to a connected device.
156
157 $ pw my_command -h
158
159 ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄
160 ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌
161 ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌
162 ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌
163 ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀
164
165 usage: pw my_command [-h] [--device DEVICE]
166
167 Do something to a connected device.
168
169 optional arguments:
170 -h, --help show this help message and exit
171 --device DEVICE Set which device to target
Keir Mierle6ec3c3b2020-06-10 10:30:33 -0700172
173Branding Pigweed's tooling
174==========================
175An important part of starting a new project is picking a name, and in the case
176of Pigweed, designing a banner for the project. Pigweed supports configuring
177the banners by setting environment variables:
178
179* ``PW_BRANDING_BANNER`` - Absolute path to a filename containing a banner to
180 display when running the ``pw`` commands. See the example below.
181* ``PW_BRANDING_BANNER_COLOR`` - Color of the banner. Possible values include:
182 ``red``, ``bold_red``, ``yellow``, ``bold_yellow``, ``green``,
183 ``bold_green``, ``blue``, ``cyan``, ``magenta``, ``bold_white``,
184 ``black_on_white``. See ``pw_cli.colors`` for details.
185
186The below example shows how to manually change the branding at the command
187line. However, these environment variables should be set in the project root's
188``bootstrap.sh`` before delegating to Pigweed's upstream ``bootstrap.sh``.
189
190.. code-block:: text
191
192 $ cat foo-banner.txt
193
194 ▒██████ ░▓██▓░ ░▓██▓░
195 ▒█░ ▒█ ▒█ ▒█ ▒█
196 ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█
197 ▒█▀ ▒█ ▒█ ▒█ ▒█
198 ▒█ ░▓██▓░ ░▓██▓░
199
200 $ export PW_BRANDING_BANNER="$(pwd)/foo-banner.txt"
201 $ export PW_BRANDING_BANNER_COLOR="bold_red"
202 $ pw logdemo
203
204 ▒██████ ░▓██▓░ ░▓██▓░
205 ▒█░ ▒█ ▒█ ▒█ ▒█
206 ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█
207 ▒█▀ ▒█ ▒█ ▒█ ▒█
208 ▒█ ░▓██▓░ ░▓██▓░
209
210 20200610 12:03:44 CRT This is a critical message
211 20200610 12:03:44 ERR There was an error on our last operation
212 20200610 12:03:44 WRN Looks like something is amiss; consider investigating
213 20200610 12:03:44 INF The operation went as expected
214 20200610 12:03:44 OUT Standard output of subprocess
215
216The branding is not purely visual; it serves to make it clear which project an
217engineer is working with.
218
219Making the ASCII / ANSI art
220---------------------------
221The most direct way to make the ASCII art is to create it with a text editor.
222However, there are some tools to make the process faster and easier.
223
224* `Patorjk's ASCII art generator <http://patorjk.com/software/taag/>`_ - A
225 great starting place, since you can copy and paste straight from the browser
226 into a file, and then point ``PW_BRANDING_BANNER`` at it. Most of the fonts
227 use normal ASCII characters; and fonts with extended ASCII characters use the
228 Unicode versions of them (needed for modern terminals).
Keir Mierle6ec3c3b2020-06-10 10:30:33 -0700229
230There are other options, but these require additional work to put into Pigweed
231since they only export in the traditional ANS or ICE formats. The old ANS
232formats do not have a converter (contributions welcome!). Here are some of the
233options as of mid-2020:
234
235* `Playscii <http://vectorpoem.com/playscii/>`_ - Actively maintained.
236* `Moebius <https://github.com/blocktronics/moebius>`_ - Actively maintained.
237* `SyncDraw <http://syncdraw.bbsdev.net/>`_ - Actively maintained, in 2020, in
238 a CVS repository.
239* `PabloDraw <http://picoe.ca/products/pablodraw/>`_ - Works on most desktop
240 machines thanks to being written in .NET. Not maintained, but works well. Has
241 an impresive brush system for organic style drawing.
242* `TheDraw <https://en.wikipedia.org/wiki/TheDraw>`_ - One of the most popular
243 ANSI art editors back in the 90s. Requires DOSBox to run on modern machines,
244 but otherwise works. It has some of the most impressive capabilities,
245 including supporting full-color multi-character fonts.
246
247Future branding improvements
248----------------------------
249Branding the ``pw`` tool is a great start, but more changes are planned:
250
251- Supporting branding the ``bootstrap/activate`` banner, which for technical
252 reasons is not the same code as the banner printing from the Python tooling.
253 These will use the same ``PW_BRANDING_BANNER`` and
254 ``PW_BRANDING_BANNER_COLOR`` environment variables.
255- Supporting renaming the ``pw`` command to something project specific, like
256 ``foo`` in this case.
257- Re-coloring the log headers from the ``pw`` tool.
Wyatt Hepler91d2c502021-03-12 15:21:30 -0800258
259pw_cli Python package
260=====================
261The ``pw_cli`` Pigweed module includes the ``pw_cli`` Python package, which
262provides utilities for creating command line tools with Pigweed.
263
Wyatt Heplerbea166e2021-04-08 10:56:31 -0700264pw_cli.log
Wyatt Hepler91d2c502021-03-12 15:21:30 -0800265----------
266.. automodule:: pw_cli.log
267 :members:
Wyatt Heplerbea166e2021-04-08 10:56:31 -0700268
269pw_cli.plugins
270--------------
271:py:mod:`pw_cli.plugins` provides general purpose plugin functionality. The
272module can be used to create plugins for command line tools, interactive
273consoles, or anything else. Pigweed's ``pw`` command uses this module for its
274plugins.
275
276To use plugins, create a :py:class:`pw_cli.plugins.Registry`. The registry may
277have an optional validator function that checks plugins before they are
278registered (see :py:meth:`pw_cli.plugins.Registry.__init__`).
279
280Plugins may be registered in a few different ways.
281
Wyatt Heplere9230322021-04-13 16:47:26 -0700282 * **Direct function call.** Register plugins by calling
283 :py:meth:`pw_cli.plugins.Registry.register` or
Wyatt Heplerbea166e2021-04-08 10:56:31 -0700284 :py:meth:`pw_cli.plugins.Registry.register_by_name`.
Wyatt Heplere9230322021-04-13 16:47:26 -0700285
286 .. code-block:: python
287
288 registry = pw_cli.plugins.Registry()
289
290 registry.register('plugin_name', my_plugin)
291 registry.register_by_name('plugin_name', 'module_name', 'function_name')
292
293 * **Decorator.** Register using the :py:meth:`pw_cli.plugins.Registry.plugin`
294 decorator.
295
296 .. code-block:: python
297
298 _REGISTRY = pw_cli.plugins.Registry()
299
300 # This function is registered as the "my_plugin" plugin.
301 @_REGISTRY.plugin
302 def my_plugin():
303 pass
304
305 # This function is registered as the "input" plugin.
306 @_REGISTRY.plugin(name='input')
307 def read_something():
308 pass
309
310 The decorator may be aliased to give a cleaner syntax (e.g. ``register =
311 my_registry.plugin``).
312
313 * **Plugins files.** Plugins files use a simple format:
Wyatt Heplerbea166e2021-04-08 10:56:31 -0700314
315 .. code-block::
316
317 # Comments start with "#". Blank lines are ignored.
318 name_of_the_plugin module.name module_member
319
320 another_plugin some_module some_function
321
Wyatt Heplere9230322021-04-13 16:47:26 -0700322 These files are placed in the file system and apply similarly to Git's
323 ``.gitignore`` files. From Python, these files are registered using
324 :py:meth:`pw_cli.plugins.Registry.register_file` and
325 :py:meth:`pw_cli.plugins.Registry.register_directory`.
326
327pw_cli.plugins module reference
328^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Wyatt Heplerbea166e2021-04-08 10:56:31 -0700329.. automodule:: pw_cli.plugins
330 :members: