建構元件

本文件說明如何建構及測試元件,並醒目顯示最佳做法 定義套件、元件及其測試的實務做法。

概念

建構元件之前,您應瞭解下列概念:

套件 是軟體發布單位 紫紅色。套件是一組檔案,且具有相關聯的路徑, 與該套件的底層相對關係舉例來說,套件可能含有 bin/hello_world 路徑下的 ELF 二進位檔,以及路徑下方的 JSON 檔案 data/config.json。您需要將檔案分組到套件中,才能推送檔案 並將這些檔案傳送到裝置

元件 是軟體執行單位 紫紅色。Fuchsia 上所有軟體 (核心圖片和使用者模式除外) Bootstrap 程式被定義為元件。

元件是由 元件資訊清單 。通常 包括執行其他檔案所需的其他檔案,如執行檔和資料資產 執行階段。

開發人員必須根據套件和元件來定義軟體 無論是建構生產軟體 還是編寫測試用途

在執行階段, 元件執行個體 會看到 以唯讀檔案的形式儲存在 /pkg 路徑下。定義兩個 無法將存取權授予每個元件 也就是執行另一個神經元的能力不過,這可以保證只讓 其他可用的網路因此,如果元件嘗試啟動 如整合測試中的另一個元件 就是將這兩個元件套件在一起

系統會透過幾種方式將元件例項化,並逐一指定元件 網址 ,直接在 Google Cloud 控制台實際操作。一般來說,您可以透過指定 並使用 這個 fuchsia-pkg:// 配置 ,直接在 Google Cloud 控制台實際操作。

元件資訊清單

元件資訊清單是將元件宣告編碼的檔案,通常是 隨著套件一起發布二進位格式是保留的 FIDL 檔案 包含元件宣告資訊清單會宣告 元件的程式二進位檔和必要功能。

以下是簡易「Hello, World」的資訊清單檔案範例元件:

{
    // Information about the program to run.
    program: {
        // Use the built-in ELF runner to run platform-specific binaries.
        runner: "elf",
        // The binary to run for this component.
        binary: "bin/hello",
        // Program arguments
        args: [
            "Hello",
            "World!",
        ],
    },

    // Capabilities used by this component.
    use: [
        { protocol: "fuchsia.logger.LogSink" },
    ],
}

資訊清單資料分割

某些功能集合代表常見的用途需求 加入系統中的許多元件,例如記錄為了簡化納入這些流程 元件功能,元件架構支援抽象化 整理成資訊清單資料分割,並加入主要資訊清單檔案中。 這在概念上與 C 程式設計中的 #include 指令類似 語言。

下列的資訊清單與上例的相等資訊清單, 能力,由資訊清單資料分割 include 取代:

{
    // Include capabilities for the syslog library
    include: [ "syslog/client.shard.cml" ],

    // Information about the program to run.
    program: {
        // Use the built-in ELF runner to run platform-specific binaries.
        runner: "elf",
        // The binary to run for this component.
        binary: "bin/hello-world",
        // Program arguments
        args: [
            "Hello",
            "World!",
        ],
    },
}

相對路徑

包含以 "//" 開頭的路徑與來源樹狀結構的根部相對 以及執行方式如果包含的路徑不是以 "//" 開頭,則 建構系統會嘗試透過 Fuchsia SDK 解決這些變數。

跨資料分割依附元件

假設一個資訊清單資料分割同時在資訊清單和第二個資訊清單資料分割中新增子項 新增第二個子發布商,其取決於第一個屬性,接著是 如果有第一個子項,就會導致資訊清單驗證錯誤。 第二個資料分割則包含在沒有第一個資料分割的資訊清單中,因為 優惠會參照一個不存在的子項。

// echo_server.shard.cml
{
    children: [ {
        name: "echo_server",
        url: "fuchsia-pkg://fuchsia.com/echo_server#meta/echo_server.cm",
    } ],
}
// echo_client.shard.cml
{
    children: [
        {
            name: "echo_client",
            url: "fuchsia-pkg://fuchsia.com/echo_client#meta/echo_client.cm",
        }
    ],
    offer: [ {
        // This offer will cause manifest validation to fail if
        // `echo_client.shard.cml` is included in a manifest without
        // `echo_server.shard.cml`.
        protocol: "fuchsia.examples.Echo",
        from: "echo_server",
        to: "echo_client",
    } ],
}

