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