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