1.. _docs-pw-style-cli: 2 3========= 4CLI style 5========= 6This guide helps ensure that command-line interface (CLI) utilities in Pigweed 7behave in a reasonably consistent manner. This applies to build tools, test 8programs, etc. 9 10The Pigweed CLI style guide is based on the `Command Line Interface Guidelines 11<https://clig.dev/>`_ (CLIG), and mostly defers to it. The CLIG applies to 12Pigweed except as noted. This document covers key guidelines and Pigweed 13specifics. 14 15As most Pigweed CLIs are not expected to be full-fledged user interfaces like 16``git`` or ``docker``---but rather a collection of smaller tools---this guide 17focuses on specific basics rather than broad philosophy or advanced topics. 18The Pigweed CLI style guide only applies to utilities included in Pigweed 19itself; projects which use Pigweed are free to conform to this, or any other 20guide as they see fit. 21 22-------- 23Examples 24-------- 25The following programs demonstrate conformance to Pigweed's CLI style guide rules: 26 27* `pw_digital_io_linux_cli 28 <https://cs.opensource.google/pigweed/pigweed/+/main:pw_digital_io_linux/digital_io_cli.cc>`_ 29 30 * Note: This does not yet fully conform. See :bug:`330435501`. 31 32---------- 33Exit codes 34---------- 35Programs must exit with a zero exit code on success and nonzero on failure. 36 37If multiple non-zero exit codes are used, they should be clearly documented and 38treated as a stable API. If appropriate, consider using :ref:`module-pw_status` codes. 39 40.. note:: 41 In no case should a program return ``-1`` from ``main()``. The exit code is 42 essentially a ``uint8_t`` so returning ``-1`` would result in an exit status 43 of 255. 44 45.. tip:: 46 Avoid exit codes 126 and above because those conflict with exit codes 47 returned by some shells. This can create ambiguity when a tool is called via 48 a wrapper script. See https://tldp.org/LDP/abs/html/exitcodes.html. 49 50-------------- 51Program output 52-------------- 53Following these guidelines ensures that the output of a program can be properly 54used in a shell pipeline (e.g. ``pw_foo | grep foo``) or otherwise consumed by 55another program or script. 56 57See `CLIG:Guidelines Output <https://clig.dev/#output>`_. 58 59Standard output 60=============== 61The main output of a program should be written to *standard out* (``stdout``). 62 63.. tab-set-code:: 64 65 .. code-block:: c++ 66 67 #include <iostream> 68 69 int main() { 70 std::cout << "foo: " << foo() << std::endl; 71 std::cout << "bar: " << bar() << std::endl; 72 } 73 74 .. code-block:: c 75 76 #include <stdio.h> 77 78 int main() { 79 printf("foo: %d\n", foo()); 80 printf("bar: %d\n", bar()); 81 } 82 83 .. code-block:: python 84 85 def main() -> int: 86 print("foo:", foo()) 87 print("bar:", bar()) 88 return 0 89 90Standard error 91============== 92Debug logs, error messages, etc. should be written to *standard error* (``stderr``). 93 94.. tab-set-code:: 95 96 .. code-block:: c++ 97 98 #include <iostream> 99 100 int main() { 101 if (!InitFoo()) { 102 std::cerr << "Error: failed to initialize foo!" << std::endl; 103 return 2; 104 } 105 // ... 106 } 107 108 .. code-block:: c 109 110 #include <stdio.h> 111 112 int main() { 113 if (!InitFoo()) { 114 fprintf(stderr, "Error: failed to initialize foo!\n"); 115 return 2; 116 } 117 // ... 118 } 119 120 .. code-block:: python 121 122 import sys 123 124 def main() -> int: 125 if not init_foo(): 126 print("Error: failed to initialize foo!", file=sys.stderr) 127 return 2 128 # ... 129 130------- 131Logging 132------- 133It is recommended to use :ref:`module-pw_log` for logging, including 134``PW_LOG_DEBUG`` for debug messages, and ``PW_LOG_ERROR`` for all error 135conditions. 136 137.. warning:: 138 139 Currently there is no preconfigured ``pw_log`` backend which sends log 140 messages to ``stderr``. See :bug:`329747262`. 141 142 This can be achieved by using ``pw_log_basic`` and calling ``SetOutput()`` 143 as follows: 144 145 .. code-block:: c++ 146 147 pw::log_basic::SetOutput([](std::string_view log) { 148 std::cerr << log << std::endl; 149 }); 150 151.. warning:: 152 153 Currently there is no mechanism for setting the ``pw_log`` level at runtime. 154 (E.g. via ``--verbose`` or ``--quiet`` options). See :bug:`329755001`. 155 156**Exceptions**: 157 158* Informative messages which should be written to ``stderr``, but which form a 159 core part of the user interface, can be written directly to ``stderr`` rather 160 than via ``pw_log``. You can do this for usage text, for example. 161 162------------------- 163Arguments and flags 164------------------- 165See `CLIG:Guidelines Arguments and flags <https://clig.dev/#arguments-and-flags>`_. 166 167- Prefer flags over (positional) arguments. This leads to a more verbose, but 168 much more extensible interface. 169 170 .. admonition:: **Yes**: Using flags to clarify inputs 171 :class: checkmark 172 173 .. code-block:: console 174 175 $ pw_foo --symbols=symbols.txt --config=config.json --passes=7 \ 176 --bin-out=output.bin --map-out=output.map 177 178 .. admonition:: **No**: Using a lot of positional arguments 179 :class: error 180 181 .. code-block:: console 182 183 $ pw_foo symbols.txt config.json 7 output.bin output.map 184 185- Prefer subcommands (which are naturally mutually exclusive) over 186 mutually exclusive flags. 187 188 .. admonition:: **Yes**: Using subcommands to specify actions 189 :class: checkmark 190 191 .. code-block:: console 192 193 $ pw_foo get --key abc 194 $ pw_foo set --key abc 195 196 .. admonition:: **No**: Using mutually-exclusive flags 197 :class: error 198 199 .. code-block:: console 200 201 $ pw_foo --get-key abc 202 $ pw_foo --set-key abc 203 $ pw_foo --get-key abc --set-key abc # Error 204 205- Show usage or help text when no subcommand or arguments are provided. 206 Display full help text by default unless it is longer than 24 lines, in which 207 case, show abbreviated usage text. Show full help text if ``--help`` is given. 208