本页面介绍了为 ffx
创建插件的基本步骤。
插件系统采用 GN 构建规则和 Rust 属性的组合,将插件代码与 ffx
内部构件分离开来。
GN build 规则
使用项目的 BUILD.gn
文件中的 ffx_plugin()
构建规则模板,为您的插件创建构建目标。
您的 BUILD.gn
文件应类似于以下示例:
import("//src/developer/ffx/build/ffx_plugin.gni")
ffx_plugin("ffx_example") {
version = "0.1.0"
edition = "2021"
with_unit_tests = true
deps = []
args_sources = [
"src/args.rs",
]
sources = [
"src/lib.rs",
]
}
在 src/
目录内,项目应包含两个源文件:
src/args.rs
:定义插件的 CLI 参数。src/lib.rs
:包含主插件源代码实现。
参数
创建包含插件支持的参数的文件 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 {}
这会用到 argh crate;如需更多文档,请点击此处。此结构已由 ffx_command
属性修饰,该属性表示当有人输入以下命令时,您的插件应运行:
fx ffx example
如果要为插件添加更多参数,请将这些参数添加到此结构体中。
下面是一个参数示例:
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>,
}
查看更多文档: - Argh
插件
创建包含插件实现的文件 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(())
}
插件方法需要接受在 src/args.rs
文件中创建的 argh 命令作为参数,即使它们不使用它们也是如此。
集成
将插件库作为依赖项添加到 ffx
中,以将其包含在 build 中。修改 ffx
build 目标中的 plugin_deps
数组,以将 ffx_plugin()
目标添加到顶层:
plugin_deps = [
"//path/to/your/plugin/dir:ffx_example",
...
]
如需构建和测试插件,请构建 ffx
:
fx build ffx
现在,当您运行示例命令时,应该会看到输出:
$ fx ffx example
Hello from the example plugin :)
单元测试
如果要对插件进行单元测试,只需遵循在主机上测试 rust code 的标准方法即可。当 with_unit_tests
参数设置为 true
时,ffx_plugin()
GN 模板会为单元测试生成 <target_name>_lib_test
库目标。
如果您的 lib.rs
包含测试,可以使用 fx test
调用这些测试:
fx test ffx_example_lib_test
如果 fx 测试没有找到您的测试,请检查产品配置是否包含您的测试。您可以使用以下命令添加所有 ffx 测试:
fx set ... --with=//src/developer/ffx:tests
FIDL 协议
FFX 插件可以通过 Overnet 使用 FIDL 协议与目标设备通信。如需从插件访问 FIDL 协议,请按照本部分中的说明操作。
将 FIDL Rust 绑定作为依赖项添加到插件的
BUILD.gn
文件中。 以下示例添加了fuchsia.device
FIDL 库的绑定:import("//src/developer/ffx/build/ffx_plugin.gni") ffx_plugin("ffx_example") { version = "0.1.0" edition = "2021" with_unit_tests = true deps = [ "//sdk/fidl/fuchsia.device:fuchsia.device_rust", ] args_sources = [ "src/args.rs", ] sources = [ "src/lib.rs", ] }
在插件实现中导入必要的绑定。以下示例从
)的 Rust 绑定过程中生成的fuchsia.device
导入NameProviderProxy
:use { anyhow::Result, ffx_core::ffx_plugin, ffx_example_args::ExampleCommand, fidl_fuchsia_device::NameProviderProxy, };
添加可在插件实现中使用的 FIDL 代理。插件可以接受参数列表中的代理:
pub async fn example( name_proxy: NameProviderProxy, _cmd: ExampleCommand, ) -> Result<()> { }
将代理类型映射到组件选择器,该组件选择器表示在
ffx_plugin()
注解中提供 FIDL 协议的组件:#[ffx_plugin( NameProviderProxy = "bootstrap/device_name_provider:out:fuchsia.device.NameProvider" )]
现在,src/lib.rs
中的插件实现示例应如下所示:
use {
anyhow::Result,
ffx_core::ffx_plugin,
ffx_example_args::ExampleCommand,
fidl_fuchsia_device::NameProviderProxy,
};
#[ffx_plugin(
NameProviderProxy = "bootstrap/device_name_provider:out:fuchsia.device.NameProvider"
)]
pub async fn example(
name_proxy: NameProviderProxy,
_cmd: ExampleCommand,
) -> Result<()> {
if let Ok(name) = name_proxy.get_device_name().await? {
println!("Hello, {}", name);
}
Ok(())
}
重复上述步骤,将其他 FIDL 代理添加到您的 ffx
插件。
以下 FIDL 代理内置于 ffx
中,不需要额外的依赖项或映射:
只需将上述代理添加到插件的参数列表中,即可在实现中访问它们。
代理名称映射
ffx
和远程控制服务 (RCS) 提供了一种机制,用于在表示给定 FIDL 代理的别名发生更改时,与 ffx
插件使用的现有名称保持兼容性。例如:
- FIDL 代理由新组件提供
- FIDL 协议名称变更
- 代理名称因产品版本而异
RCS 支持使用别名映射来实现这一点,此类映射会覆盖 ffx
插件的来源中定义的别名,并将其映射到其他值。如需替换给定名称,请按以下格式向 //src/developer/remote-control/data/moniker-map.json
添加一个条目:
{
...
"original/moniker:out:fuchsia.MyService": "some/new/moniker:expose:fuchsia.MyOtherService"
}
此示例允许 RCS 替换对 ffx
插件中 original/moniker:out:fuchsia.MyService
的引用,并将其路由到包含该映射的任何 build 中的 some/new/moniker:expose:fuchsia.MyOtherService
。