RFC-0106:Fuchsia SDK 中的组件清单 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 在 SDK 中创建包含分片 sysroot 的组件清单,并将其分发给树外集成商。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-05-12 |
审核日期(年-月-日) | 2021-06-23 |
摘要
本文档提议将组件清单分片添加到集成商开发套件 (IDK)。这将创建一种标准方式,用于将组件清单分片发布给树外 (OOT) 开发者,并建立一种通用模式,使清单分片可在不同的开发环境之间移植。
读者应熟悉以下内容:
- 组件清单
- 特别是组件清单中的
include
语法。
设计初衷
跨环境协调工作流
自推出以来,组件清单分片以及 cml
和 cmx
中的 include
语法得到广泛采用。它们可用于各种任务,例如缩减样板代码、封装实现细节、简化开发者工作流,以及核心领域变体。
目前,处理清单的机制包括依赖于 Fuchsia 树的源代码布局。这适用于位于紫红色树中的清单,但不适合移植到 OOT 开发。例如,请考虑本指南,了解如何记录 C/C++ 组件中的内容。本指南在开篇时会指示开发者将以下内容添加到其组件清单中:
{
include: [ "sdk/lib/diagnostics/syslog/client.shard.cml" ],
}
不过,本指南指出,上述内容仅适用于树内 (IT) 环境。由于 OOT 开发者也是本指南的目标受众群体,因此本指南会指示这些开发者将以下内容添加到其组件清单中,从而有效地内嵌分片的内容:
{
use: [
{ protocol: "fuchsia.logger.LogSink" },
],
}
其他地方也存在同样的问题。例如,测试运行程序的测试运行程序框架清单(用于支持 Fuchsia 上的各种测试,例如 C/C++ GoogleTest、Rust libtest、gotest on Go 等)包含仅适用于 IT 的说明。这阻碍了良好的测试实践向 OOT 开发者普及。
这种折衷方案违背了先添加清单分片的目的,因为客户端会看到 Fuchsia 日志记录的实现细节。此外,这还使未来更难以编排未来大规模更改,例如计划将未来软过渡到用于从组件发布日志的其他协议。最后,这会使 IT 和 OOT 开发者工作流之间的摩擦增加。
模糊实现细节
如果我们重新查看 LogSink
分片示例,建议 syslog 库的客户端添加此分片,因为这会将使该客户端库正常运行所需的功能引入其组件的沙盒。
目前,这组功能非常简单,只有一个协议,但将来可能会有所变化。例如,如果当前协议(客户端将一个套接字连接到 syslog,然后将字符缓冲到该套接字中以便记录这些字符),我们可能会引入一个系统,用于将以专用格式的结构化数据写入 VMO,并在客户端和服务器之间旋转这些数据,这与现在的跟踪工作方式不同。
在分片后面隐藏这些细节可以减轻组件作者的认知负担,组件作者只想写入 Syslog(从他们的角度来看,这是一种常见的实用程序),而不关心这些细节。不过,这也使得 Syslog 维护人员可以不断完善这些细节,而无需组件所有者注意。
这同样以相反的方向运行(从组件公开功能,而不是使用它们),并且使用不同类型的功能(例如,目录功能而不是协议功能)。例如,请考虑此检查发现和托管指南。本指南解释了开发者需要将以下内容添加到其组件清单中:
{
capabilities: [
{
directory: "diagnostics",
rights: [ "connect" ],
path: "/diagnostics",
},
],
expose: [
{
directory: "diagnostics",
from: "self",
to: "framework",
},
],
}
或者,开发者可以包含一个分片。这样一来,开发者无需编写 14 行样板代码,无需费心处理向框架公开目录 capability 等概念(大多数组件开发者并不需要这些概念),而且日后需要时也无需更改此机制。最后,如果可以在 IT 和 OOT 组件清单之间移植分片,则检查指南将大大缩短,因为它不需要为 IT 和 OOT 开发者指定一组单独的说明。
简化系统功能的使用
另一个更复杂的示例是,直接作为 FIDL 客户端使用 Web 引擎时可能需要的各种功能,而不是像上一个示例那样通过客户端库使用该功能。Web 运行时功能强大且稳健,凭借必需的功能,当今的 Web 应用能够执行许多特权设备操作。fuchsia.web
定义了 Web 引擎实现托管某些 Web 应用所需的不同功能。
为了简化从环境中使用这些功能并将其提供给 Web 引擎的流程,我们使用分片将这些功能按常见类别划分。例如,此分片定义了 Web 引擎所需的一组基准“表赌注”功能:
{
"sandbox": {
"services": [
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.intl.PropertyProvider",
"fuchsia.logger.LogSink",
"fuchsia.memorypressure.Provider",
"fuchsia.process.Launcher",
"fuchsia.sysmem.Allocator",
]
}
}
此分片定义了当 Web 应用不是本地且/或需要网络时,所需的其他功能:
{
"sandbox": {
"services": [
"fuchsia.net.name.Lookup",
"fuchsia.net.interfaces.State",
"fuchsia.posix.socket.Provider"
]
}
}
然而,若要在景观视图中渲染 Web 应用,就需要用到此分片:
{
"sandbox": {
"services": [
"fuchsia.accessibility.semantics.SemanticsManager",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.input.ImeVisibilityService",
"fuchsia.ui.scenic.Scenic"
]
}
}
此外还有一些其他分片,例如用于为图形或硬件媒体编解码器解锁硬件加速。
这些信息量相当大。将其保留在自己的分片中比将其添加到组件沙盒中的一堆其他服务中更为简单。此外,这同样让进化变得更容易。
请注意,这只是一个虚构的示例。如今,这些碎片已成为 IT 领域的发展来源。此为探索将这些碎片移至 IDK 以使其可用 OOT 的可能性,但并非承诺这样做。此外,上述碎片的内容仍在不断变化,在此仅作说明之用,不用作参考文档。
实现
将清单分片打包到 IDK 发行版中
通过将包含路径(通过 --include-directory
或 -I
标志指定给编译器)设置为一个或多个目录(包含子目录和头文件的特别嵌套层次结构),即可实现以 Fuchsia 为目标的 C/C++ 开发。这样一来,包含 Fuchsia 专用头文件的代码便可以在 IT 和 OOT build 之间移植。
例如,以下 C 代码行对 IT 和 OOT 均有效:
#include <lib/zx/process.h>
这是因为,以 Fuchsia 为目标平台的 IT build 和 OOT build 都会在其目录中添加路径,且该路径的下方有 lib/zx/process.h
文件。在 Fuchsia 签出中,相应的 include 目录为 //zircon/system/ulib/zx/include/
,而在 OOT build 中,此路径必须为 $FUCHSIA_SDK/pkg/zx/include/
。另请参阅:IDK 布局。
在 include 路径中设置目录以使包含指令可移植性也称为设置“sysroot”。
我们将以类似方式部署 $FUCHSIA_SDK/pkg/
下的组件清单分片(在概念上与分片的用途关联的子路径中),并相应地在 IT 和 OOT build 的 cmc
中设置 --includepath
标志。
例如,在上述示例中使用的 Syslog 分片可能是:
- 包含如下内容:
include: [ "syslog/client.shard.cml" ]
。 - 在
//sdk/lib/syslog/client.shard.cml
下找到了 IT,因此我们将使用--includepath $FUCHSIA_CHECKOUT/sdk/lib/
配置cmc
IT。 - 在
$FUCHSIA_SDK/pkg/syslog/client.shard.cml
下发现 OOT,因此我们将使用--includepath $FUCHSIA_SDK_ROOT/pkg/
配置cmc
OOT。
可移植分片与本地分片
预计一些分片将提供给 OOT 开发者,而其他分片仅供 IT 使用。我们查看了可以使用 OOT 的分片的一些示例。这个分片仅在两个组件定义之间共享大部分复杂功能路由,其中一个是系统组件,另一个是该组件的测试替身,这个分片才是真正有用的 IT 分片。此特定分片 OOT 没有用。
因此,一些分片应该具有可移植性并在 IDK 中发布,而其他分片则应该保持为特定仓库的私有状态。
我们提议使用相对路径和绝对路径的通用表示法将这种区别编码化。清单 include
指令中应解析为可移植分片的路径不应包含前导 //
,例如 syslog/client.shard.cml
。这些将根据分片的 sysroot 进行解析。另一方面,特定代码库本地的分片路径应以 //
开头,并根据代码库的源根目录(或检出根目录)进行解析。例如,此分片应通过路径 //src/sys/test_manager/meta/common.shard.cml
包含 IT 部分。
构建系统与 cmc
集成
构建系统(如树内 GN & Ninja build 以及任何以 Fuchsia 为目标的树外 build)已与 cmc
集成。需要修改此类集成,以承受新的包含行为。尤其是以下 cmc
子命令:
compile
include
check-includes
调用 cmc
时需要指定以下标志:
--includeroot
:解析带有//
前缀的 include 路径的路径。--includepath
:按照指定的顺序(第一个匹配)解析其他路径的零个或多个路径。
分片为 SDK Atom
与其他 IDK 元素的处理方式类似,构建系统会将分片包含在 IDK 中。我们将重复使用现有的 sdk_atom()
模板,根据我们想要的 IDK 布局指定 id
参数。
将分片添加到 IDK 的流程
分片可以指定可能与平台表面重叠的合同预期。例如,syslog 分片引用了 Fuchsia 命名空间中的一项协议 fuchsia.logger.LogSink
,该协议众所周知地是由 Fuchsia 系统组件提供的。因此,在 IDK 中发布的分片将被视为 API 和 SDK Atom,并且将通过目前用于添加或修改在 IDK 中发布 FIDL 文件的相同流程接受 API 审核。您也可以使用 sdk_atom()
的类别概念,例如,首先将分片作为“内部”数据引入(而非分布式 OOT),然后通过现有流程将其提升为风险较高的类别。
后续工作
端口:expect_includes()
Fuchsia 的 GN build 提供了一个模板,用于要求依赖组件在其清单中包含特定分片(请参阅本指南)。这样做的好处之一是,它会指示添加依赖于某项特定服务的客户端库等依赖项的开发者在其组件中也包含一个清单分片,以确保库能够在运行时访问正常运行所需的功能。
IT 开发者对此给予了非常积极的反馈,一些 OOT 开发者表示感兴趣。此模板或其概念可以使用 GN 元数据移植到 GN SDK 中,也可以使用 Bazel 方面或 Bazel 提供程序移植到 Bazel/Blaze SDK。
另请参阅:https://fxbug.dev/42156975
性能
此方案对运行时性能没有任何影响,因为所有工作都是在构建时完成的。
构建时包含的处理会增加一些额外的工作。不过,这预计不会对干净 build 墙使用时间产生任何影响。根据之前的研究,我们发现,不在关键路径上的构建通常不会影响构建延迟,而且直接源自来源的工作(例如 cmc
调用、fidlc
调用等)在关键路径上几乎不存在,因为这项工作可以很好地并行运行,并且可以非常灵活地进行安排。
例如,考虑这项更改,其中 cmc
中的一项常见操作的执行速度加快了约 300 毫秒,不过尽管在 build 中调用了数千次 cmc
,我们测得其对 build 实际用时没有影响。
工效学设计
大家已经注意到如何使清单包含的机制简单透明。例如,可以通过一个简单的命令生成后处理的组件清单,其中 include 指令被替换为所包含文件的内容(如果需要,则传递文件)。
fx cmc include manifest --includepath $(fx get-src-dir)
原则上,这与在 C/C++ 源代码上运行 C 预处理器类似(请参阅:man cpp
)。
此外,cmc
会生成易于排查的简单错误,包括异常错误情况(如包含周期)。所有受支持的错误都会进行单元测试。
cmc
工具本身已经捆绑在 Fuchsia IDK 中。它是完全封闭的,无需任何外部依赖项即可运行。该工具可从命令行调用,无需集成构建系统。
向后兼容性
在 IDK 中更改分片会影响新构建的 OOT 组件,一旦它们选择最新的 IDK 版本,但不会影响旧的预构建组件。必须小心避免破坏性更改,并针对平台更改采用标准做法:多个版本的软转换、支持期限以及与利益相关方沟通。
与平台演变的所有问题一样,您应对更改进行全面测试,并对破坏性更改进行管理,使其能够应对服务中断,例如使用某种版本控制机制。请注意,有关平台版本控制的问题以及此问题的部分内容(例如 FIDL 版本控制)目前仍保持开放状态,不在此 RFC 的范围内。
安全注意事项
组件清单定义了功能路由和沙盒,它们会对安全性产生直接影响。不过,清单包含的目标不是最终生成不同的清单,而是以更符合人体工学的方式生成同一清单。只要在处理 include 后生成相同的最终清单,就不会对安全性产生任何影响。
为了帮助开发者了解其清单在处理 include 后会是什么样,可以使用上面演示的 cmc include
。
测试
单元测试和集成测试已涵盖 cmc
中的现有功能。cmc
的测试覆盖率超过 90%,专门涵盖 include 子命令的覆盖率已完成。需要对 Fuchsia IDK 或 OOT 集成的任何未决更改,将按照 SDK 团队的指示和预期进行测试。
就 CTS 测试覆盖范围而言,应将 IDK 中的组件清单视为任何其他 IDK 工件。
文档
我们介绍了 include
语法。
cmc include
命令通过 cmc help include
自行记录。
目前指示 OOT 开发者替换清单包含项的现有文档将进行更新,在不再需要这种区分时,不再对 IT 和 OOT 开发进行这种区分。
缺点、替代方案和问题
不进行任何操作
我们可以保持现状,但代价是没有解决上述动机部分所述的问题。
将 IDK 中的分片整理为顶层工件
除了在 IDK 中各种可能的位置发布组件清单分片之外,例如在 $FUCHSIA_SDK_ROOT/pkg
或 $FUCHSIA_SDK_ROOT/fidl
(基于分片的逻辑关联)下,我们还探索了为组件清单分片建立单个基本目录的替代方案。例如:$FUCHSIA_SDK_ROOT/component_manifests/
。这类似于所有 .fidl
文件在 $FUCHSIA_SDK_ROOT/fidl/
下的组织方式,也就是说,它们按类型(即 FIDL 文件)而不是其他逻辑关联(例如 pkg/async/
、pkg/memfs/
、pkg/zx/
)聚合。
根据 SDK 客户的反馈,此替代方案遭到了拒绝,这些客户的反馈指出,他们更喜欢 SDK 内容的逻辑关联,而不是将不同类型的文件分布到不同的基本目录中。例如,接受的设计允许将用于使用 syslog 的组件清单分片放置在用于从使用 C++ 编写的组件写入 syslog 的 C++ 头文件旁边。
现有艺术和参考资料
组件清单包含的灵感源自 C/C++ 包含。
cmc include
命令的灵感来自 cpp
命令,该命令对给定文件运行 C 预处理器并输出后处理结果。请参阅:man cpp
。