build 组件

本文档演示了如何构建和测试组件,重点介绍了 定义软件包、组件及其测试的最佳做法。

概念

在构建组件之前,您应该了解以下概念:

<ph type="x-smartling-placeholder"></ph> 软件包 是 Google Cloud 上的软件分发单元, 紫红色。Packages 是一系列文件集合,其关联路径可用于 相对于文件包底板的高度例如,一个软件包可能包含 路径 bin/hello_world 下的 ELF 二进制文件,路径下的 JSON 文件 data/config.json。必须将文件分组到文件包中才能推送 将这些文件上传到设备上

<ph type="x-smartling-placeholder"></ph> 组件 是 Google Cloud 上软件执行的 紫红色。Fuchsia 上的所有软件(内核映像和用户模式除外) 引导加载程序被定义为组件。

组件由 <ph type="x-smartling-placeholder"></ph> 组件清单 。通常使用的组件 添加额外的文件,例如在 Google Cloud 控制台中 运行时。

开发者必须根据软件包和组件来定义其软件 无论是构建生产软件还是编写测试。

在运行时, 组件实例 请参阅 其软件包的内容以只读文件形式保存在路径 /pkg 下。定义两个 同一软件包中的一个或多个组件不会向每个组件 彼此的功能不过,它能保证一个组件 其他可用。因此,如果组件试图启动 其他组件(例如在集成测试中), 将这两个组件打包在一起

组件可通过几种方式进行实例化,所有这些方式都以某种方式指定其 网址 ,了解所有最新动态。通常,您可以通过指定 其软件包名称及其在软件包中的组件清单的路径,使用 fuchsia-pkg:// 架构 ,了解所有最新动态。

组件清单

组件清单是对组件声明进行编码的文件, 作为软件包的一部分分发二进制格式是持久性 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 的工作原理,请参阅可用性

客户端库包括

如上所示,组件清单支持“include”语法, 引用一个或多个清单分片作为其他清单内容的来源。 库等某些依赖项会假定依赖组件 在运行时可用的某些功能例如, 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" ]
}

Rust

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" ]
}

Go

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" ]
}

Rust

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" ]
}

Go

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() 以包含所有测试( 只能有 1 项)。这是推荐做法 以便在整个源代码树中组织目标

由于 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() 目标添加到deps fuchsia_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" ]
}

Rust

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" ]
}

Go

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" ]
}

使用 fx test 和 GN 目标名称启动测试组件 或完整的组件网址:

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" ]
}

Rust

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" ]
}

Go

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)

使用 fx test 和 GN 目标名称启动测试组件 或完整的组件网址:

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",
  ]
}

Rust

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",
  ]
}

Go

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 会自动重复此命令 每次保存更改。对于测试驱动型开发,请尝试启动 在单独的 shell 中运行此命令,并观察您的代码如何重新构建和重新测试, 正在处理中。

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.ttfdata/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() 依赖项放在测试组件上。在 从构建系统的角度来看,资源依赖项可能处于 测试软件包,并且 build 会产生相同的结果。 不过,更好的做法是将依赖项置于需要 。这样,我们就可以在不同的 test 软件包(例如,针对不同的压缩工具组件进行测试);以及 测试组件的运作方式与之相同

示例:使用 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) 控制对 您可以通过组件 build 中的选择启用属性将这些受限功能 规则。

如需使用受限功能,请添加 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

问题排查

本部分包含您在构建组件时可能会遇到的常见问题。

缺少分片包含

在以下情况下,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”。请参考以下示例 使用 Inspect

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_includesexpect_includes() 规则。

Rust

  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_includesexpect_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

Rust

$ 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" ] 
    }
    

    Rust

    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 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 请求 use 使用 fuchsia.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", 
        }, 
    ],
}

如需详细了解如何构建功能路由,请参阅连接组件