| # 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](plugin-internals.md) 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](https://fuchsia.googlesource.com/fuchsia/+/HEAD/src/developer/ffx/build/ffx_plugin.gni). |
| |
| Your BUILD.gn file should look something like this: |
| |
| ```GN |
| 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`: |
| |
| ```rust |
| 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](https://docs.rs/argh/0.1.3/argh/) crate and more |
| documentation can be found [here](https://docs.rs/argh/0.1.3/argh/). This |
| struct has been decorated by the `ffx_command` attribute which signifies that |
| your plugin should run when someone types the following command: |
| |
| ```sh |
| $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: |
| |
| ```rust |
| 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: |
| - [Argh](https://docs.rs/argh/0.1.3/argh/) |
| |
| ## Plugin |
| |
| Next, you will define the plugin execution code. Create a file `src/lib.rs`: |
| |
| ```rust |
| 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](fuchsia.dev/fuchsia-src/development/languages/rust/testing) |
| on a host. The `ffx_plugin` GN template will generate a library name |
| "<target_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](https://fuchsia.googlesource.com/fuchsia/+/HEAD/src/developer/ffx/BUILD.gn#25) |
| 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: |
| |
| ```GN |
| plugin_deps = [ |
| "//path/to/your/plugin/dir:ffx_example", |
| ... |
| ] |
| ``` |
| |
| And that's it! Build ffx: |
| |
| ```sh |
| $fx build ffx |
| ``` |
| |
| You should now see the output when you run your example: |
| |
| ```sh |
| $fx ffx example |
| Hello from the example plugin :) |
| ``` |
| |
| Plugins can also use FIDL proxies to communicate with a target device through |
| Overnet: |
| |
| - [Continue to next example](proxy-plugin.md) |
| |
| 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](plugin-experimental.md) 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](plugin-internals.md) 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 "<target_name>_args_lib_test". For this example it would be |
| "ffx_example_args_lib_test". |
| |
| |
| |