Developing for FFX
This page will walk you through how to create a plugin for FFX. If you are looking for a more advanced look into the internals of FFX plugins, visit Plugin Internals page.
The plugin system employs a combination of GN build rules and Rust attributes to decouple plugin code from FFX internals.
First, create a directory to store your plugin. Next, create a BUILD.gn in that directory.
GN Build Rule
You will need to create a GN build target for your plugin. You”ll need to use
to use the ffx_plugin
build rule template defined
here.
Your BUILD.gn file should look something like this:
import("//src/developer/ffx/build/ffx_plugin.gni")
ffx_plugin("ffx_example") {
version = "0.1.0"
edition = "2018"
with_unit_tests = true
deps = []
sources = [
"src/args.rs",
"src/lib.rs",
]
}
Next, create a “src” directory to store your code in the same directory as your
BUILD.gn file (ffx_plugin
wraps the rustc_library
build template - so if you
are familiar with that template, you should be familiar with this template).
Args
Inside the “src” directory, there needs to be two files. The first file will
define the CLI params for your plugin. Create a file src/args.rs
:
use {argh::FromArgs, ffx_core::ffx_command};
#[ffx_command()]
#[derive(FromArgs, Debug, PartialEq)]
#[argh(subcommand, name = "example", description = "an example")]
pub struct ExampleCommand {}
This uses the argh crate and more
documentation can be found here. This
struct has been decorated by the ffx_command
attribute that signifies that
your plugin should run when someone types the following command:
$ fx ffx example
If you want to add more parameters for your plugins, you add them to this struct.
An example parameter would look like this:
use {argh::FromArgs, ffx_core::ffx_command};
#[ffx_command()]
#[derive(FromArgs, Debug, PartialEq)]
#[argh(subcommand, name = "example", description = "an example")]
pub struct ExampleCommand {
#[argh(positional)]
/// example optional positional string parameter
pub example: Option<String>,
}
See more documentation:
Plugin
Next, you will define the plugin execution code. Create a file src/lib.rs
:
use {
anyhow::Result,
ffx_core::ffx_plugin,
ffx_example_args::ExampleCommand,
};
#[ffx_plugin()]
pub async fn example(_cmd: ExampleCommand) -> Result<()> {
println!("Hello from the example plugin :)");
Ok(())
}
Plugin methods need to accept the argh command created in the src/args.rs
file as a parameter even if they do not use them.
If you want to unit tests your plugin, just follow the standard method for
testing rust code
on a host. The ffx_plugin
GN template will generate a library name
“with_unit_tests
parameter is
set to true
.
Lastly, you”ll need to add the plugin as a dependency to FFX to include it in
the build. You”ll need to edit this
file
to add your ffx_plugin
target to the top level. Alternatively, you can add your
plugin to the dependency list of any other plugin to create a
subcommand.
You just need to add the plugin library as a dependency. It should look something like this:
plugin_deps = [
"//path/to/your/plugin/dir:ffx_example",
...
]
And that’s it! Build ffx:
$fx build ffx
You should now see the output when you run your example:
$ fx ffx example
Hello from the example plugin :)
If your lib.rs
contains tests, they can be invoked via:
$ fx test ffx_example_lib_test
Plugins can also use FIDL proxies to communicate with a target device through Overnet:
Note: If this is the start of a larger project, introduce the new plugin by marking it experimental. This will allow for work-in-progress commits and allow others to try out your plugin by opting-in. See more at the Experimental Plugins page.
You may notice that we imported the ExampleCommand in the src/lib.rs
via a
library that was automatically generated by the ffx_plugin
template,
“ffx_example_args”. The ExampleCommand struct gets compiled into its own
library due to the internal dependency graph of FFX which you can read more
about in the Plugin Internals page. This is why there
must be two different files for the two parts of the plugin. The name of this
library will always be the name of your plugin library concatenated with
“_args”. If you want to run the unit tests for this library, the test libraries
name will be “