RFC-0093 - 组件清单的设计原则

RFC-0093:组件清单的设计原则
状态已接受
领域
  • 组件框架
说明

关于用于组件清单的“.cml”和“.cm”格式的设计说明。

问题
  • 75514
Gerrit 更改
  • 521303
作者
审核人
提交日期(年-月-日)2021-04-26
审核日期(年-月-日)2021-05-05

总结

本文档记录了对用于组件清单.cml.cm 格式进行的设计考虑、原则和决策。

下述大部分决定都是在 2018 年至 2019 年期间做出的。

  1. 组件清单具有前端和后端
  2. 组件由清单定义
  3. 组件清单是声明式的
  4. 组件清单可能来自各种来源

术语库

  • .cml组件清单源文件的通用文件扩展名。CML 文件是用于声明组件的 JSON5 DSL
  • ComponentDecl 是一个 FIDL 表,它是组件清单的规范传输和存储格式。
  • .cm 是包含采用 FIDL 信封二进制格式的 ComponentDecl 的文件的通用文件扩展名。
  • cmc 是一种命令行主机工具,用于从 .cml 文件生成 .cm 文件。它在 Fuchsia 源代码树中构建,并通过 Fuchsia SDK 作为预构建的可执行文件进行分发。

#1:组件清单具有前端和后端

人和机器有不同的需求和偏好。在设计组件清单语法和格式时,一个关键的设计原则是:创建单个后端格式供组件框架读取,并创建单独的前端(最初为单个前端)供开发者编写。这种设计决策具有以下优势:

  1. 您可以单独优化后端和前端,以满足不同的设计目标。
  2. 可以在不修改后端的情况下改进前端,反之亦然。
  3. 虽然当前只有一个前端,但可以引入其他前端,例如满足不同受众群体的不同设计目标,或者满足对便利性、熟悉性或样式的偏好。

为了支持这些目标,SDK 提供了 [cmc](“组件清单编译器”),这是一种将组件清单源文件 (.cml) 转换为清单二进制文件 (.cm) 的标准工具。cmc 通常与构建系统透明集成,这意味着开发者通常会与源文件交互,除非他们在对清单进行调试或分析。

ComponentDecl:组件清单后端

ComponentDecl 是组件清单的规范存储和传输格式。此工具旨在由组件管理器、组件解析器和清单分析工具(如 fx scrutiny)等代码编写和解释。此类格式需要实现以下目标:

  1. 名称必须明确且具有自描述性。应该可以通过清单内容直接推导清单的含义。
  2. 它必须能够随着时间的推移不断演变,并支持向前和向后兼容性。
  3. 它必须易于解析,并避免不必要的格式转换。否则,无必要地增加处理组件清单的代码(包括组件管理器)出现 bug 或攻击的风险。
  4. 它必须易于与与组件清单连接的运行时、组件和工具集成。

根据这些设计目标,我们自然而然地选择了一种格式:FIDL。FIDL 是 Fuchsia 中 IPC 的标准传输格式。FIDL 值类型(即没有句柄的类型)可以持久保留并用作存储格式。 具体而言,使用 FIDL 信封是因为它们会跳过未知字段,这有助于实现向后和向前兼容性。由于在 Fuchsia 上运行的任何运行时都已有 FIDL 绑定,因此 FIDL 绑定可以轻松与代码集成,并且无需对解析或转换提供额外的支持。

ComponentDecl 采用没有默认值的方式:换言之,只有当字段不适用或来自不含该字段的清单版本时,系统才会填充该字段。此外,为了支持向前和向后兼容性,ComponentDecl 及其嵌套的结构是 FIDL 表FIDL 灵活联合

CML:组件清单前端

CML(“组件清单语言”)是组件清单的来源格式。其设计为人类读写,但也可供格式化程序和语言服务器等开发工具读取和写入。

  1. 本指南应由了解基本组件框架概念的新手阅读。
  2. 表示常见模式应该很方便,而无需过多的样板。
  3. 应该有可能影响语法但不影响清单语义含义的更改,而无需更改清单的二进制文件表示法。例如,从单例数组更改为单数值应该不会影响输出。
  4. 它应该提升可维护性。具体来说,它应该允许评论。
  5. 它应该足够好用,能够支持自动转换,例如支持大规模重构。

CML 就是为了满足这些目标而发明的。CML 是一种基于 JSON5 的配置语言,它可充当生成 ComponentDecl 的简单 DSL。通过使用 JSON5,CML 利用了许多开发者已经熟悉的语言,并且在 Fuchsia 的其他地方广泛使用。与 ComponentDecl 不同,CML 提供了一些功能,让您可以更简洁地编写清单:

  • 它允许省略某些字段的默认值。
  • 它允许将多项功能组合到一个声明中,前提是它们共用相同的选项。
  • 它允许清单包含为清单贡献内容的清单分片。例如,您可以依赖于某个库并包含库的分片,以获取该库所需的所有功能。

最后,从 CML 到 ComponentDecl 的转换虽然不是一对一的,但应该让用户不必学习相关规则就能轻松理解。

#2:组件由清单定义

组件由清单进行描述。系统会在启动时通过组件的网址解析清单。例如,通过 fuchsia-pkg:// 网址启动的组件将具有具有 .cm 扩展的清单,其中包含从软件包解析的序列化 ComponentDecl。除了清单之外,组件还可以整合来自同一软件包的资源。例如,使用 ELF 运行程序的组件会指定 ELF 二进制文件在该软件包中的位置。另一方面,具有 https:// 网址的组件可能具有 ComponentDecl,它由 https 解析器生成,但不由可通过网址获取的资源提供支持。

组件的清单完整描述了其输入、输出和内部组合。目前,组件清单中不得包含在运行时填充的任何参数或“悬挂”值。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:// 解析器将检索软件包,并从网址的片段标识符部分指定的清单读取清单。Web 解析器可能会生成一个清单,其内容可能会因网域、安全政策和用户偏好设置而有所不同。

目前,最常用的组件分发方式是通过 Fuchsia 软件包。此类组件由 fuchsia-pkg:// 网址进行标识。组件清单以 blob 的形式提供在此软件包中(通常位于 meta/ 中)。

灵感

  • Kubernetes 中的声明式应用管理:为 Kubernetes 设计配置语言的原则以及对替代方案的研究。
  • 命令式与声明式:扩展了标题性主题。
  • Starlark:基于 Python 的 DSL,一种命令式配置语言。
  • Jsonnet:从 JSON 到数据模板语言的扩展,其中任何程序都会生成 JSON 文档。
  • borgcfgGCLborgmon:Google 使用的功能配置语言,与 Kubernetes 大致类似,后者的历史信息有助于我们在命令式语法和声明式语法之间进行权衡。

备注


  1. 未来,清单很有可能需要支持某种参数化功能,以支持变体和产品可配置性。在进行这一操作时,我们应尽量避免与可参数化配置相关的常见陷阱。 

  2. 有关这一点的更多背景信息,Kubernetes 提供了大量文档,说明了非声明式配置的许多缺点。