声明组件

每个组件都有一个声明,用于描述组件的属性和功能。对于在软件包中分发的组件,声明 使用组件清单文件表示,并借助 组件解析器

显示如何使用“组件清单”声明组件的示意图。通过
清单由开发者工具编译并在设备上进行解析,网址为:
运行时。

您可以使用组件清单语言 (CML) 文件来声明组件。在构建时,组件清单编译器 (cmc) 工具会验证并将清单源代码编译为二进制格式 (.cm),并将其存储在组件的软件包中。在运行时,组件解析器会将二进制清单加载到 ComponentDecl FIDL 结构中,以便 Component Manager 使用。

组件清单

CML 文件是 JSON5 文件,此类文件以 .cml扩展程序。下面是一个简单组件的 CML 清单文件示例 运行输出“Hello, World”的 ELF 二进制文件写入系统日志:

{
    // Information about the program to run.
    program: {
        // Use the built-in ELF runner.
        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" },
    ],
}

此文件声明了与组件相关的两个主要信息部分:

  • program:描述可执行文件信息,例如二进制文件、程序参数和关联的运行时。在此示例中,二进制文件会编译为 ELF 可执行文件,并使用内置的 ELF 运行程序
  • use:声明此组件运行所需的功能。在此示例中,fuchsia.logger.LogSink 协议使组件能够将消息写入系统日志 (syslog)。

清单分片

某些功能集代表了系统中许多组件(例如日志记录)共有的用例要求。为了简化包含这些 功能,框架会将它们抽象为 清单分片(可包含在 CML 源文件中)。

以下是与上一示例等效的 CML。在这种情况下, 包括通过添加 diagnostics/syslog/client.shard.cml,而不是声明 fuchsia.logger.LogSink

{
    include: [ "syslog/client.shard.cml" ],

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

构建组件

Fuchsia SDK 系统提供了构建和打包软件的 Bazel 规则 集成到 Fuchsia 组件中。Fuchsia SDK 环境会在 Bazel 工作区目录中提供这些规则。

在 Bazel 工作区中,您可以将 Fuchsia 软件包和组件声明为 Bazel 软件包中的 Bazel 目标,该软件包由 BUILD.bazel 文件描述。

以下是简单 C++ 组件的 BUILD.bazel 文件示例:

# Build rules provided by the Fuchsia SDK
load(
    "fuchsia_cc_binary",
    "fuchsia_component",
    "fuchsia_component_manifest",
    "fuchsia_package",
)

fuchsia_cc_binary(
    name = "hello_world",
    srcs = [
        "hello_world.cc",
    ],
)

fuchsia_component_manifest(
    name = "manifest",
    src = "meta/hello_world.cml",
)

fuchsia_component(
    name = "component",
    manifest = ":manifest",
    deps = [":hello_world"],
)

fuchsia_package(
    name = "pkg",
    package_name = "hello_world",
    visibility = ["//visibility:public"],
    deps = [
        ":component",
    ],
)

此文件包含以下主要元素:

  • fuchsia_cc_binary():将 C++ 源代码编译为二进制文件,包括所有必要的库依赖项。
  • fuchsia_component_manifest():使用 cmc 将组件清单源文件 (.cml) 编译为二进制组件声明。
  • fuchsia_component():将二进制文件、组件清单和其他资源收集到单个目标中。
  • fuchsia_package():组件的分布单位。允许将一个或多个组件托管在软件包仓库中,并包含在目标设备的软件包集中。此目标会生成软件包元数据和 build Fuchsia 归档 (.far) 文件。

练习:创建新组件

在本练习中,您将构建并运行一个基本组件,用于读取程序 参数,并在系统日志中回显问候语。

首先,在 Bazel 工作区中为名为 echo 的新组件创建一个新项目目录:

mkdir -p fuchsia-codelab/echo

完成本部分后,项目应具有以下目录结构:

//fuchsia-codelab/echo
                  |- BUILD.bazel
                  |- meta
                  |   |- echo.cml
                  |
                  |- echo_component.cc
                  |- echo_component.h
                  |- main.cc
  • BUILD.bazel:可执行二进制文件、组件和软件包的 Bazel 构建目标。
  • meta/echo.cml:用于声明组件可执行文件的清单; 所需的功能。
  • echo_component.cc:C++ 组件功能的源代码。
  • main.cc:C++ 可执行二进制文件主入口点的源代码。

添加程序参数

组件清单文件定义了组件可执行文件的属性, 包括程序参数和组件功能。

创建 echo/meta/echo.cml 并添加以下内容:

echo/meta/echo.cml

{
    include: [
        "syslog/client.shard.cml",
    ],

    // 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",

        // Program arguments
        args: [
            "Alice",
            "Bob",
        ],

        // Program environment variables
        environ: [ "FAVORITE_ANIMAL=Spot" ],
    },

    // ...
}

