CLI style#
This guide helps ensure that command-line interface (CLI) utilities in Pigweed behave in a reasonably consistent manner. This applies to build tools, test programs, etc.
The Pigweed CLI style guide is based on the Command Line Interface Guidelines (CLIG), and mostly defers to it. The CLIG applies to Pigweed except as noted. This document covers key guidelines and Pigweed specifics.
As most Pigweed CLIs are not expected to be full-fledged user interfaces like
git
or docker
—but rather a collection of smaller tools—this guide
focuses on specific basics rather than broad philosophy or advanced topics.
The Pigweed CLI style guide only applies to utilities included in Pigweed
itself; projects which use Pigweed are free to conform to this, or any other
guide as they see fit.
Examples#
The following programs demonstrate conformance to Pigweed’s CLI style guide rules:
-
Note: This does not yet fully conform. See b/330435501.
Exit codes#
Programs must exit with a zero exit code on success and nonzero on failure.
If multiple non-zero exit codes are used, they should be clearly documented and treated as a stable API. If appropriate, consider using pw_status codes.
Note
In no case should a program return -1
from main()
. The exit code is
essentially a uint8_t
so returning -1
would result in an exit status
of 255.
Tip
Avoid exit codes 126 and above because those conflict with exit codes returned by some shells. This can create ambiguity when a tool is called via a wrapper script. See https://tldp.org/LDP/abs/html/exitcodes.html.
Program output#
Following these guidelines ensures that the output of a program can be properly
used in a shell pipeline (e.g. pw_foo | grep foo
) or otherwise consumed by
another program or script.
Standard output#
The main output of a program should be written to standard out (stdout
).
#include <iostream>
int main() {
std::cout << "foo: " << foo() << std::endl;
std::cout << "bar: " << bar() << std::endl;
}
#include <stdio.h>
int main() {
printf("foo: %d\n", foo());
printf("bar: %d\n", bar());
}
def main() -> int:
print("foo:", foo())
print("bar:", bar())
return 0
Standard error#
Debug logs, error messages, etc. should be written to standard error (stderr
).
#include <iostream>
int main() {
if (!InitFoo()) {
std::cerr << "Error: failed to initialize foo!" << std::endl;
return 2;
}
// ...
}
#include <stdio.h>
int main() {
if (!InitFoo()) {
fprintf(stderr, "Error: failed to initialize foo!\n");
return 2;
}
// ...
}
import sys
def main() -> int:
if not init_foo():
print("Error: failed to initialize foo!", file=sys.stderr)
return 2
# ...
Logging#
It is recommended to use pw_log for logging, including
PW_LOG_DEBUG
for debug messages, and PW_LOG_ERROR
for all error
conditions.
Warning
Currently there is no preconfigured pw_log
backend which sends log
messages to stderr
. See b/329747262.
This can be achieved by using pw_log_basic
and calling SetOutput()
as follows:
pw::log_basic::SetOutput([](std::string_view log) {
std::cerr << log << std::endl;
});
Warning
Currently there is no mechanism for setting the pw_log
level at runtime.
(E.g. via --verbose
or --quiet
options). See b/329755001.
Exceptions:
Informative messages which should be written to
stderr
, but which form a core part of the user interface, can be written directly tostderr
rather than viapw_log
. You can do this for usage text, for example.
Arguments and flags#
See CLIG:Guidelines Arguments and flags.
Prefer flags over (positional) arguments. This leads to a more verbose, but much more extensible interface.
Yes: Using flags to clarify inputs
$ pw_foo --symbols=symbols.txt --config=config.json --passes=7 \ --bin-out=output.bin --map-out=output.map
No: Using a lot of positional arguments
$ pw_foo symbols.txt config.json 7 output.bin output.map
Prefer subcommands (which are naturally mutually exclusive) over mutually exclusive flags.
Yes: Using subcommands to specify actions
$ pw_foo get --key abc $ pw_foo set --key abc
No: Using mutually-exclusive flags
$ pw_foo --get-key abc $ pw_foo --set-key abc $ pw_foo --get-key abc --set-key abc # Error
Show usage or help text when no subcommand or arguments are provided. Display full help text by default unless it is longer than 24 lines, in which case, show abbreviated usage text. Show full help text if
--help
is given.