為解決這個問題,優惠的「source_availability」欄位可設為 提供資訊清單編譯,表示優惠來源可接受 設為 unknown 後,優惠會受到以下影響 宣告:

  • 如果 from 來源已存在:供應情形設為 required
  • 如果 from 來源不存在:供應情形會設為 optional,然後 優惠來源將重寫為 void
// echo_client.shard.cml
{
    children: [
        {
            name: "echo_client",
            url: "fuchsia-pkg://fuchsia.com/echo_client#meta/echo_client.cm",
        }
    ],
    offer: [
        {
            // If `echo_server.shard.cml` is included in this manifest, then
            // `echo_client` can access the `fuchsia.examples.Echo` protocol from
            // it.
            //
            // If `echo_server.shard.cml` is not included in this manifest, then
            // `echo_client` will be offered the protocol with a source of
            // `void` and `availability == optional`. `echo_client` must consume
            // the capability optionally to not fail route validation.
            protocol: "fuchsia.examples.Echo",
            from: "echo_server",
            to: "echo_client",
            source_availability: "unknown",
        }
    ],
}

如要進一步瞭解 availability 的運作方式,請參閱「適用國家/地區」。

用戶端程式庫包括

如上所示,元件資訊清單支援「包含」功能語法 將一或多個資訊清單資料分割做為其他資訊清單內容的來源。 程式庫等部分依附元件會假設相依元件具有 這些功能在執行階段中可以提供某些功能舉例來說: C++ Syslog 程式庫進行此類假設。

如果您正在建構用戶端程式庫,可以宣告這些必要的依附元件 在 BUILD.gn 檔案中使用 expect_includes。舉例來說,假設 以下假設檔案 //sdk/lib/fonts/BUILD.gn

import("//tools/cmc/build/expect_includes.gni")

# Client library for components that want to use fonts
source_set("font_provider_client") {
  sources = [
    "font_provider_client.cc",
    ...
  ]
  deps = [
    ":font_provider_client_includes",
    ...
  ]
}

expect_includes("font_provider_client_includes") {
  includes = [
    "client.shard.cml",
  ]
}

這會為依附的資訊清單設定建構時間規定, 預期的資訊清單資料分割:

{
    include: [
        "//sdk/lib/fonts/client.shard.cml",
    ]
    ...
}

納入的路徑是根據來源根目錄進行解析。 遞移形式包含 (包含包括) 在內。 不允許進行循環活動。

為資料分割命名時,請勿以完整路徑重複命名, 在上述範例中,為資料分割命名後,被重複命名了 fonts.shard.cml,因為那麼完整路徑就會 sdk/lib/fonts/fonts.shard.cml,內容重複。該檔案則是 名為 client.shard.cml 的子字串,表示用戶端的用戶端會使用該欄位 適用於字型的 SDK 程式庫

元件套件 GN 範本

GN 是 Fuchsia 使用的中繼建構系統。Fuchsia 擴充 GN 定義範本範本可讓使用者新增至 GN 內建目標 。

Fuchsia 定義下列 GN 範本來定義套件和元件:

以下的假設套件包含執行 C++ 的元件 計畫:

import("//build/components.gni")

executable("my_program") {
  sources = [ "my_program.cc" ]
}

fuchsia_component("my_component") {
  manifest = "meta/my_program.cml"
  deps = [ ":my_program" ]
}

fuchsia_package("my_package") {
  deps = [ ":my_component" ]
}

請注意下列細節:

  • 匯入「"//build/components.gni"」即可存取所有與以下項目相關的範本 套件、元件和測試
  • fuchsia_component() 範本會宣告元件。這取決於 程式二進位檔 (在本範例中為 executable()),且需要 manifest 指向元件資訊清單檔案
  • 元件和套件名稱兩者皆衍生自目標名稱。 在上述範例中,這些名稱一起形成 啟動元件: fuchsia-pkg://fuchsia.com/my_package#meta/my_component.cm

特定語言元件範例

以下使用單一元件定義套件的基本範例。 ,以多種常用語言啟動程式。參照的 系統假設來源檔案和元件資訊清單 路徑。

C++

