開始開發 ffx 子工具

FFX 子工具是 ffx cli 可執行的頂層指令。這些指令可以直接編譯至 ffx 和/或建構為可在建構輸出目錄或 SDK 中找到的個別指令,然後使用 FHO 工具介面叫用。

放置地點

首先,請在 fuchsia.git 樹狀結構的某處建立目錄,用於儲存子工具。目前子工具位於以下位置:

  • ffx 外掛程式樹狀結構,其中僅內建且 混合型外掛程式/子工具 go新的子工具通常不應放在這裡。
  • ffx 工具樹狀結構,這是專門用於外部執行的子工具。將子工具放在這個位置,ffx 維護者就能更輕鬆地協助處理任何問題,或在 ffx 與工具之間的介面有任何變更時更新子工具。如果將說明放在這裡,但 FFX 團隊不是這項工具的主要維護人員 您必須OWNERS 檔案存放在加入 因此能瞭解團隊的元件和部分個別擁有者,因此說明如何將問題分類 你的工具。
  • 專案樹狀結構中的某處。如果 ffx 工具 只是用於現有程式的包裝函式,但建立後,您必須 將您的 OWNERS 檔案設定成 FFX 團隊可以核准 與 ffx 互動的部分方法是新增 file:/src/developer/ffx/OWNERSOWNERS 檔案

除非在外掛程式中導入新工具,否則將決定特定位置 可能需要與工具團隊討論,決定最適合的場所。

哪些檔案

確定要導入工具的位置後,請建立來源 檔案。最佳做法是將工具的程式碼拆分為實作項目的程式庫,以及只呼叫該程式庫的 main.rs

下列檔案集是正常的起點:

BUILD.gn
src/lib.rs
src/main.rs
OWNERS

當然,你當然也可以視需要將項目拆分為多個程式庫。請注意,這些範例皆以 echo 子工具範例為基礎,但為了簡化內容,部分內容可能已移除或簡化。如果這個目錄中的任何內容無法運作或不清楚,請查看該目錄中的檔案。

BUILD.gn

以下是簡單子工具的 BUILD.gn 檔案範例。注意事項 因此,如果你習慣使用舊版外掛程式介面,ffx_tool 動作就不會 定義資料庫結構 或執行真正複雜的工作這個原則相當公平 rustc_binary 動作周圍的簡易包裝函式,但已加上一些額外目標 產生中繼資料、產生主機工具,以及產生 SDK Atom。

# Copyright 2022 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/host.gni")
import("//build/rust/rustc_library.gni")
import("//src/developer/ffx/build/ffx_tool.gni")
import("//src/developer/ffx/lib/version/build/ffx_apply_version.gni")

rustc_library("lib") {
  # This is named as such to avoid a conflict with an existing ffx echo command.
  name = "ffx_tool_echo"
  edition = "2021"
  with_unit_tests = true

  deps = [
    "//src/developer/ffx/fidl:fuchsia.developer.ffx_rust",
    "//src/developer/ffx/lib/fho:lib",
    "//third_party/rust_crates:argh",
    "//third_party/rust_crates:async-trait",
  ]

  test_deps = [
    "//src/lib/fidl/rust/fidl",
    "//src/lib/fuchsia",
    "//src/lib/fuchsia-async",
    "//third_party/rust_crates:futures-lite",
  ]

  sources = [ "src/lib.rs" ]
}

ffx_tool("ffx_echo") {
  edition = "2021"
  output_name = "ffx-echo"
  deps = [
    ":lib",
    "//src/developer/ffx/lib/fho:lib",
    "//src/lib/fuchsia-async",
  ]
  sources = [ "src/main.rs" ]
}

group("echo") {
  public_deps = [
    ":ffx_echo",
    ":ffx_echo_host_tool",
  ]
}

group("bin") {
  public_deps = [ ":ffx_echo_versioned" ]
}

group("tests") {
  testonly = true
  deps = [ ":lib_test($host_toolchain)" ]
}

main.rs

主要 rust 檔案通常較為簡單,只需透過 正確類型來做為進入點,ffx 知道如何 取代為:

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use ffx_tool_echo::EchoTool;
use fho::FfxTool;

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

lib.rs

這是工具主要程式碼的儲存位置。您將設定一個 用於指令引數及衍生 FfxToolFfxMain 的 argh 結構體 其導入方式必須保留工具執行時的背景脈絡。

引數

