• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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