开发 ffx 插件

本页面介绍了为 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 协议,请按照本部分中的说明操作。

  1. 将 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",
      ]
    }
    
  2. 在插件实现中导入必要的绑定。以下示例从 fuchsia.device 导入 NameProviderProxy

    )的 Rust 绑定过程中生成的
    use {
        anyhow::Result,
        ffx_core::ffx_plugin,
        ffx_example_args::ExampleCommand,
        fidl_fuchsia_device::NameProviderProxy,
    };
    
  3. 添加可在插件实现中使用的 FIDL 代理。插件可以接受参数列表中的代理:

    pub async fn example(
        name_proxy: NameProviderProxy,
        _cmd: ExampleCommand,
    ) -> Result<()> { }
    
  4. 将代理类型映射到组件选择器,该组件选择器表示在 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