import("//build/components.gni")

executable("bin") {
  output_name = "my_program"
  sources = [ "main.cc" ]
}

fuchsia_component("my_component") {
  manifest = "meta/my_component.cml"
  deps = [ ":bin" ]
}

fuchsia_package("my_package") {
  deps = [ ":my_component" ]
}

荒漠油廠

import("//build/rust/rustc_binary.gni")
import("//build/components.gni")

rustc_binary("bin") {
  output_name = "my_program"
  sources = [ "src/main.rs" ]
}

fuchsia_component("my_component") {
  manifest = "meta/my_component.cml"
  deps = [ ":bin" ]
}

fuchsia_package("my_package") {
  deps = [ ":my_component" ]
}

查看

import("//build/go/go_binary.gni")
import("//build/components.gni")

go_binary("bin") {
  output_name = "my_program"
  sources = [ "main.go" ]
}

fuchsia_component("my_component") {
  manifest = "meta/my_component.cml"
  deps = [ ":bin" ]
}

fuchsia_package("my_package") {
  deps = [ ":my_component" ]
}

包含單一元件的套件

套裝方案是發布單位。建議您在定義多個值時 指定相同套件中的多個元件,以保證數個 元件一律共同呈現,或希望更新內容 可一次更新多個元件 (透過更新單一套件)。

這個模式也常用於建立密封的整合測試。 例如,在兩個元件之間執行整合測試,其中之一是用戶端 在其他元件中實作的服務會包含用戶端 和伺服器元件

不過,您通常會定義只需要單一元件的套件。 在這種情況下,您可以使用 fuchsia_package_with_single_component() 來簡化範本設計這個範本結合了 fuchsia_package()fuchsia_component()

C++

import("//build/components.gni")

executable("rot13_encoder_decoder") {
  sources = [ "rot13_encoder_decoder.cc" ]
}

fuchsia_package_with_single_component("rot13") {
  manifest = "meta/rot13.cml"
  deps = [ ":rot13_encoder_decoder" ]
}

荒漠油廠

import("//build/rust/rustc_binary.gni")
import("//build/components.gni")

rustc_binary("rot13_encoder_decoder") {
  sources = [ "src/rot13_encoder_decoder.rs" ]
}

fuchsia_package_with_single_component("rot13") {
  manifest = "meta/rot13.cml"
  deps = [ ":rot13_encoder_decoder" ]
}

查看

import("//build/go/go_binary.gni")
import("//build/components.gni")

go_binary("rot13_encoder_decoder") {
  sources = [ "rot13_encoder_decoder.go" ]
}

fuchsia_package_with_single_component("rot13") {
  manifest = "meta/rot13.cml"
  deps = [ ":rot13_encoder_decoder" ]
}

測試套件 GN 範本

測試套件包含至少一個元件 做為測試版本測試套件是使用 fuchsia_test_package.gni。這個 範本仍可用來定義各種測試 整合測試 -- 測試除了測試本身之外其他元件的情況 應用於測試請查看「單元測試」, 以及單元測試

import("//build/components.gni")

executable("my_test") {
  sources = [ "my_test.cc" ]
  testonly = true
  deps = [
    "//src/lib/fxl/test:gtest_main",
    "//third_party/googletest:gtest",
  ]
}

fuchsia_component("my_test_component") {
  testonly = true
  manifest = "meta/my_test.cml"
  deps = [ ":my_test" ]
}

executable("my_program_under_test") {
  sources = [ "my_program_under_test.cc" ]
}

fuchsia_component("my_other_component_under_test") {
  manifest = "meta/my_component_under_test.cml"
  deps = [ ":my_program_under_test" ]
}

fuchsia_test_package("my_integration_test") {
  test_components = [ ":my_test_component" ]
  deps = [ ":my_other_component_under_test" ]
  test_specs = {
    environments = [ vim3_env ]
  }
}

group("tests") {
  deps = [ ":my_integration_test" ]
  testonly = true
}

