RFC-0093:组件清单的设计原则 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 有关用于组件清单的 `.cml` 和 `.cm` 格式的设计备注。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-04-26 |
审核日期(年-月-日) | 2021-05-05 |
摘要
本文档记录了有关用于组件清单的 .cml
和 .cm
格式的设计讨论、原则和决策。
下文所述的大多数决定是在 2018 年至 2019 年间做出的。
术语库
.cml
是组件清单源文件的常用文件名扩展名。CML 文件是一种用于声明组件的 JSON5 DSL。- ComponentDecl 是一个 FIDL 表,是组件清单的规范化线和存储格式。
.cm
是包含 FIDL 封装容器二进制格式的 ComponentDecl 的文件的常用文件名扩展名。cmc
是一个命令行主机工具,用于从.cml
文件生成.cm
文件。它内置于 Fuchsia 源代码树中,并通过 Fuchsia SDK 作为预构建的可执行文件进行分发。
#1:组件清单包含前端和后端
人类和机器的需求和偏好各不相同。在设计组件清单语法和格式时,一个关键设计原则是为组件框架创建单个后端格式,并为开发者创建单独的前端(最初是单数)。此设计决策具有以下优势:
- 您可以单独优化后端和前端,以满足不同的设计目标。
- 您可以在不修改后端的情况下改进前端,反之亦然。
- 虽然目前只有一个前端,但您可以引入其他前端,例如为不同的受众群体满足不同的设计目标,或者迎合便利性、熟悉度或样式的偏好。
为了实现这些目标,SDK 提供了 [cmc]
(“组件清单编译器”),这是一个标准工具,用于将组件清单源文件 (.cml
) 转换为清单二进制文件 (.cm
)。cmc
通常与构建系统透明集成,这意味着,除非开发者在调试或对清单执行分析,否则通常会与源文件交互。
ComponentDecl:组件清单后端
ComponentDecl 是组件清单的规范存储和线格格式。它旨在由组件管理器、组件解析器和清单分析工具(例如 fx scrutiny
)等代码编写和解读。此类格式需要满足以下目标:
- 必须明确且自描述。应能够直接从清单的内容中推断出清单的含义。
- 它必须能够随时间推移而演变,支持向前和向后兼容性。
- 必须易于解析,并避免不必要的格式转换。否则,会不必要地增加处理组件清单(包括组件管理器)的代码中出现 bug 或遭到攻击的风险。
- 它必须能够轻松与与组件清单交互的运行时、组件和工具集成。
这些设计目标自然而然地促成了 FIDL 格式的选择。FIDL 是 Fuchsia 中 IPC 的标准线格式。FIDL 值类型(即没有句柄的类型)可以持久化并用作存储格式。具体而言,使用 FIDL 封装容器是因为它们具有额外的好处,即会跳过未知字段,这对向后和向前兼容性很有用。由于 Fuchsia 上运行的任何运行时都已存在 FIDL 绑定,因此可以轻松与代码集成,并且无需额外支持解析或转换。
ComponentDecl 的结构不含默认值:换句话说,只有当字段不适用或来自不含该字段的清单版本时,才不会填充该字段。此外,为了支持向前和向后兼容性,ComponentDecl 及其嵌套的结构是 FIDL 表或 FIDL 灵活联合。
CML:组件清单前端
CML(“组件清单语言”)是组件清单的源格式。它旨在供人类读取和编写,但也可以供格式设置工具和语言服务器等开发工具读取和编写。
- 新手应该能够理解其中的内容,前提是他们了解基本的组件框架概念。
- 应便于表示常见模式,而不会产生过多的样板代码。
- 应该可以更改影响清单语法但不影响清单语义含义的内容,而无需更改清单的二进制表示法。例如,从单例数组更改为单个值不应影响输出。
- 应促进可维护性。具体而言,该视频应允许添加评论。
- 它应足够易于机器处理,以支持自动转换,例如支持大规模重构。
CML 就是为了实现这些目标而发明的。CML 是一种基于 JSON5 的配置语言,可用作生成 ComponentDecl 的简单 DSL。通过使用 JSON5,CML 利用了许多开发者已经熟悉且在 Fuchsia 的其他位置广泛使用的语言。与 ComponentDecl 不同,CML 提供了一些功能,可让您更简洁地编写清单:
- 它允许省略某些字段的默认值。
- 它允许将多个功能组合到单个声明中,前提是它们共享相同的选项。
- 它允许清单包含清单分片,以向清单提供内容。例如,您可以依赖于某个库并添加该库的分片,以获取该库所需的所有功能。
最后,从 CML 转换为 ComponentDecl 虽然不是一对一,但应该简单明了,用户无需学习规则即可理解。
#2:组件由清单定义
组件由清单描述。系统会在启动时通过组件的网址解析清单。例如,通过 fuchsia-pkg://
网址启动的组件将具有包含 .cm
扩展的清单,其中包含从软件包解析的序列化 ComponentDecl
。除了清单之外,组件还可以包含同一软件包中的资源。例如,使用 ELF 运行程序的组件会指定该软件包中 ELF 二进制文件的位置。另一方面,具有 https://
网址的组件可能具有由 https
解析器生成的 ComponentDecl
,但该 ComponentDecl
并非由可通过网址获取的资源提供支持。
组件的清单会完整描述其输入、输出和内部组成。目前,组件清单不得包含任何在运行时填充的参数或“悬空”值。1
不过,这并不意味着清单完全描述了组件的行为。一方面,向组件提供的功能由父级决定;组件无法控制提供这些功能的人员。此外,每个组件都属于某个环境,该环境会为组件提供特定类型的配置,例如用于解析组件网址的解析器。
沿着清单之间的网址,您可以得到一个组件实例树。组件实例树是对构成 Fuchsia 映像的软件的全面描述。这样,您就可以对给定系统映像(例如 [fx
scrutiny](https://fuchsia.dev/reference/tools/fx/cmd/scrutiny)
)放心地执行安全审核。
#3:组件清单是声明式的
虽然具有命令式功能的配置语言功能强大,但代价是可读性、可预测性和可审核性会降低。先例表明,过于强调命令式风格的配置语言易于出错且不太人性化。2对于组件框架,组件定义必须可审核且易于理解,因此命令式配置语言不适用。
因此,CML 是一种声明式语言。虽然 CML 支持生成清单的部分内容,但仅在结果非常可预测的情况下才支持这种做法。例如,清单支持默认值和包含,但不提供模板化或参数化功能。
CML 是一种供人类读写的语言。除了开发者工具集成(例如格式设置工具或 IDE 模板)外,CML 不应由工具生成。生成 CML 文件会增加掩盖清单底层内容的风险,因为现在涉及三个层:CM、CML 和工具。如果出于某种原因必须生成清单,您应编写一个单独的前端来生成 CM。
#4:组件清单可以来自各种来源
组件清单通常不绑定到任何单一分发机制。最终,组件解析器负责检索网址的组件清单。解析器如何实现此目的取决于给定的网址架构。例如,fuchsia-pkg://
解析器会检索软件包,并从网址的 fragment 标识符部分指定的软件包中读取清单。网络解析器可能会生成一个清单,其内容可能会因网域、安全政策和用户偏好设置而异。
目前,分发组件最常用的方式是通过 Fuchsia 软件包。此类组件由 fuchsia-pkg://
网址标识。组件的清单会作为此软件包中的 blob 分发,通常位于 meta/
中。
灵感
- Kubernetes 中的声明式应用管理:设计 Kubernetes 配置语言时所遵循的原则,以及对替代方案的研究。
- 命令式与声明式:对标题主题进行展开。
- Starlark:一种基于 Python 的 DSL 和命令式配置语言。
- Jsonnet:将 JSON 扩展为数据模板语言,其中任何程序都可以生成 JSON 文档。
- borgcfg、GCL 和 borgmon:Google 使用的函数式配置语言,大致类似于 Kubernetes,其历史有助于我们了解命令式和声明式语法之间的权衡。