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:

  1. import("//src/developer/ffx/build/ffx_plugin.gni")
  2. ffx_plugin("ffx_example") {
  3. version = "0.1.0"
  4. edition = "2018"
  5. with_unit_tests = true
  6. deps = []
  7. sources = [
  8. "src/args.rs",
  9. "src/lib.rs",
  10. ]
  11. }

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:

  1. use {argh::FromArgs, ffx_core::ffx_command};
  2. #[ffx_command()]
  3. #[derive(FromArgs, Debug, PartialEq)]
  4. #[argh(subcommand, name = "example", description = "an example")]
  5. 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:

  1. $ 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:

  1. use {argh::FromArgs, ffx_core::ffx_command};
  2. #[ffx_command()]
  3. #[derive(FromArgs, Debug, PartialEq)]
  4. #[argh(subcommand, name = "example", description = "an example")]
  5. pub struct ExampleCommand {
  6. #[argh(positional)]
  7. /// example optional positional string parameter
  8. pub example: Option<String>,
  9. }

See more documentation:

Plugin

Next, you will define the plugin execution code. Create a file src/lib.rs:

  1. use {
  2. anyhow::Result,
  3. ffx_core::ffx_plugin,
  4. ffx_example_args::ExampleCommand,
  5. };
  6. #[ffx_plugin()]
  7. pub async fn example(_cmd: ExampleCommand) -> Result<()> {
  8. println!("Hello from the example plugin :)");
  9. Ok(())
  10. }

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 “_lib_test” for unit tests if the 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:

  1. plugin_deps = [
  2. "//path/to/your/plugin/dir:ffx_example",
  3. ...
  4. ]

And that’s it! Build ffx:

  1. $fx build ffx

You should now see the output when you run your example:

  1. $ fx ffx example
  2. Hello from the example plugin :)

If your lib.rs contains tests, they can be invoked via:

  1. $ 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 “_args_lib_test”. For this example it would be “ffx_example_args_lib_test”.