請注意下列細節:

  • 這個範例會定義 "my_test_component",這原理是實作 測試是運用 C++ Googletest 等常見測試架構撰寫而成 Rust Cargo 測試等
  • 測試會與相依元件一併封裝 "my_other_component_under_test"。這可能是模擬服務供應商 或測試需要叫用的其他元件所需的任何部分。 將這些元件封裝在一起可確保相依元件 容器都是可以在測試執行期間啟動, 測試版本
  • environments 參數可讓 fuchsia_test_package() 視需要 採用 test_spec.gni 參數並覆寫 預設測試行為在此範例中,這項測試設為 它可以在 VIM3 裝置上執行
  • 最後,這個範例會定義 group(),並納入所有測試 ( 只能有一個)。此為建議做法 用於整理來源樹狀結構的目標

由於 GN 的限制,任何 test_component 目標 您的 fuchsia_test_package()BUILD.gn 測試套件目標對這類行為 至 fuchsia_test()

在單一 BUILD.gn 檔案中定義:

# Let this be //foo/BUILD.gn
import("//build/components.gni")

executable("my_test") {
  sources = [ "my_test.cc" ]
  testonly = true
  deps = [
    "//src/lib/fxl/test:gtest_main",
    "//third_party/googletest:gtest",
  ]
}

fuchsia_component("my_test_component") {
  testonly = true
  manifest = "meta/my_test.cml"
  deps = [ ":my_test" ]
}

fuchsia_test("my_test_component_test") {
  package = "//bar:my_test_package"
  component = ":my_test_component"
}

group("tests") {
  testonly = true
  deps = [ ":my_test_component_test" ]
}

接著,您可以將 fuchsia_component() 目標新增至 depsfuchsia_package()目標。

# Let this be //bar/BUILD.gn
import("//build/components.gni")

fuchsia_package("my_test_package") {
  testonly = true
  deps = [ "//foo:my_test_component" ]
}

單元測試

由於單元測試十分常見,因此建構系統會提供兩個簡化的 GN 範本:

  • fuchsia_unittest_component.gni敬上 定義要做為測試執行的元件,以及 產生基本元件資訊清單,您必須將其納入套件中。
  • fuchsia_unittest_package.gni敬上 定義了內含單一元件的套件作為測試,簡寫 一個fuchsia_unittest_component()目標並搭配 fuchsia_test_package()

使用資訊清單進行單元測試

下方範例將示範如何建立測試執行檔 套件和元件

C++

import("//build/components.gni")

executable("my_test") {
  sources = [ "test.cc" ]
  deps = [
    "//src/lib/fxl/test:gtest_main",
    "//third_party/googletest:gtest",
  ]
  testonly = true
}

fuchsia_unittest_package("my_test") {
  manifest = "meta/my_test.cml"
  deps = [ ":my_test" ]
}

荒漠油廠

import("//build/rust/rustc_test.gni")
import("//build/components.gni")

rustc_test("my_test") {
  sources = [ "test.rs" ]
  testonly = true
}

fuchsia_unittest_package("my_test") {
  manifest = "meta/my_test.cml"
  deps = [ ":my_test" ]
}

查看

import("//build/go/go_test.gni")
import("//build/components.gni")

go_test("my_test") {
  sources = [ "test.go" ]
  testonly = true
}

fuchsia_unittest_package("my_test") {
  manifest = "meta/my_test.cml"
  deps = [ ":my_test" ]
}

使用具有 GN 目標名稱的 fx test 啟動測試元件 或完整元件網址:

GN 目標

fx test my_test

元件網址

fx test fuchsia-pkg://fuchsia.com/my_test#meta/my_test.cm

使用已產生資訊清單的單元測試

以上範例為測試指定資訊清單。不過, 不需要任何特定功能。

以下是執行 ROT13 加密與解密的測試範例。 測試中的演算法是可以完整測試的純邏輯 和隔離機制如果我們為這些測試編寫資訊清單 包含要執行的測試二進位檔。在這種情況下 測試執行檔路徑,而範本會產生簡易的資訊清單 。

C++

import("//build/components.gni")

executable("rot13_test") {
  sources = [ "rot13_test.cc" ]
  deps = [
    "//src/lib/fxl/test:gtest_main",
    "//third_party/googletest:gtest",
  ]
  testonly = true
}

fuchsia_unittest_package("rot13_test") {
  deps = [ ":rot13_test" ]
}

荒漠油廠

import("//build/rust/rustc_test.gni")
import("//build/components.gni")

