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.
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.
IDK
Some sections have an “IDK” call-out, like this one. These detail specific rules that apply to tools included with the Fuchsia Integrator Development Kit distribution.
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.
IDK
IDK 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 IDK. 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.
ffx
ffx is Fuchsia's unified CLI tool platform for host to target interactions. It provides a logical subcommand based grouping that maps to high-level Fuchsia workflows. It also provides a plugin framework to allow contributors to expand the
ffx
command surface.ffx
is distributed as part of the Fuchsia IDK.
Tools may be used for different development tasks. On a large team these roles may be separate people. Some categories are:
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 component development may expect tools to integrate with their Integrated Development Environment (IDE), while a build integration tool may be called from a script.
Prefer to put related commands under a common tool, such as ffx
. As an example, git
, ffx
, or fx
present many features (or, “sub-tools”) under a single user-facing command. This helps encourage the team toward a shared workflow and provides a single 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.
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:
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).
IDK
For development environment integrators and EngProd teams, separate tools. The build integrators will learn each and piece them together to make a working system.
ffx
ffx
introduces many subgroupings and related subcommands. In general for tools that fall in the categories such as host to target interaction, system integration, and publishing, prefer extending the existingffx
service instead of a new standalone tool. This can be accomplished by extendingffx
via additional flags, options or subcommands to take advantage of shared code and functionality. For considerations and additional details refer to theffx
development overview.
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.
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.
ffx
ffx
follows the rubric and conventions laid out below and provides a reference implementation for the outlined recommendations.
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 letters (a-z
) 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:
foo-start
, foo-stop
, foo-reset
; instead have foo
that accepts commands start|stop|reset
).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.
IDK
If a SDK that is an integration of the Fuchsia IDK includes a specific language (e.g. Dart), that language may be used for tools that are distributed with that SDK. In other words, do not include a Dart tool in a SDK that wouldn‘t otherwise include the Dart runtime, but if it’s already there, that's okay.
Follow the corresponding style guide for the language and area of Fuchsia being developed. For example, 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.
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.
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.
Keep an eye on how resource heavy a tool becomes and what OSes it will be expected to operate on.
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.
This section is for the convenience of the reader. This document is not authoritative on which platforms are supported.
We currently support
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:
This is not an exhaustive list, we may support others.
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.
There are several aspects to consider for non-English developers:
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.
At runtime (or execution time) consider how the tool should behave.
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.
There are three types of command line arguments:
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 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.
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.
For option names with more than one word (for example, “foo bar”), you must use a single dash (‘-
’) between words. For example, “foo bar” becomes --foo-bar
.
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.
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 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.
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:
passing -z will override -y
”. In this case the handling will be documented in the --help
output.See Notes
” may be used in Description and Options with the full write-up in Notes
.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).
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 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.
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).
A single dash (‘-’) on its own is reserved for future use.
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.
Command line tools are expected to support some common switches:
--help
--quiet
--verbose
--version
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 CLI tool help requirements.
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.
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).
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 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.
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:
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.
Tools often need to know something about the context they are running. Let's look at how that context should be gathered or stored.
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 independent 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 unbiased towards any build system or environment as well. Accessing a common file such as build input dependency file is okay.
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.
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.
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 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 “/
”.
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.
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
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.gn
can explain how to run the tool to get more help).If the error came from missing dependencies
apt-get install foo
) or a link to further instructions (see: http:example.com/how-to-install-foo
).If the error came from an unexpected state (i.e. a bug) in the tool
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.
IDK
It's especially important that IDK tools imported from the Fuchsia build (pm, etc.) have tests that run in Fuchsia continuous integration because the IDK bot does not currently prevent breaking changes.
ffx The
ffx
platform provides a framework for introducing tests that are run automatically in Fuchsia continuous integration. Contributors can see examples of plugin tests and end-to-end self-tests in theffx
source.
The Markdown documentation is the right place to put more verbose usage examples and explanations.
IDK
All tools included in the IDK and intended to be executed directly by an end user must have a corresponding Markdown documentation file.
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).
For tools that are not normally interactive, avoid requesting user input e.g. readline or linenoise). 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.
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.
Try to check for output to terminal, i.e. see if a user is there or whether the receiver is a program.
Use of color is allowed, with the following caveats:
--no-color
flag and/or by setting the NO_COLOR
environment variable (no-color.org).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.
Avoid creating full-screen terminal applications. Use a GUI application for such a tool.
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.
Allow for tools to be used by an Integrated Development Environment. This generally involves accepting a manifest for input and generating a manifest.
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 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).