每个组件都有一个描述组件属性和功能的声明。对于在软件包中分发的组件,该声明使用组件清单文件来表示,并借助组件解析器加载。
您可以使用组件清单语言 (CML) 文件来声明组件。在构建时,组件清单编译器 (cmc
) 工具会验证清单源代码并将其编译为二进制格式 (.cm
),并将其存储在组件的软件包中。
ComponentDecl
组件清单
CML 文件是以 .cml
扩展名结尾的 JSON5 文件。下面是一个运行 ELF 二进制文件的简单组件的示例 CML 清单文件,该二进制文件会将“Hello, World”消息输出到系统日志:
{
// 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()
:组件的分布单位。允许将一个或多个组件托管在软件包代码库中,并添加到目标设备的软件包集中。此目标会生成软件包元数据并构建 Fuchsia Archive (.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.h
和 echo/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
此函数会根据列表的长度,从提供的参数列表中创建一个简单的字符串。
添加到 build 配置
创建 echo/BUILD.bazel
文件并添加以下构建规则,以便将新组件包含在 build 配置中:
echo/BUILD.bazel
:
load(
"@rules_fuchsia//fuchsia:defs.bzl",
"fuchsia_cc_binary",
"fuchsia_component",
"fuchsia_component_manifest",
"fuchsia_package",
)
fuchsia_cc_binary(
name = "echo_example",
srcs = [
"echo_component.cc",
"echo_component.h",
"main.cc",
],
deps = [
"@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 中,并在系统日志中测试输出。