#[derive(ArgsInfo, FromArgs, Debug, PartialEq)]
#[argh(subcommand, name = "echo", description = "run echo test against the daemon")]
pub struct EchoCommand {
    #[argh(positional)]
    /// text string to echo back and forth
    pub text: Option<String>,
}

這個結構體會定義子工具在子指令名稱後方所需的任何引數。

工具結構

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

這樣的結構可保留工具所需的背景資訊。包括上述定義的引數結構、您可能需要的守護程式或裝置的任何 Proxy,或是您可能自行定義的其他項目。

這個結構體中必須包含參照上述引數類型的元素,且應具有 #[command] 屬性,以便為 FfxTool 實作設定正確的關聯類型。

這個結構中的任何項目都必須實作 TryFromEnv 或具有 #[with()] 註解,指向函式,該函式會傳回實作 TryFromEnvWith。您也可以使用 fho 程式庫或其他 ffx 程式庫

此外,工具上方的 #[check()] 註解會使用 CheckEnv 的實作項目,驗證指令應在未為結構體本身產生項目的情況下執行。這裡的 AvailabilityFlag 會檢查實驗狀態,並在未啟用實驗功能時提早退出。撰寫新的 子指令,因此應在其中加入這項宣告,以免員工 之後才會擴大運用

FIDL 通訊協定

FFX 子工具可以透過 FIDL 通訊協定與目標裝置通訊,包括 Overnet:如要從子工具存取 FIDL 通訊協定,請按照下列步驟操作:

  1. 將 FIDL Rust 繫結項目新增為子工具的 BUILD.gn 檔案依附元件。以下範例會為 fuchsia.device FIDL 程式庫新增繫結:

      deps = [
        "//sdk/fidl/fuchsia.device:fuchsia.device_rust",
      ]
    
  2. 將必要的繫結匯入子工具實作項目。下列 範例會從 fuchsia.device 匯入 NameProviderProxy

    use fidl_fuchsia_device::NameProviderProxy;
    
  3. 宣告工具結構的成員欄位。由於 FIDL 代理程式會實作 TryFromEnv 特徵,FHO 架構會為您建立並初始化欄位。

    #[derive(FfxTool)]
    pub struct EchoTool {
      #[command]
      cmd: EchoCommand,
      name_proxy: NameProviderProxy,
    }
    

FfxMain 實作

#[async_trait(?Send)]
impl FfxMain for EchoTool {
    type Writer = MachineWriter<String>;
    async fn main(self, mut writer: Self::Writer) -> Result<()> {
        let text = self.cmd.text.as_deref().unwrap_or("FFX");
        let echo_out = self
            .echo_proxy
            .echo_string(text)
            .await
            .user_message("Error returned from echo service")?;
        writer.item(&echo_out)?;
        Ok(())
    }
}

您可以在這裡實作實際的工具邏輯。您可以為 Writer 關聯特徵指定類型,系統會根據執行 ffx 的內容,透過 TryFromEnv 為該類型進行初始化。大多數的新子工具應使用 MachineWriter<> 類型,指定比上述 String 範例更不通用的類型,但實際情況會因工具而異。日後 但所有新工具都必須實作機器介面。

此外,這個函式的結果類型預設為使用 fho Error 類型。 可區分因使用者造成的錯誤 發生非預期的互動和錯誤如需進一步瞭解,請參閱「錯誤」文件。

單元測試

如果您想對子工具進行單元測試,只要按照標準方法在主機上測試Rust 程式碼即可。ffx_plugin() GN 範本 產生單元測試時,產生 <target_name>_lib_test 程式庫目標 with_unit_tests 參數已設為 true

如果 lib.rs 包含測試,則可使用 fx test 叫用:

fx test ffx_example_lib_test

如果 fx 測試找不到您的測試,請檢查產品設定是否包含您的測試。您可以使用下列指令納入所有 ffx 測試:

fx set ... --with=//src/developer/ffx:tests

在測試中使用偽造的 FIDL Proxy

測試子工具的常見模式,就是為 FIDL 通訊協定建立假的 Proxy。這樣一來,您就能透過呼叫 Proxy 傳回各種結果,而無須實際處理整合測試的複雜性。

    fn setup_fake_echo_proxy() -> ffx::EchoProxy {
        let (proxy, mut stream) =
            fidl::endpoints::create_proxy_and_stream::<ffx::EchoMarker>().unwrap();
        fuchsia_async::Task::local(async move {
            while let Ok(Some(req)) = stream.try_next().await {
                match req {
                    ffx::EchoRequest::EchoString { value, responder } => {
                        responder.send(value.as_ref()).unwrap();
                    }
                }
            }
        })
        .detach();
        proxy
    }

