blob: b286514edfe06767fe9384b71f523cee4f38a9f4 [file] [log] [blame] [view]
# 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".