blob: a9c7503600712f83d4870060a4847273117e9523 [file] [log] [blame] [view]
# Command-line Tools Rubric
## Overview
This document is for command line interface (CLI) tools. Graphical User
Interfaces (GUI) are out of scope.
When developing tools for Fuchsia there are specific features and styles that
will be used to create consistency. This document walks through those
requirements.
The goal is to maintain a uniform fit and finish for Fuchsia developer tools so
that developers can know what to expect. They can most easily see how to
accomplish common tasks and there is a well lit path to discover rarer used
tools.
## Guide
The experience developers have writing software for Fuchsia will impact their
general feelings toward writing for the platform and our tools are a significant
part of that experience. Providing tools that are inconsistent (with one
another) creates a poor developer experience.
This guide provides a rubric that Fuchsia tools must follow.
> **SDK**
>
> Some sections have an "SDK" call-out, like this one. These detail specific
> rules that apply to tools included with the SDK distribution.
## Considerations
Before embarking on the creation of a new tool, consider these factors to
determine if the tool is a good fit for Fuchsia or the Fuchsia SDK.
> **SDK**
>
> SDK tools are specific to Fuchsia in some way. Generic tools or tools that are
> widely available should not be part of Fuchsia and will not be included in the
> Fuchsia SDK. For example, a tool that verifies generic JSON files would not be
> a good addition. However a tool that verifies Fuchsia `.cmx` files, which
> happen to use the JSON format, would be okay.
### Audience
Tools may be used for different development tasks. On a large team these roles
may be separate people. Some categories are:
- Component development (mods/agents)
- Driver development (DDK)
- Fuchsia development (SDK)
- Build integration (Blaze, Bazel, etc.)
- Quality assurance (QA)
- System integrators (e.g., on-device network tools)
- Publishing (from dev host to server)
- Deployment (from server to customers)
Consider which users may use a tool and cater the tool to the audience.
Tools may have different integration expectations. For example, a developer
doing mod development may expect tools to integrate with their Integrated
Development Environment (IDE), while a build integration tool may be called from
a script.
### Grouping Related Tools
Prefer to put related commands under a common tool, i.e. like `git` and `fx` do.
This helps encourage the team toward a shared workflow and provides a point of
discovery.
Prefer subcommands to multiple tools. E.g. don't create tools with hyphenated
names like `package-create` and `package-publish`, instead create a `package`
command that accepts create and publish subcommands.
Keep the number of commands under a tool organized and reasonable. I.e. avoid
adding unrelated commands to a tool and provide sensible organization of the
commands in the help and documentation.
### Scope of Work
Command line tools can be divided into two groups: simple single purpose tools
and larger more featureful tools. Create tools that are ergonomic for their
purpose. Simple tools should be quick to start up, while more complex tools will
lean toward the more featureful.
Larger tools will encompass an entire task at the user (developer) level. Avoid
making a tool that accomplishes one small step of a task; instead make a tool
that will perform a complete task.
For example, when:
- developing a C++ application: run the preprocessor, run the compiler, run the
linker, start the built executable.
- working on a unit test: build the tests and run the tests being worked on
- developing a mod: compile the code, move the code and resources to the device,
start the mod (or hot-reload)
Lean toward a tool that will accomplish all the steps needed by default, but
allow for an advanced user to do a partial step (for example, passing an
argument to ask the C++ compiler to only run the preprocessor).
> **SDK**
>
> For SDK build integrators, separate tools. The build integrators will learn
> each and piece them together to make a working system.
#### Sharing common functionality
If a small step of a task will be needed by several tools, it doesn't make sense
to duplicate that code. Consider making a small support tool or create a library
to share the code.
Making a small tool that performs one step of the task can make sense to promote
code reuse. If the user is not expected to run this small tool individually,
place the support tool in a directory that is not added to the `$PATH`. I.e.
avoid polluting the environment path unnecessarily.
Providing a library to share code may be preferable, so that a subprocess isn't
needed.
## Implementation
Here is some guidance for the nuts and bolts of creating a tool. We'll cover
which language to write the tool in, what style to use in that language, and so
on.
### Naming
The following applies to names of binaries, tools, sub-commands, and long
parameter flags.
Use well-known US English terms or nouns for names. Well-known nouns includes
those in common use for the subject matter, or the names of whole subsystems.
If a name does not appear in documentation, it is likely not well-known. If
it does not appear in any implementation, it is definitely not well-known.
Only use lower-case characters in the US-ASCII character set and hyphens.
A single hyphen (`-`) is used to separate words in a name. A Platform
required extension is an exception (such as `.exe`).
Name CLI tools with more than three characters. Keep the short file names
available for user shortcuts (aliases). If you believe a tool should have
a very short name, request approval from the Fuchsia API Council.
Keeping the points above in mind:
- Prefer whole words rather than abbreviations.
- Prefer shorter names where a user is expected type the name frequently. For
less frequently typed names bias to more explicit names.
- Prefer a single word to multiple words
- Prefer subcommands to multiple tools that are hyphenated (e.g. avoid
`foo-start`, `foo-stop`, `foo-reset`; instead have `foo` that accepts
commands `start|stop|reset`).
- Prefer symmetry (particularly in verbs) with other similar commands or
sub-systems, unless that introduces a broken metaphor.
### Programming Languages
Tools may be written in C++, Rust, and Go. For clarity, here are some languages
not approved: Bash, Python, Perl, JavaScript, and Dart (see exceptions below).
No language is preferred between C++, Rust, and Go. The choice between these
languages is up to the author of the tool.
> **SDK**
>
> If a given flavor of SDK includes a specific language (e.g. Dart), that
> language may be used for tools that are distributed with that SDK. I.e. do not
> include a Dart tool in an SDK that wouldn't otherwise include the Dart
> runtime, but if it's already there, that's okay.
### Style Guides
Follow the corresponding [style guide](../README.md#languages) for the language
and area of Fuchsia being developed.
E.g. if the tool is included with Zircon and written
in C++, use the style guide for C++ in Zircon. Specifically, avoid creating a
separate style guide for tools.
### Runtime Link Dependencies
Try to minimize runtime link dependencies (statically link dependencies
instead). On Linux it is acceptable to runtime link against the glibc suite of
libraries (libm, etc.); other runtime link dependencies are not allowed.
### Building from Source
Keep in mind that some developers will want to build the tools from source. Use
the same build and dependency structure as the code in the Platform Source Tree.
Do not make a separate system to build tools.
## Host Platforms
Keep an eye on how resource heavy a tool becomes and what OSes it will be
expected to operate on.
### Run on a Variety of Hardware
Developer machines may range from a few CPU cores and moderate amount of RAM to
dozens of CPU cores and huge amounts of RAM. Don't assume that host machines are
very powerful or that a server cluster is available to offload work to.
### Supported OSes
This section is for the convenience of the reader. This document is not
authoritative on which platforms are supported.
We currently support
- Linux
- macOS
Tools written for developers must run on those platforms. There are other
platforms to consider, and while these are not required at this time, it's good
to keep the platforms listed below in mind.
Tools should be built in a way that makes them easy to port to the following
platforms:
- Fuchsia (self-hosted)
- Windows
This is not an exhaustive list, we may support others.
### Case Insensitive File Systems
Don't rely on case sensitivity in file paths. E.g. don't expect that `src/BUILD`
and `src/build` are different files. Conversely, don't rely on case
insensitivity since some platforms are case sensitive.
### Development Hosts Using a Non-English Locale
There are several aspects to consider for non-English developers:
- Whether the tool itself can be localized
- Whether the documentation for the tool can be localized
- Whether the tool can work with path names and data that include non-ASCII
- Whether the tool works correctly on non-English OSes
Tools are provided in US English. It's not required that a tool be localized.
(This may change in the future.)
The documentation for a tool will support non-ASCII characters. Both HTML and
Markdown can support Unicode (UTF-8) characters, so these are both good choices
for documentation. Doing the translation is not required, merely allow for the
possibility.
Tools will function properly with file paths that contain binary sequences and
white space. Use a library to work with file paths rather than manipulating
paths as strings. (e.g. path.Join in Go.)
Tools will operate correctly on non-English platforms (e.g. Japanese or French).
This means handling binary (e.g. UTF-8) data without corrupting it. E.g. don't
assume a text file is just ASCII characters.
## Execution
At runtime (or execution time) consider how the tool should behave.
### Optimize for No Work Needed
When appropriate, such as with a build tool, have the tool exit quickly if there
is no work to do. If possible, go one step better by providing information to
the caller about the dependencies so that the caller can accurately determine
whether the tool needs to be called at all.
### Command Line Arguments
There are three types of command line arguments:
- exact text
- arguments
- options (i.e. switches and keys)
#### Exact text
Exact text is placed as-is on the command line. A piece of exact text may be
required or optional. Parsing exact text arguments should be restricted to cases
where they are needed for disambiguation (i.e. for correctly parsing other
arguments). For example if a `copy` command accepted multiple source and
destination arguments, an exact text argument may be used to clarify which is
which: `copy a b c` may be ambiguous; while `copy a to b c` may indicate that
'`a`' is copied to two destinations.
#### Arguments
Arguments are like function parameters or slots for data to fit into. Often,
their order matters. In the example `copy <from> <destination>`, both `<from>`
and `<destination>` are ordered arguments. In cases where a single logical
argument is repeated the order may not matter, such as remove `<files>...` where
the tool might process the `<files>` in an arbitrary order.
#### Options
Some arguments are known as options. Both switches and keyed (key/value pairs)
are options. Options tend to modify the behavior of the tool or how the tool
processes parameter arguments. Options consist of a dash prefixed letter or
word.
Options must start with either one ('`-`') or two ('`--`') dashes followed by an
alphanumeric label. In the case of a single dash, the length of the label must
be 1. If the length of the label is two or more then two dashes must be used.
For example: `-v` or `--help` are correct; `-help` is not valid.
All choices are required to have a (`--`) option. Providing single character
shorthand (`-`) is optional. E.g. it's okay to provide just `--output`, or both
`-o` and `--output`, but it's not ok to only provide an `-o` option without a
long option as well.
Do not create numeric options, such as `-1` or `-2`. E.g. rather than having
`-1` mean to do something once, add a `--once` option. If a numeric value is
needed, make a keyed option, like `--repeat <number>`.
One (`-`) or two (`--`) dashes on their own are special cases and are not
allowed as a key or switch.
#### Switches
The presence of a switch means the feature it represents is 'on' while its
absence means that it is 'off'. Switches default to 'off'. Unlike keyed options,
a switch does not accept a value. E.g. `-v` is a common switch meaning verbose;
it doesn't take a value, making it switch rather than a keyed value.
All switches must be documented (hidden switches are not allowed).
Running switches together is not allowed. E.g. `-xzf` or `-vv`, each must be
separate: "`-x -z -f`" or "`-v -v`".
#### Keyed Options
Keyed options consist of a key and a value. Keys are similar in syntax to
switches except that a keyed option expects a value for the key.
E.g. `-o <my_output_file>` has a key '`-o`' and a value of '`my_output_file`'.
Do not use an equals punctuation (or similar) to separate the key and value.
E.g. do not do `-o=<my_output_file>`.
Note about a rare case: Avoid making optional keys (where the value appears
without its key) or optional values (where the key appears without its
value). It's clearer to consider the key/value pair optional, but inseparable.
I.e. if the key is present a value is required and vice versa. Consider making
an argument instead of a keyed option with an optional key. E.g. rather than
"`do-something [--config [<config_file>]]`" where not passing `[<config_file>]`
means don't use a config file; instead do
"`do-something [--config <config_file>|--no-config]`" where passing
`--no-config` means don't load a config file.
##### Mutually Exclusive Options
Some options don't make sense with other options. We call the options mutually
exclusive.
Passing mutually exclusive options is considered a user error. When this occurs
the tool will do one of the following:
- Write an error message explaining the issue and exit with a non-zero result
code; doing no work (i.e. there was no data changed as a result of the call).
This is the expected handling, so no further documentation or notes are
required.
- Prioritize one option over another. E.g. "`passing -z will override -y`". In
this case the handling will be documented in the `--help` output.
- Other handling is possible (first takes precedence or last takes precedence or
something else) though this is discouraged. In this case the handling will
be documented in the Description, Options, ***and*** Notes; though
"`See Notes`" may be used in Description and Options with the full write-up in
`Notes`.
##### Grouping Options
There is no specific syntax to indicate when enabling one option will also
affect another option. When an option implies that another option is enabled or
disabled, specify that in the Options. E.g. "`passing -e implies -f`" means that
if `-e` is enabled, `-f` will be enabled as if it were passed on the command
line (regardless of whether `-f` was explicitly passed). The redundant passing
of the implied value is harmless (not an error).
##### Option Delimiter
Two dashes ('`--`') on their own indicates the end of argument options. All
subsequent values are given to the tool as-is. For example, with
"`Usage: foo [-a] <file>`", the command line "`foo -- -a`" may interpret `-a` as
a file name rather than a switch. Further, "`foo -a -- -a`" enables the switch
`-a` (the first `-a`, before the `--`) and passes the literal text `-a` (the
second `-a`).
##### Repeating Options
Repeating switches may be used to apply more emphasis (what more emphasis means
is up to the tool, the description here is intentionally vague). A common
example is increasing verbosity by passing more `-v` switches.
Repeating keyed options may be used to pass multiple values to the same command.
Often this is done to avoid calling the same command multiple times. Common
commands that accept repeating options are `cp`, `rm`, `cat`. Care must be taken
to ensure that repeating commands are unambiguous and clear. E.g. `cp` always
interprets the last argument as the destination; if `cp` accepted multiple
source and destination arguments the parsing would become ambiguous or unclear.
#### Standard Input Alias
In Fuchsia tools a single dash (`-`) is not interpreted as an alias to stdin. Use
pipes to direct data into stdin or use `/dev/stdin` as an alias for stdin.
(Note: `/dev/stdin` is not available on Fuchsia or Windows).
#### Single Dash
A single dash ('-') on its own is reserved for future use.
#### Subcommands
Tools may contain sub-command that accept independent command line arguments.
(Similar to the `git` tool). Subcommands do not begin with any dashes. E.g. in
`fx build` the `build` argument is a subcommand.
When a tool has many subcommands, it should also have a help subcommand that
display help about other subcommands. E.g. "`fx help build`" will provide help
on the build subcommand.
Subcommands may have their own arguments that are not handled by the main tool.
Arguments between the tool name and the subcommand are handled by the tool and
arguments that follow the subcommand are handled by the subcommand. E.g. in
`fx -a build -b` the `-a` is an argument for the `fx` tool, while the `-b`
argument is handled by the `build` subcommand.
### Common Features
Command line tools are expected to support some common switches:
- `--help`
- `--quiet`
- `--verbose`
- `--version`
#### Interactive Help (--help)
A tool must accept a `--help` switch and provide usage information to the
command line in that case. The layout and syntax of the help text is described
in a future document.
The tool must not do other work (i.e. have side effects) when displaying help.
Use a library that can parse the arguments as well as present help information
from the same source. Doing so keeps the two in sync. I.e. avoid writing command
line help as an independent paragraph of text.
Keep the interactive help reasonably concise. Plan for a skilled reader, i.e.
someone looking for a reminder on how to use the tool or a developer experienced
in reading interactive help. For the novice, provide a note referring them to
the Markdown documentation.
Provide an option to generate machine parsable output.
#### Verbosity (--quiet and --verbose)
The `--quiet` and `--verbose` switches decrease or increase informational output
to the user. Their implementation is optional, but all tools will accept them as
arguments and must not use those terms for other purposes, e.g. don't use
`--quiet` to turn off the audio output (use `--silence` or `--volume 0` or some
other synonym).
#### Interactive Version (--version)
A tool must accept a `--version` switch and provide an indication of the code
used to build the tool in that case. The layout and syntax is not specified, but
the version will include a version number of some kind.
The tool must not do other work (have side effects) when reporting its version.
### Logging
Logging is distinct from normal output. The audience for logging is normally the
tool developer or a power user trying to debug an issue. Logging may go to
stdout in special cases, such as when `--verbose` output is requested.
Logging from multiple threads will not interlace words within a line, i.e. the
minimum unit of output is a full text line. Each line will be prefixed with an
indication of the severity of the line. The severity will be one of: detail,
info, warning, error, fatal.
## Metrics
Every tool must file a Privacy Design Document (PDD) in order to collect usage
metrics.
Metrics are important to drive quality and business decisions. Questions we want
to answer with metrics include:
- Which OS are our users using? - so we know how to prioritize work for various
platforms
- Which tools are they using? - so we know how to prioritize investments, and to
learn which workflows are currently being used so we can prioritize
investments or identify weak spots
- How often do they use a tool? - so we know how to prioritize investments, and
to learn which workflows are currently being used so we can prioritize
investments or identify weak spots
- Do our tools crash in the wild? How often? - so we know how to prioritize
maintenance of tools
- How do they use a tool? - assuming that a tool can do one or more things, we'd
like to learn how to prioritize investments in particular workflows of a tool
The type and content of the metrics collected must be carefully chosen. We will
go through the Google-standard PDD review process to ensure we are compliant
with Google's practices and policies. Tools must get approval on which metrics
are collected before collection.
## Configuration and Environment
Tools often need to know something about the context they are running. Let's
look at how that context should be gathered or stored.
#### Reading Information
Tools should not attempt to gather or intuit settings or other state directly
from the environment. Information such as an attached target's IP address, the
out directory for build products, or a directory for writing temporary files
will be gathered from a platform agnostic source. Separating out the code that
performs platform-specific work will allow tools to remain portable between
disparate platforms.
Where practical, configuration information should be stored in a way familiar to
the user of the host machine (e.g. on Windows, use the registry). Tools should
gather information from SDK files or platform-specific tools that encapsulate
the work of reading from the Windows registry, Linux environment, or Mac
settings.
Tools will be build-system agnostic as well. Accessing a common file such as
build input dependency file is okay.
#### Writing Information
Tools will not modify configuration or environment settings, except when the
tool is clearly for the purpose of modifying an expected portion of the
environment.
If modifying the environment outside of the tool's normal scope may help the
user, the tool may do so with the express permission of the user.
## Execution Success and Failure
Command line tools return an integer value in the range [0..127] when they exit.
A zero represents success (no error) and 1-127 are various forms of error. The
value 1 is used as a general error. Any values other than 0 and 1 that may be
returned must be documented for the user.
### Succeed with Grace
If there were no errors encountered, return a result code of zero.
Avoid producing unnecessary output on success. Don't print "succeeded" (unless
the user is asking for verbose output).
### If Something is Unclear, Stop
If the tool encounters an ambiguous situation or is in danger of corrupting
data, do not continue. E.g. if the path to the directory you're being asked to
delete comes back as just "`/`", there was likely an error trying to get that
configuration information, avoid 'soldiering on' and removing everything under
"`/`".
### Do Not Fail Silently
Tools must clearly indicate failure by returning a non-zero error code. If
appropriate (if it makes sense for the tool or if the user explicitly asked for
verbose output) print an error message explaining what went wrong.
### Provide Direction on Failure
When a tool execution fails, be clear about whether the error came from bad
inputs, missing dependencies, or bugs within the tool. Make error reports
comprehensible and actionable.
If the error came from bad inputs
1. If the user gave the tool bad data, give context about the error and guide
the user toward fixing the input, e.g. print which input file (and line
number if that's appropriate for the input) where the input error occurred.
- Prefer output that follows this format (for easy regex use):
`file_name:line:column:description`. This is a common format used by many
tools. Other formats are acceptable, but try to use something that is easy
for both humans and tools to parse.
2. Provide a reference to further information. E.g. if documentation is
available, provide a link to documentation about the tool in general or to
documentation regarding the specific error. If the tool has the capacity to
provide more details, describe that (like how `gn` can explain how to run the
tool to get more help).
If the error came from missing dependencies
1. Be clear that the error is from missing dependencies, i.e. don't leave the
user trying to debug their input data if that is not the issue.
2. Provide instruction on how to satisfy the dependencies. This can be an
example command to run (e.g. `apt-get install foo`) or a link to further
instructions (e.g. "`see: http:example.com/how-to-install-foo`").
If the error came from an unexpected state (i.e. a bug) in the tool
1. Apologize. Explain that the tool got into an unexpected state. Don't leave
the user trying to guess whether their input data was bad or they were
missing dependencies.
2. Suggest a mailing list or forum to get help. Help the user find out if the
bug is fixed in the next tool version; or someone has found a workaround.
3. Invite the user to enter a bug report and make that as easy as possible. E.g.
provide a link that goes to the bug database with the tool and platform
information prepopulated.
## Include Tests
Tools must include tests that guarantee its correct behavior. Include both unit
tests and integration tests with each tool. Tests will run in Fuchsia continuous
integration.
> **SDK**
>
> It's especially important that SDK tools imported from the Fuchsia build (pm,
> etc.) have tests that run in Fuchsia continuous integration because the SDK
> bot does not currently prevent breaking changes.
## Documentation
The Markdown documentation is the right place to put more verbose usage examples
and explanations.
> **SDK**
>
> All tools included in the SDK and intended to be executed directly by an end
> user must have a corresponding Markdown documentation file.
## User vs. Programmatic Interaction
A tool may be run interactively by a human user or programmatically via a script
(or other tool).
While each tool will default to interactive or non-interactive mode if they can
glean which is sensible, they must also accept explicit instruction to run in a
given mode (e.g. allow the user to execute the programmatic interface even if
they are running in an interactive shell).
### Stdin
For tools that are not normally interactive, avoid requesting user input
e.g. readline or linenoise). I.e. Don't suddenly put up an unexpected prompt to
ask the user a question.
For interactive tools (e.g. `zxdb`) prompting the user for input is expected.
### Stdout
When sending output to the user on stdout use proper spelling, grammar, and
avoid unusual abbreviations. If an unusual abbreviation is used, be sure it has
an entry in the [glossary.md](../../glossary.md).
Try to check for output to terminal, i.e. see if a user is there or whether the
receiver is a program.
#### ANSI Color
Use of color is allowed with the following caveats
- Enabling/disabling color output based on terminal information (i.e. whether it
supports color) is encouraged, but that's not always possible (so it's not
required)
- Always allow the user to override color use (they can disable it)
- When using color, be sure to use colors that are distinct for readers who may
not be able to see a full range of color (e.g. color-blindness).
- Never rely on color to convey information. Only use color as an enhancement.
Seeing the color must not be needed for correct interpretation of the output.
### Stderr
Use stderr for reporting invalid operation (diagnostic output) i.e. when the
tool is misbehaving. If the tool's purpose is to report issues (like a linter,
where the tool is not failing) output those results to stdout instead of stderr.
See Success and Failure for more information on reporting errors.
### Full-Screen
Avoid creating full-screen terminal applications. Use a GUI application for such
a tool.
### Non-interactive (Programmatic)
Include a programmatic interface where reasonable to allow for automation.
If there is an existing protocol for that domain, try to follow suit (or have a
good reason not to). Otherwise consider using manifest or JSON files for
machine input.
### IDE (Semi-Programmatic)
Allow for tools to be used by an Integrated Development Environment. This
generally involves accepting a manifest for input and generating a manifest.
### Interactive (User)
Interacting with the user while the tool is running is an uncommon case for many
tools. Some tools may run interactively as an option, e.g. `rm -i` will prompt
the user before each removal.
## State Files
State files encode information for data sharing between tools. PID file and lock
files are examples of state files.
Avoid using a PID file to contain the process ID of a running executable.
Avoid using a lock file to manage mutual exclusion of resource access (i.e. a
mutex).