接著在單元測試中使用這個假 Proxy

    #[fuchsia::test]
    async fn test_regular_run() {
        const ECHO: &'static str = "foo";
        let cmd = EchoCommand { text: Some(ECHO.to_owned()) };
        let echo_proxy = setup_fake_echo_proxy();
        let test_stdout = TestBuffer::default();
        let writer = MachineWriter::new_buffers(None, test_stdout.clone(), Vec::new());
        let tool = EchoTool { cmd, echo_proxy };
        tool.main(writer).await.unwrap();
        assert_eq!(format!("{ECHO}\n"), test_stdout.into_string());
    }

OWNERS

如果這個子工具位於 ffx 樹狀結構中,您必須新增 OWNERS 檔案,告訴我們誰負責這個程式碼,以及如何在分類中將問題導向該程式碼。如下所示:

file:/path/to/authoritative/OWNERS

建議你將其新增為參照 (使用 file: 或可能為 include) 視為人員直接清單,以免因為不在 。

新增至版本

如要將工具新增至 GN 建構圖做為主機工具,會需要參照 在 ffx 工具 gn 檔案中的主要清單, 已新增至 toolstest 群組的 public_deps

完成後,如果您 fx build ffx,應該會在 ffx commands 的輸出內容中,在 Workspace Commands 的清單中看到您的工具,並且可以執行該工具。

實驗性子工具和子指令

建議子工具一開始不要將 sdk_category 他們的 BUILD.gn。如未指定類別,系統會採用這些子工具 而且不會納入 SDK 版本如果使用者希望 使用者必須直接獲得二進位資料。

不過,子指令的處理方式有所不同。

子指令需要在工具中新增 AvailabilityFlag 屬性 (請參閱修訂版本 從ffx target update的歷史記錄 範例)。如果使用者想要使用子指令,就必須將 來叫用該子指令

但這種方法有問題,例如缺少任何驗證資訊。 寫入子指令的 FIDL 依附元件因此,自 2023 年 12 月起,我們將改變處理子指令的機制。

與子工具類似,子指令將能宣告 SDK 類別 (使用 ,以判斷是否有可用的子指令。 建立子工具時,只會使用子工具類別 (或更高) 的子指令 第二,自訂角色只能 套用至專案或機構FIDL 依附元件檢查會正確驗證子指令的要求。

新增至 SDK

工具穩定後,您就可以將其納入 SDK,並將二進位檔新增至 SDK 建構。請注意,在執行這個步驟前 必須是相對穩定且經過充分測試 (若沒有 務必已將其納入 SDK),接下來請務必 相容性問題。

相容性

將子工具加到 SDK 和 IDK:

  1. FIDL 程式庫:將子工具新增至 SDK 時,您必須將依附於 SDK 的任何 FIDL 程式庫新增至 SDK。(詳情請參閱 將 API 升級為 partner_internal)。

  2. 指令列引數 - 用於測試因指令列造成的破壞性變更 選項變更,則 ArgsInfo 衍生巨集可用來產生 JSON 表示法 建立服務帳戶

    這項功能會用於黃金檔案測試,用來偵測差異。Golden 檔案。最後,這項測試將強化,以偵測及警告 導致回溯相容性發生異動

  3. 機器友善輸出 - 工具和子指令需有機器輸出內容 ,特別是用於測試或建構指令碼的工具。 MachineWriter 物件可用於以 JSON 格式編碼輸出內容,並提供用於偵測輸出結構變更的結構定義。

    機器輸出內容必須在目前的相容性時間窗格內保持穩定。最後,系統會檢查機器輸出格式是否符合標準。使用機器寫入器輸出的優點是,您可以自由使用自由文字輸出不穩定的輸出內容。

更新子工具

如要將子工具新增至 SDK,請在其 BUILD.gn 中將 sdk_category 設為 適當的類別 (例如 partner)。如果子工具包含子指令 不再處於實驗階段,請移除其 AvailabilityFlag 屬性, 因此不再需要特殊的設定選項就能叫用。

納入 SDK

你也需要將子工具新增到host_tools分子 SDK GN 檔案,例如:

sdk_molecule("host_tools") {
  visibility = [ ":*" ]

  _host_tools = [
    ...
    "//path/to/your/tool:sdk", # <-- insert this
    ...
  ]
]

使用者體驗和 SDK 審查

子工具必須遵循 CLI 指南