rustc_test("rot13_test") {
  sources = [ "rot13_test.rs" ]
  testonly = true
}

fuchsia_unittest_package("rot13_test") {
  deps = [ ":rot13_test" ]
}

查看

import("//build/go/go_test.gni")
import("//build/components.gni")

go_test("rot13_test") {
  sources = [ "rot13_test.go" ]
  testonly = true
}

fuchsia_unittest_package("rot13_test") {
  deps = [ ":rot13_test" ]
}

產生的元件資訊清單檔案使用下列指令:

fx gn outputs $(fx get-build-dir) //some/path/to/build/file:unittest target_component_generated_manifest

如何直接列印:

fx build && cat $(fx get-build-dir)/$(fx gn outputs $(fx get-build-dir) //some/path/to/build/file:unittest target_component_generated_manifest)

使用具有 GN 目標名稱的 fx test 啟動測試元件 或完整元件網址:

GN 目標

fx test rot13_test

元件網址

fx test fuchsia-pkg://fuchsia.com/rot13_test#meta/rot13_test.cm

單一套件中的多個單元測試

如要同時套件多個單元測試元件,請使用 有 fuchsia_unittest_component() 項規則,而不是 fuchsia_unittest_package(), 一起使用 fuchsia_test_package() 收集這些資料您可以使用 內含 fx test <package_name> 的單一套件中的所有測試元件 而不是個別執行容器

以下範例會建立單一測試套件 rot13_tests,其中包含兩個 獨立的測試元件:rot13_decoder_testrot13_encoder_test

C++

import("//build/components.gni")

executable("rot13_decoder_bin_test") {}

executable("rot13_encoder_bin_test") {}

fuchsia_unittest_component("rot13_decoder_test") {
  deps = [ ":rot13_decoder_bin_test" ]
}

fuchsia_unittest_component("rot13_encoder_test") {
  deps = [ ":rot13_encoder_bin_test" ]
}

fuchsia_test_package("rot13_tests") {
  test_components = [
    ":rot13_decoder_test",
    ":rot13_encoder_test",
  ]
}

荒漠油廠

import("//build/rust/rustc_test.gni")
import("//build/components.gni")

rustc_test("rot13_decoder_bin_test") {}

rustc_test("rot13_encoder_bin_test") {}

fuchsia_unittest_component("rot13_decoder_test") {
  deps = [ ":rot13_decoder_bin_test" ]
}

fuchsia_unittest_component("rot13_encoder_test") {
  deps = [ ":rot13_encoder_bin_test" ]
}

fuchsia_test_package("rot13_tests") {
  test_components = [
    ":rot13_decoder_test",
    ":rot13_encoder_test",
  ]
}

查看

import("//build/go/go_test.gni")
import("//build/components.gni")

go_test("rot13_decoder_test") {}

go_test("rot13_encoder_test") {}

fuchsia_unittest_component("rot13_decoder_test") {
  deps = [ ":rot13_decoder_bin_test" ]
}

fuchsia_unittest_component("rot13_encoder_test") {
  deps = [ ":rot13_encoder_bin_test" ]
}

fuchsia_test_package("rot13_tests") {
  test_components = [
    ":rot13_decoder_test",
    ":rot13_encoder_test",
  ]
}

使用 fx test 啟動套件中的所有測試元件,只需使用 GN 目標名稱:

fx test rot13_tests

以測試為導向開發

fx smoke-test 指令會自動偵測所有已知的測試 變更會影響建構系統的建構系統。在此情況下,建議您嘗試下列疑難排解做法:

fx -i smoke-test --verbose

在上述指令中,--verbose 會顯示 fx smoke-test 認為的測試 -i 會自動重複執行這個指令 每次儲存變更時如要進行測試導向的開發作業,可嘗試啟動 在單獨的殼層中執行這項指令,並觀察您的程式碼將自動建構和重新測試

fx smoke-test 搭配密封測試套件的效果最佳。測試套件為 以確保套件包含所有測試的所有依附元件。 換句話說,任何會影響這項測試結果的程式碼變更,都應 也需要重建測試套件。

其他套件資源

在上述範例中,我們展示了deps從套件到 以便產生執行檔,確保該執行檔已納入 套件。

某些情況下,需要加入額外檔案。我們示範的 使用兩個 resource.gni 範本 resource()resource_group()resource_tree()

範例:字型

import("//build/components.gni")

resource("roboto_family") {
  sources = [
    "Roboto-Black.ttf",
    "Roboto-Bold.ttf",
    "Roboto-Light.ttf",
    "Roboto-Medium.ttf",
    "Roboto-Regular.ttf",
    "Roboto-Thin.ttf",
  ]
  outputs = [ "data/fonts/{{source_file_part}}" ]
}

fuchsia_component("text_viewer") {
  ...
  deps = [
    ":roboto_family",
    ...
  ]
}

在上述範例中,我們提供六個檔案封裝在 data/fonts/ 底下, 會產生 data/fonts/Roboto-Black.ttf 路徑 data/fonts/Roboto-Bold.ttf 等。destination 的格式接受 GN 來源展開預留位置

接著,定義文字檢視器元件,使其取決於字型。在本 例如,文字檢視器實作會使用 Roboto 字型來轉譯文字。 元件可以在其沙箱中讀取指定字型 /pkg/data/fonts/...

範例:與黃金資料的整合測試

在這個範例中,我們會定義壓縮 JSON 檔案的假設服務。 「服務」可能會收到包含 JSON 文字的緩衝區,並傳回緩衝區 含有相同的 JSON 資料,但空白字元較少。我們會提出 整合測試,其中測試元件可做為壓縮器用戶端 並比較要壓縮的特定 JSON 檔案結果 已知良好結果 (或「黃金檔案」)。

import("//build/components.gni")

fuchsia_component("minifier_component") {
  ...
}

fuchsia_package("minifier_package") {
  ...
}

resource("testdata") {
  sources = [
    "testdata/input.json",
    "testdata/input_minified.json",
  ]
  outputs = [ "data/{{source_file_part}}" ]
}

fuchsia_component("minifier_test_client") {
  testonly = true
  deps = [
    ":testdata",
    ...
  ]
  ...
}

fuchsia_test_package("minifier_integration_test") {
  test_components = [ ":minifier_test_client" ]
  deps = [ ":minifier_component" ]
}

請注意,我們會將 resource() 依附元件放在測試元件上。從 因為建構系統認為資源依附元件 測試套件會產生相同結果,而建構也會產生相同的結果。 然而,將依附元件放在需要的目標上,是更好的做法 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件如此一來,我們就能在另一個 舉例來說,如要針對不同的壓縮器元件測試測試套件 測試元件的運作方式也相同

範例:使用 resource_group()

在上述範例中,所有符合某個結構的路徑 我們可以為多個檔案指定單一輸出模式 GN 來源擴展預留位置:接下來 因此必須將不同檔案重新命名為其他目的地 不同的包裝路徑

import("//build/components.gni")

resource_group("favorite_recipes") {
  files = [
    {
      source = "//recipes/spaghetti_bolognese.txt"
      dest = "data/pasta/spaghetti_bolognese.txt"
    },
    {
      source = "//recipes/creamy_carbonara.txt"
      dest = "data/pasta/carbonara.txt"
    },
    {
      source = "//recipes/creme_brulee.txt"
      dest = "data/dessert/creme_brulee.txt"
    },
    ...
  ]
}

我們的來源全部集中在同一個目錄,但被封裝在不同的目錄中 也可以用不同的名稱命名為了展現 我們可能需要盡量多的 resource() 目標。情境: 改為使用 resource_group() 呼叫,如上所示。

範例:使用 resource_tree()

使用 resource_group() 將每個來源檔案對應至目的地檔案路徑, 這對大型檔案集來說很麻煩resource_tree() 可讓您對應 來源檔案的目錄樹狀結構,屬於停靠站下的相同階層 檔案包在一個目錄內的目錄。以下範例複製了 default_repo_files/ 至套件目錄 repo/ (使用 sources 清單) 以確保只包含明確列出的檔案)。

