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.
Some sections have an “SDK” call-out, like this one. These detail specific rules that apply to tools included with the SDK 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.
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
.cmxfiles, which happen to use the JSON format, would be okay.
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 mod 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, i.e. like
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-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).
For SDK build integrators, separate tools. The build integrators will learn each and piece them together to make a working system.
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.
No language is preferred between C++, Rust, and Go. The choice between these languages is up to the author of the tool.
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.
Follow the corresponding style guide 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.
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 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
<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:
--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
--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
-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
-) 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.
-vv, each must be separate: “
-x -z -f” or “
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 ‘
Do not use an equals punctuation (or similar) to separate the key and value. E.g. do not do
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
See Notes” may be used in Description and Options with the full write-up in
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
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
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
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
Command line tools are expected to support some common switches:
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.
--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
--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 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.
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.
gncan 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 (e.g. “
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.
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.
The Markdown documentation is the right place to put more verbose usage examples nd explanations.
All tools included in the SDK 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). 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.
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.
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
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).