声明组件

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

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

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

组件清单

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 构建系统以 GN 导入形式提供模板, //build/components.gni:用于构建和打包软件 集成到 Fuchsia 组件中。下面是一个简单的 BUILD.gn 文件示例 C++ 组件:


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

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

resource("my_file") {
  sources = [ "my_file.txt" ]
  outputs = [ "data/{{source_file_part}}" ]
}

fuchsia_component("hello-world-component") {
  component_name = "hello-world"
  deps = [
    ":bin",
    ":my_file",
  ]
  manifest = "meta/hello-world.cml"
}

fuchsia_package("hello-world") {
  package-name = "hello-world"
  deps = [
    ":hello-world-component",
  ]
}

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

  • executable():将源代码编译为二进制文件。此目标值不尽相同 具体取决于编程语言例如,executable 目标 可用于 C++,rustc_binary 用于 Rust,go_binary 用于 Golang。
  • resource():要作为资源复制的数据文件的可选命名集合 复制到另一个 GN 目标中。这些文件可通过 该组件的命名空间。
  • fuchsia_component():收集二进制文件、组件清单和其他 将资源整合到单个目标中。此目标会编译清单 使用 cmc 将 source 转换为组件声明。
  • fuchsia_package():组件的分布单位。允许一个或多个 托管在软件包代码库中并包含在目标 设备的软件包集。此目标会生成软件包元数据和 build Fuchsia 归档 (.far) 文件。

软件包可以包含多个组件,如以下项中的 deps 所示: fuchsia_package()模板。您可以简化软件包的构建文件 仅包含一个组件,使用 fuchsia_package_with_single_component() 模板。

以下简化的 BUILD.gn 示例等同于前一个 示例:


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

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

resource("my_file") {
  sources = [ "my_file.txt" ]
  outputs = [ "data/{{source_file_part}}" ]
}

fuchsia_package_with_single_component("hello-world") {
  manifest = "meta/hello-world.cml"
  deps = [
    ":bin",
    ":my_file",
  ]
}

练习:创建新组件

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

首先,在以下位置为名为 echo-args 的新组件创建一个项目基架 //vendor/fuchsia-codelab 目录:

mkdir -p vendor/fuchsia-codelab/echo-args

在新项目目录中创建以下文件和目录结构:

Rust

//vendor/fuchsia-codelab/echo-args
                        |- BUILD.gn
                        |- meta
                        |   |- echo.cml
                        |
                        |- src
                            |- main.rs
  • BUILD.gn:可执行二进制文件、组件和 软件包。
  • meta/echo.cml:用于声明组件可执行文件的清单; 所需的功能。
  • src/main.rs:Rust 可执行二进制文件和单元测试的源代码。

C++

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

添加程序参数

组件清单文件定义了组件可执行文件的属性, 包括程序参数和组件功能。 将以下内容添加到 meta/echo.cml

Rust

echo-args/meta/echo.cml

{
    include: [
        // Enable logging on stdout
        "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-args",

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

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

C++

echo-args/meta/echo.cml

{
    include: [
        // Enable logging.
        "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-args",

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

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

记录参数

打开主可执行文件的源文件,并添加以下 import 语句:

Rust

echo-args/src/main.rs

use tracing::info;

C++

echo-args/main.cc

#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>

#include <cstdlib>
#include <iostream>

#include "vendor/fuchsia-codelab/echo-args/echo_component.h"

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

Rust

echo-args/src/main.rs

#[fuchsia::main(logging = true)]
async fn main() -> Result<(), anyhow::Error> {
    // Read program arguments, and strip off binary name
    let mut args: Vec<String> = std::env::args().collect();
    args.remove(0);

    // Include environment variables
    let animal = std::env::var("FAVORITE_ANIMAL").unwrap();
    args.push(animal);

    // Print a greeting to syslog
    info!("Hello, {}!", greeting(&args));

    Ok(())
}
<ph type="x-smartling-placeholder">

C++

echo-args/main.cc

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

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

  // Print a greeting to syslog
  FX_LOG_KV(INFO, "Hello", FX_KV("greeting", echo::greeting(arguments).c_str()));

  return 0;
}

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

添加以下代码以实现 greeting() 函数:

Rust

echo-args/src/main.rs

// Return a proper greeting for the list
fn greeting(names: &Vec<String>) -> String {
    // Join the list of names based on length
    match names.len() {
        0 => String::from("Nobody"),
        1 => names.join(""),
        2 => names.join(" and "),
        _ => names.join(", "),
    }
}

C++

echo-args/echo_component.h

#include <string>
#include <vector>

namespace echo {

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

}  // namespace echo

echo-args/echo_component.cc

#include "vendor/fuchsia-codelab/echo-args/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

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

<ph type="x-smartling-placeholder">

添加到构建配置

BUILD.gn 文件中更新程序的依赖项:

Rust

echo-args/BUILD.gn

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


group("echo-args") {
  testonly = true
  deps = [
    ":package",
  ]
}

rustc_binary("bin") {
  output_name = "echo-args"
  edition = "2021"

  # Generates a GN target for unit-tests with the label `bin_test`,
  # and a binary named `echo_bin_test`.
  with_unit_tests = true

  deps = [
    "//src/lib/fuchsia",
    "//third_party/rust_crates:anyhow",
    "//third_party/rust_crates:tracing",
  ]

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

fuchsia_component("component") {
  component_name = "echo-args"
  manifest = "meta/echo.cml"
  deps = [ ":bin" ]
}

fuchsia_package("package") {
  package_name = "echo-args"
  deps = [ ":component" ]
}

C++

echo-args/BUILD.gn

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


group("echo-args") {
  testonly = true
  deps = [
    ":package",
  ]
}

executable("bin") {
  output_name = "echo-args"

  sources = [ "main.cc" ]

  deps = [
    ":cpp-lib",
    "//sdk/lib/async-default",
    "//sdk/lib/async-loop:async-loop-cpp",
    "//sdk/lib/async-loop:async-loop-default",
    "//sdk/lib/syslog/cpp",
  ]
}

source_set("cpp-lib") {
  sources = [
    "echo_component.cc",
    "echo_component.h",
  ]
}

fuchsia_component("component") {
  component_name = "echo-args"
  manifest = "meta/echo.cml"
  deps = [ ":bin" ]
}

fuchsia_package("package") {
  package_name = "echo-args"
  deps = [ ":component" ]
}

将新组件添加到 build 配置:

fx set workstation_eng.x64 --with //vendor/fuchsia-codelab/echo-args

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

fx build

在下一部分中,您需要将此组件集成到 build 中,并测试 在系统日志中记录输出内容。