import("//build/components.gni")

resource_tree("default-repo") {
  sources_root = "default_repo_files"
  sources = [
    "keys/root.json",
    "keys/snapshot.json",
    "keys/targets.json",
    "keys/timestamp.json",
    "repository/1.root.json",
    "repository/1.snapshot.json",
    "repository/1.targets.json",
    "repository/root.json",
    "repository/snapshot.json",
    "repository/targets.json",
    "repository/timestamp.json",
  ]
  dest_dir = "repo"
}

resource()resource_group()resource_tree()相同。您可以自由選擇要喜歡的項目。

受限制的功能

新的元件資訊清單功能處於開發階段,或有新功能時 元件架構小,因此元件架構團隊 限制可使用此功能的對象。CML 編譯器 (cmc) 用來控制 這些受限功能 (在元件版本中透過選擇加入屬性) 規則。

如要使用受限制的功能,請新增 restricted_features 屬性:

fuchsia_component("my-component") {
  manifest = "meta/my-component.cml"
  # This component opts-in to the restricted "allow_long_names" feature.
  restricted_features = [ "allow_long_names" ]
  deps = [ ... ]
}

只有許可清單可以使用受限制的功能。 您必須將元件加入以下功能的許可清單: //tools/cmc/build/restricted_features/BUILD.gn

