将现有插件迁移到子工具

为何迁移

新的 FHO 子工具库提供了一些有用的宏,可帮助您将现有插件迁移到新的子工具接口。将插件迁移到子工具有以下好处:

  • 错误的接口边界更加清晰。
  • 类型安全的机器 writer 输出并最终进行架构验证。
  • 可以更灵活地从紫红和 ffx 环境获取子工具的输入类型(请参阅上面链接的 FHO 文档)。
  • 更少的宏观魔力。
  • 如果与 ffx 本身分开构建,则构建时间更短。

请注意,如果您要启动一个新的子工具,可以跳过旧版插件系统涉及的许多步骤,因此您应该按照使用入门说明进行操作。

非递归插件

对于不含子插件的插件,如果其 ffx_plugin() 操作中没有 plugin_deps 部分,则迁移起来相对简单。迁移具有子插件的插件被视为一项单独的任务。

以下说明主要基于最初编写的 ffx daemon echo 插件与在开发新的子工具接口时用作概念验证的 ffx echo 子工具之间的差异。

迁移 Rust lib.rs

假设插件 lib.rs 文件如下所示:

use anyhow::Result;
use ffx_core::ffx_plugin;
use ffx_echo_args::EchoCommand;
use ffx_writer::Writer;
use fidl_fuchsia_developer_ffx::EchoProxy;

#[ffx_plugin(EchoProxy = "daemon::protocol")]
pub async fn echo(echo_proxy: EchoProxy, cmd: EchoCommand, #[ffx(machine = String)] mut writer: Writer) -> Result<()> {
    // implementation here
    Ok(())
}

如要迁移到新插件系统,最简单的方法是移除 #[ffx_plugin] 宏,然后添加以下基于宏的派生代码:

use fho::{FfxTool, FfxMain, MachineWriter, Result};
use ffx_echo_args::EchoCommand;
use fidl_fuchsia_developer_ffx::EchoProxy;

#[derive(FfxTool)]
pub struct EchoTool {
    #[command]
    cmd: EchoCommand,
    #[with(fho::daemon_protocol())]
    echo_proxy: ffx::EchoProxy,
}

#[async_trait(?Send)]
impl FfxMain for EchoTool {
    type Writer = MachineWriter<String>;
    async fn main(self, writer: MachineWriter<String>) -> Result<()> {
        // implementation here
        Ok(())
    }
}

与前面的宏不同,在命名 EchoTool 结构体的成员时没有任何限制,如果您想执行更复杂的操作,甚至可以通过从 FHO 派生一个特征来实现自己的加载器。

main() 函数被赋予此结构体和写入器作为移动对象,因此您可以随意解构它们。

另请注意,Result 类型来自 FHO,而不是 anyhow。在此边界上,现在有一种特定的错误类型,其中包含有关错误是否可由用户呈现以及如何显示的信息。如需详细了解如何处理此错误类型及其与 ffx_errorffx_bail 宏的交互,请参阅错误

目前,大多数迁移的插件仍然需要添加到主 ffx 二进制文件中,这意味着您还需要添加以下宏调用,以生成旧版插件入口点:

fho::embedded_plugin!(EchoTool);

添加 main.rs

由于您的子工具现在可以独立运行,因此您需要添加一个简单的 main.rs,它会调用 FHO 和您的插件库,以便在 ffx 调用它时正常运行:

use ffx_tool_echo::EchoTool;
use fho::FfxTool;

#[fuchsia_async::run_singlethreaded]
async fn main() {
    EchoTool::execute_tool().await
}

一般来说,main.rs 不会比这更复杂。

BUILD.gn(用于顶级插件)

对于仍需要与现有 ffx 插件系统集成的现有插件,这些插件将继续使用 ffx_plugin GN 操作,因为它会正确设置所有依赖项以包含在 ffx 二进制文件中。

如果插件是 ffx 的顶级子命令,那么我们还将添加 ffx_tool() 操作,以构建单独编译的插件:

# Existing import for the plugin action
import("//src/developer/ffx/build/ffx_plugin.gni")
# New import for the ffx_tool action.
import("//src/developer/ffx/build/ffx_tool.gni")

ffx_plugin("ffx_echo") {
  version = "0.1.0"
  edition = "2021"
  with_unit_tests = true

  args_sources = [ "src/args.rs" ]

  sources = [ "src/lib.rs" ]

  deps = [ "//src/developer/ffx/fidl:fuchsia.developer.ffx_rust" ]

  test_deps = [ "//src/lib/fuchsia-async" ]
}

# This will generate the executable file for your plugin.
ffx_tool("ffx_echo_tool") {
  edition = "2021"
  output_name = "ffx-echo"
  deps = [
    ":ffx_echo",
    "//src/developer/ffx/lib/fho:lib",
    "//src/lib/fuchsia-async",
  ]
  sources = [ "src/main.rs" ]

  # To be included in the sdk in the future, add this:
  sdk_category = "partner"
  sdk_target_name = "ffx_echo_tool_sdk"
}

向 SDK 添加单独的子工具构建

此时,您可以构建子工具,但不会主动构建。为此,您需要将其添加到 ffx config.gni 中的“双模式插件”列表中:

# ...snip...
dual_mode_plugins = [
  # ...
  "//path/to/your/plugin:ffx_echo_tool",
  # ...
]
# ...snip...

现在,运行 fx build ffx 应该会重新构建 ffx 和您的插件,如果运行 ffx commands,您应该能够在“Workspace Commands”列表中看到您的命令。