记录参数

为主可执行文件创建 echo/main.cc 源文件,并将 以下 import 语句:

echo/main.cc

#include <lib/syslog/global.h>

#include <cstdlib>
#include <iostream>
#include <string>

#include "echo_component.h"

main() 函数添加以下代码:

echo/main.cc

int main(int argc, const char *argv[], char *envp[]) {
  // Read program arguments, and exclude the binary name in argv[0]
  std::vector<std::string> arguments;
  arguments.resize(0);
  for (int i = 1; i < argc; i++) {
    arguments.push_back(argv[i]);
  }

  // Include environment variables
  const char *favorite_animal = std::getenv("FAVORITE_ANIMAL");
  if (favorite_animal != NULL) {
    arguments.push_back(favorite_animal);
  }

  // Print a greeting to syslog
  FX_LOGF(INFO, "echo", "Hello, %s!", echo::greeting(arguments).c_str());
  return 0;
}

这段代码读取程序参数,并将其传递给名为 greeting(),用于为 syslog 条目生成响应。

创建 echo/echo_component.hecho/echo_component.cc,包括 以下代码来实现 greeting() 函数:

echo/echo_component.h

#include <string>
#include <vector>

namespace echo {

std::string greeting(std::vector<std::string> &names);

} // namespace echo

echo/echo_component.cc

#include "echo_component.h"

#include <numeric>

namespace echo {

static std::string join(std::vector<std::string> &input_list,
                        const std::string &separator) {
  return std::accumulate(
      std::begin(input_list), std::end(input_list), std::string(""),
      [&separator](std::string current, std::string &next) {
        return current.empty() ? next : (std::move(current) + separator + next);
      });
}

// Return a proper greeting for the list
std::string greeting(std::vector<std::string> &names) {
  // Join the list of names based on length
  auto number_of_names = names.size();
  switch (number_of_names) {
  case 0:
    return "Nobody!";
  case 1:
    return join(names, "");
  case 2:
    return join(names, " and ");
  default:
    return join(names, ", ");
  }
}

} // namespace echo

此函数根据提供的参数列表创建简单的字符串, 列表长度上限。

添加到构建配置

创建 echo/BUILD.bazel 文件并在其中添加以下构建规则 构建配置中的新组件:

echo/BUILD.bazel

load(
    "@fuchsia_sdk//fuchsia:defs.bzl",
    "fuchsia_cc_binary",
    "fuchsia_component",
    "fuchsia_component_manifest",
    "fuchsia_package",
    "fuchsia_select",
    "fuchsia_unittest_package",
)

fuchsia_cc_binary(
    name = "echo_example",
    srcs = [
        "echo_component.cc",
        "echo_component.h",
        "main.cc",
    ],
    deps = [] + fuchsia_select({
        "@platforms//os:fuchsia": [
            "@fuchsia_sdk//pkg/fdio",
            "@fuchsia_sdk//pkg/syslog",
        ],
    }),
)

fuchsia_component_manifest(
    name = "manifest",
    src = "meta/echo.cml",
    component_name = "echo",
    includes = [
        "@fuchsia_sdk//pkg/syslog:client",
    ],
)

fuchsia_component(
    name = "component",
    component_name = "echo",
    manifest = ":manifest",
    visibility = ["//visibility:public"],
    deps = [":echo_example"],
)

fuchsia_package(
    name = "pkg",
    package_name = "echo-example",
    visibility = ["//visibility:public"],
    components = [
        ":component",
    ],
)

运行 bazel build 并验证构建是否成功完成:

bazel build //fuchsia-codelab/echo:pkg

在下一部分中,您将将此组件集成到 build 中,并测试系统日志中的输出。