疑難排解

本節包含建構元件時可能遇到的常見問題。

缺少資料分割的 include

check_includes如果 元件資訊清單 缺少 include 的 必要資訊清單資料分割

Error at ../../examples/components/echo_server/meta/echo_server.cml:
"../../examples/components/echo_server/meta/echo_server.cml" must include "../../sdk/lib/inspect/client.shard.cml".

如果元件依附元件鏈結中的程式庫有 expect_includes 需求條件與必要項目 在元件資訊清單中找不到 include。請參考以下範例 使用「檢查」

C++

  1. 您的元件依附於 //sdk/lib/inspect/component/cpp

    executable("bin") {
      output_name = "echo_server_cpp"
      sources = [ "main.cc" ]
    
      deps = [
        "//examples/components/routing/fidl:echo",
        "//sdk/lib/sys/cpp",
        # This library requires "inspect/client.shard.cml" 
        "//sdk/lib/inspect/component/cpp", 
        "//sdk/lib/async-loop:async-loop-cpp",
        "//sdk/lib/async-loop:async-loop-default",
      ]
    }
    
  2. //sdk/lib/inspect/component/cpp 取決於 //sdk/lib/inspect:client_includes,也就是 expect_includes() 規則。

荒漠油廠

  1. 您的元件依附於 //src/lib/diagnostics/inspect/runtime/rust

    rustc_binary("echo_server") {
      edition = "2021"
      deps = [
        "//examples/components/routing/fidl:echo_rust",
        # This library requires "inspect/client.shard.cml" 
        "//src/lib/diagnostics/inspect/runtime/rust", 
        "//src/lib/diagnostics/inspect/rust",
        "//src/lib/fuchsia",
        "//src/lib/fuchsia-component",
        "//third_party/rust_crates:anyhow",
        "//third_party/rust_crates:futures",
      ]
    
      sources = [ "src/main.rs" ]
    }
    
  2. //src/lib/diagnostics/inspect/runtime/rust 取決於 //sdk/lib/inspect:client_includes,也就是 expect_includes() 規則。

如要解決這個問題,請在元件資訊清單中新增缺少的 include。例如:

{
    include: [
        // Add this required include 
        "inspect/client.shard.cml", 

        // Enable logging
        "syslog/client.shard.cml",
    ],

    // ...
}

如要進一步瞭解必要項目的來源,您可以使用 gn path 指令來探索依附元件路徑:

fx gn path $(fx get-build-dir) my-component expect_includes target --with-data

這個指令會輸出類似以下的輸出內容,並顯示指向 必須提供:

C++

$ fx gn path $(fx get-build-dir) //examples/components/routing/cpp/echo_server //sdk/lib/inspect:client_includes --with-data
//examples/components/echo_server:bin --[private]-->
//sdk/lib/inspect/component/cpp --[data]-->
//sdk/lib/inspect:client_includes

荒漠油廠

$ fx gn path $(fx get-build-dir) //examples/components/routing/rust/echo_server //sdk/lib/inspect:client_includes --with-data
//examples/components/routing/rust/echo_server:bin --[public]-->
//examples/components/routing/rust/echo_server:bin.actual --[private]-->
//src/lib/diagnostics/inspect/runtime/rust:rust --[public]-->
//src/lib/diagnostics/inspect/runtime/rust:lib --[public]-->
//src/lib/diagnostics/inspect/runtime/rust:lib.actual --[private]-->
//sdk/lib/inspect:client_includes

