xref: /aosp_15_r20/external/pigweed/docs/style/cli.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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