無法驗證資訊清單

cmc_validate_references如果 元件資訊清單 當中包含的資源參照 無法在元件套件中找到 :

Error found in: //examples/components/echo/rust:rust-component_cmc_validate_references(//build/toolchain/fuchsia:x64)
    Failed to validate manifest: "obj/examples/components/echo/rust/cml/rust-component_manifest_compile/echo_rust.cm"
program.binary=bin/echo_example_oops but bin/echo_example_oops is not provided by deps!

Did you mean bin/echo_example?

Try any of the following:
...

當元件資訊清單 program 區塊中的「binary」欄位時,就會發生這種情形 參照的檔案路徑不在 fuchsia_package() 中。

如要解決這個問題,請確認下列事項:

  1. 元件資訊清單中的參照路徑已正確輸入。

    {
        // ...
    
        // Information about the program to run.
        program: {
            // Use the built-in ELF runner.
            runner: "elf",
    
            // The binary to run for this component. 
            binary: "bin/echo_example_oops", 
        },
    }
    
  2. 元件執行檔目標是與您應用程式相連的 deps 鏈結的一部分 fuchsia_package()

    C++

    executable("bin") {
      output_name = "echo_example"
      sources = [ "main.cc" ]
    
      deps = [ ... ]
    }
    
    # Neither the component or package depend on ":bin" 
    fuchsia_component("component") {
      manifest = "meta/echo_example.cml"
      deps = [] 
    }
    
    fuchsia_package("package") {
      package_name = "echo_example"
      deps = [ ":component" ] 
    }
    

    荒漠油廠

    rustc_binary("echo_example") {
      edition = "2021"
      sources = [ "src/main.rs" ]
    
      deps = [ ... ]
    }
    
    # Neither the component or package depend on ":echo_example" 
    fuchsia_component("component") {
      manifest = "meta/echo_example.cml"
      deps = [] 
    }
    
    fuchsia_package("package") {
      package_name = "echo_example"
      deps = [ ":component" ] 
    }
    

靜態能力分析工具

Scrutiny Static 分析工具無法建構失敗,並顯示以下錯誤 以便驗證每個 功能性路徑元件拓撲 :

Static Capability Flow Analysis Error:
The route verifier failed to verify all capability routes in this build.
...

Verification Errors:
[
  {
    "capability_type": "directory",
    "results": { ... }
  },
  {
    "capability_type": "protocol",
    "results": { ... }
  },
]

如果分析無法從來源成功追蹤能力路徑,就會發生這種情況 透過 expose 的有效鏈結要求能力 和 offer 元件資訊清單宣告。

在以下範例中,因要求 /core/echo 的元件而發生錯誤 usefuchsia.logger.LogSink 通訊協定沒有對應的 offer

"errors": [
  {
    "using_node": "/core/echo",
    "capability": "fuchsia.logger.LogSink",
    "error": {
      "error": {
        "analyzer_model_error": {
          "routing_error": {
            "use_from_parent_not_found": {
              "moniker": {
                "path": [
                  {
                    "name": "core",
                    "collection": null,
                    "rep": "core"
                  },
                  {
                    "name": "echo",
                    "collection": null,
                    "rep": "echo"
                  }
                ]
              },
              "capability_id": "fuchsia.logger.LogSink"
            }
          }
        }
      },
      "message": "A `use from parent` declaration was found at `/core/echo` for `fuchsia.logger.LogSink`, but no matching `offer` declaration was found in the parent"
    }
  }
]

如要解決這個問題,請查看建構失敗時提供的錯誤詳細資料,找出 導致轉送錯誤的來源,請在轉送鏈結中新增或修正無效的宣告。 在上一個範例錯誤中,應在父項元件的資訊清單中新增 offer

{
    // ...

    children: [
        // ...
        {
            name: "echo",
            url: "echo#meta/default.cm",
        },
    ],
    offer: [
        // ...
        { 
            protocol: "fuchsia.logger.LogSink", 
            from: "parent", 
            to: "#echo", 
        }, 
    ],
}

如要進一步瞭解建構能力路徑,請參閱「連結元件」。