RFC-0208:使用 SDK 分发软件包 | |
---|---|
状态 | 已接受 |
区域 |
|
说明 | 提出了如何在 SDK 中分发一些树内软件包,以实现树外子打包。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2023-02-15 |
审核日期(年-月-日) | 2023-02-10 |
摘要
此 RFC 介绍了我们在构建和分发应用的策略 Fuchsia 平台提供的软件包 仓库作为附录 。这些软件包旨在使用子软件包机制在下游进行组合。
该策略包括引入新的 .api
文件格式,
它本身是 Fuchsia 的新 SDK API Surface。格式本身
采用 格式的文件和已签到的文件均受 API 委员会的约束
审核。
设计初衷
实现了子软件包以支持封闭 嵌套软件包依赖项。此功能的主要用例是明确包含特定软件包以及树外测试 (OOT)。替代方案(包括对基础软件包的引用) 已被废弃,因为它容易损坏。例如: 从软件包中移除软件包时,依赖项链会损坏 基础映像
本文档介绍了提交到将从 Fuchsia 平台代码库分发的一组软件包的策略,以及这些软件包将如何作为子软件包包含在下游代码库中。
请注意,虽然此 RFC 侧重于将这些软件包打包到测试中的具体情况,但这是一种通过 SDK 分发软件包的通用机制。产品所有者可能需要添加 Fuchsia 团队编写的 在生产环境中的会话中存在哪些组件。通过 SDK 分发的第一个软件包可能用于测试,因为我们可能会在测试中采用一些在正式版中不可接受的捷径。除了安排时间考虑因素之外,其目的是完全支持 测试和生产场景所需的软件包。
利益相关方
教员:
hjfreyer@google.com
审核者:
- aaronwood@google.com
- dschuyler@Google.com
- jsankey@google.com
- richkadel@google.com
已咨询:
- etryzelaar@google.com
- kjharland@google.com
- shayba@google.com
- wittrock@google.com
社交化:
此 RFC 之前已与软件代表讨论过 交付、产品组装和组件框架
定义
- 集成商开发套件 (IDK) 是一种与构建系统无关的 一组代码、二进制文件和数据文件,用于构建 紫红色,如此处所述。
- Fuchsia Software Development Kit (SDK) 是 IDK(API、 工具和语言集成工件) 集成。 其中最值得注意的是 Fuchsia SDK with Bazel。
- 树内规则是指 网址为 https://fuchsia.googlesource.com/fuchsia/。这个 会生成 IDK 作为输出。
- 外部 (OOT) 是指代码库中非树内且使用 SDK 生成软件和产品的代码和构建规则。
- Fuchsia Packages 是软件的单元 分发,其中介绍了 二进制文件/库、组件清单以及 在 Fuchsia 系统上执行某些程序所需的数据文件。
- 借助子软件包,Fuchsia 软件包可以通过封装将另一个 Fuchsia 软件包的特定版本作为依赖项进行引用。
- 本 RFC 中描述的 SDK 软件包属于树内 Fuchsia 已明确标记为包含在 IDK 中的软件包,以及 扩展为 SDK 使用方OOT 代码库 根据发布软件包的名称,针对这些软件包进行构建 (例如,通过下载或子打包文件)。请注意, 此设计的扩展可能允许 OOT 代码库分发 自己的软件包
设计
本文档中的关键字“必须”“不得”“必需”“会”“不会”“应”“不应”“建议”“可以”和“可选”将按 IETF RFC 2119 中的描述进行解释。
SDK 软件包由树内 build 生成为新工件 IDK 引用的数据 ID。由于该 IDK 包含 适用于所有构建系统的 SDK 提供的软件工件, 所有 Fuchsia SDK 集成都能够引用 SDK 软件包。
以下阶段是在树内完成的:
- 选择 - 选择要分发的软件包。
- 验证 - 确保符合预期的一组软件包 分发。
- 构建 - 为所有必需的软件包构建选定的软件包 架构。
- 捆绑 - 生成软件包目录结构 将包含在 IDK 中的资源
SDK 软件包可在 OOT 代码库中立即使用
通过位于 SDK 中的软件包目录结构位于 sdk://packages/
。
本部分的其余内容将详细介绍每个阶段的设计。
发布 SDK 软件包
选择
我们不希望自动分发 树内存储库以便使用 OOT,我们必须提交到 一组特定的软件包将与 平台版本
我们将创建一个名为 sdk_fuchsia_package
的新 GN 模板。
需要以下信息:
- 要分发的
fuchsia_package
目标。 - 软件包的 SDK 类别(例如“合作伙伴”)。
- 将软件包添加到 SDK 的 API 级别。
- (可选)计划包的 API 级别 从 SDK 中移除
- 文件包中应该包含的文件列表及其“处置方式”。 相关内容将在下一部分中介绍。
此 GN 模板将使用 sdk_atom
目标来受益
围绕 SDK 基元验证 / 工具的既定路径。
新的 GN 目标 sdk_package_bundle
将列出当前平台 API 级别的所有 sdk_fuchsia_package
目标。此目标将使用
sdk_molecule
目标,用于强制执行软件包级检查。
通过这种机制,平台所有者可以明确承诺 提供适用于特定 API 级别的 OOT 使用的特定软件包 (即允许在一段时间后弃用和移除软件包 )。
作为实现细节,我们应避免依赖 GN 元数据
而是明确列出
sdk_fuchsia_package
目标。
验证
分发树内软件包不仅是对 name 表示软件包的名称,同时表明它至少要承诺某些资源 软件包的 contents。本部分介绍了 为什么需要此流程、声明合同 以及提交包装内物品的机械处理流程。 需要在构建时进行验证
背景
举例说明为什么签订合同非常重要
包含组件 sample.cm
的 sample-package
软件包。
sample-package
是 OOT 分发的,因此 SDK 用户可能会依赖于
fuchsia-pkg://host/sample-package#meta/sample.cm
。如果重命名或移除 sample.cm
,该 OOT build 现在将会失败。这与
发布到平台中分发的树内软件包的故障模式
图片。更细微的是,如果 sample.cm
需要新的传入功能(或任何不兼容的清单更改,请参阅下文),之前的使用将意外失败。
只需提交软件包的名称,我们就能看到上述
但明确对所有内容进行版本控制的另一个极端情况是
过于繁琐。例如,如果我们分配
archivist-for-embedding
,该软件包的部分内容将会发生变化
导致 50 个依赖项中的任何一个发生变化。绝大多数
这些更改不会更改 archivist
组件的接口
或行为。因此,我们只想将其应用于选定的子集
对文件包的内容进行检查
协定
理想情况下,我们验证的软件包内容的子集代表 合同。在 则合同:
- 在树中明确表达。
- 是该软件包的 OOT 用户可能依赖的确切内容。
- 可开箱即用,因此工具仅允许用户依赖 合同条款
精确定义这种“真实合同”很难,而且没有一刀切的定义。尤其是在构建 以及组件清单等文件的内容部分 功能路由或声明的更改不会导致 兼容性问题(例如,暴露新功能),而其他 通常如此(例如,依赖于新的传入 capability)。其他 更改兼容性与情境高度相关(例如,更改 命令行参数)。目前还没有关于以这种方式推理组件更改兼容性的指南。
我们打算只运送少量经过严格审查的包裹 直到我们能够提供有关兼容性的明确指导, 需经过 Fuchsia API 委员会审核。
本部分的其余内容介绍了检测更改的机制 对“真实合同”的不完善(过于宽泛)的表述 。其中还包括一组演示文件 文件子集的哈希值,并且我们会进行 API 审核 只要在此集合中检测到任何更改就会触发。我们将在 SDK 中将此表示法与软件包一起发布,以便 OOT 用户了解所提供的合同,但我们只允许 OOT 用户使用包含哈希的文件子集。
这意味着,我们会对软件包协定中的更改过于敏感(以免遗漏任何内容),并且在允许从软件包中使用的内容方面过于保守(以免限制允许的使用情况)。这样,我们就可以在树内构建时对 SDK 中分发的内容进行验证,并在 OOT 构建时确保不会出现虚假的运行时错误。
请注意,组件使用的 FIDL API 语义的变更 与此验证阶段正交。这可能会 使用兼容性测试进行测试。未来工作可能会 还包括进一步扩展合同,涵盖所提及的 FIDL 文件。
声明合同
在上一部分中定义的 SDK 软件包的构建规则中, 在文件包中包含预期文件列表以及这些文件 文件的“dispositions”。选择处置方式可提高灵活性 定义每个文件如何适应软件包的合同。
初始处置方式如下:
exact
- 确切的文件内容是协定。必须明确确认对文件内容所做的更改,如下一部分所述。internal
- 该文件不是软件包协定的一部分。
exact
匹配适用于内容为协定的文件
必须长时间进行维护以确保兼容性(例如,组件
清单)。
internal
表示某个文件不属于协定,但
是否出现在本地 build 中。
如果需要,可以添加其他处置方式作为 此 RFC。
验证合同
我们将使用熟悉的 .api
Golden 文件机制,
需要进行明确的 API 审核进行更改,以验证合同
许多 SDK 软件包
例如:
// sample-package.api
// The name of the file matches the name of the package with '.api' added.
// The file format is JSON.
// Note: Comments will not be present in the real .api files.
{
// Each top-level key is a file path in the resulting package.
"meta/sample.cm": {
// Hash is specified for files with "exact" disposition.
hash: "...",
},
"data/some-file.json": {
// If internal, this file is simply checked for presence
// at build time.
internal: true,
}
}
在构建时,将根据以下规则生成新的 .api
文件:
输入构建规则以及生成的软件包的内容(其中
所有未声明的文件输出都隐式地为 internal
)。如果
此文件的内容与对应的 .api
文件不匹配
签入到 //sdk/packages
下的源代码控制(通过深度 JSON 处理)
等式),则构建会失败。与其他 .api
文件不匹配问题类似,
新的 .api
文件会存储为 build 输出,
命令将 .api
文件复制到新的位置,
。通过此流程,您可以轻松地确认预期更改
并上传以供审核。
我们将记录评估 SDK 的 .api
不匹配问题的步骤
打包,并提供关于哪些更改可能不安全的指南。
.api
文件将在生成的软件包归档中提供
以便 OOT 工具可以使用它来验证
不依赖于软件包的内部详细信息。
当出现以下情况时,此方法可确保进行 API 审核的安全性: 其中包含一些有意义的更改 能够定义哪些更改是有意义的。
建筑
必须为以下项构建指定分发的软件包:
我们为其生成 IDK 的各种架构(在撰写本文时,arm64
和 x64
)。debug
和 release
等其他变种可能
可以根据需要引入,但最初会构建所有软件包
以供发布。
此过程的输出是一组文件内容和 每个 SDK 软件包的清单
此流程与现有 build 的唯一区别 有一个单独的软件包清单列表 。
子软件包中包含的二进制文件和共享库的调试符号 将生成并上传到相应的 GCS 存储分区。这个 与分发的二进制文件和共享库 直接在 IDK 软件包中安装
分类显示
构建完成后,软件包将按照 如下所示:
sdk://
├── blobs
│ ├── CONTENT_MERKLE_1
│ └── CONTENT_MERKLE_2
└── packages
├── arm64
│ ├── 10
│ │ ├── debug
│ │ │ ├── PACKAGE_BAR
│ │ │ │ ├── api
│ │ │ │ └── package_manifest.json
│ │ │ └── PACKAGE_FOO
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ └── release
│ │ ├── PACKAGE_BAR
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ └── PACKAGE_FOO
│ │ ├── api
│ │ └── package_manifest.json
│ └── 11
│ ├── debug
│ │ ├── PACKAGE_BAR
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ ├── PACKAGE_BAZ
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ └── PACKAGE_FOO
│ │ ├── api
│ │ └── package_manifest.json
│ └── release
│ ├── PACKAGE_BAR
│ │ ├── api
│ │ └── package_manifest.json
│ ├── PACKAGE_BAZ
│ │ ├── api
│ │ └── package_manifest.json
│ └── PACKAGE_FOO
│ ├── api
│ └── package_manifest.json
├── x64
│ ├── 10
│ │ ├── debug
│ │ │ ├── PACKAGE_BAR
│ │ │ │ ├── api
│ │ │ │ └── package_manifest.json
│ │ │ └── PACKAGE_FOO
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ └── release
│ │ ├── PACKAGE_BAR
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ └── PACKAGE_FOO
│ │ ├── api
│ │ └── package_manifest.json
│ └── 11
│ ├── debug
│ │ ├── PACKAGE_BAR
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ ├── PACKAGE_BAZ
│ │ │ ├── api
│ │ │ └── package_manifest.json
│ │ └── PACKAGE_FOO
│ │ ├── api
│ │ └── package_manifest.json
│ └── release
│ ├── PACKAGE_BAR
│ │ ├── api
│ │ └── package_manifest.json
│ ├── PACKAGE_BAZ
│ │ ├── api
│ │ └── package_manifest.json
│ └── PACKAGE_FOO
│ ├── api
│ └── package_manifest.json
└── subpackage_manifests
├── META_FAR_MERKLE_1
└── META_FAR_MERKLE_2
每个软件包清单都会放在一个构建的目录路径中
从目标架构和 API 级别着手:
sdk://packages/<ARCH>/<API_LEVEL>/{debug/release}/<PACKAGE_NAME>
。
这些路径将成为产品可以依赖的稳定 SDK 协定。此目录将包含软件包的 package_manifest.json
、
及其 API 文件进行验证。
此外,所有子软件包的清单都将按软件包存储
Merkle 放在单独的顶层目录中:
sdk://packages/subpackage_manifests/<META_FAR_MERKLE>
。
最后,所有 blob 都将存储为内容寻址名称
时间:sdk://blobs/<CONTENT_MERKLE>
。Blob 和清单目录
是软件包的实现详情,可能会随时间而变化。产品
不应依赖这些路径才能保持稳定。
这些软件包使用的二进制文件和库的所有依赖项的 LICENSES
文件都将打包并包含在规范 SDK 许可流程中。
使用这种基于目录的结构的原因有以下几种:
- 软件包可以发布到代码库或用作子软件包,而无需解析中间文件。
- 系统会自动删除重复的 blob,即使跨 API 级别也是如此,这在软件包包含相同 blob(例如共享库和二进制文件)的常见情况下可显著节省空间(与单独归档每个软件包相比)。
- 目录结构为支持多个 API 级别奠定了基础, 以及提供调试版本和发布版本。
- 与软件包代码库交互的工具已存在于
SDK(
ffx
和pm
)。 - 通过引用软件包清单启用子打包工作流 便于直接发布到代码库中。
这种方法的一个已知缺点是,如果将软件包声明为
顶级文件包和子包中在这种情况下,package_manifest.json
将被复制。软件包清单文件很小
足够让它们同时列为顶级文件、命名文件以及
我们不用担心由其软件包 Merkle 识别出来,这样我们就能够
更好地隐藏子打包详细信息。从整体上看,
将大于该负值。
使用 SDK 软件包
开箱使用 SDK 软件包与使用软件包的
package_manifest.json
与架构和期望相关
系统的 API 级别。软件包内文件的用法
并对照分发的 .api
文件进行检查。
这样,OOT 工具和 build 就可以针对常见错误 错误,例如:
- 取决于将被移除的已弃用软件包。
- 取决于将被移除的软件包中已弃用的文件。
- 依赖于软件包中不属于显式 软件包的合同规定。
实现此功能由 SDK 负责 每个构建系统的维护者,本文将在 。
包括
OOT 代码库可对 SDK 软件包 (R) 进行子打包, 本地软件包 (L),如下所示:
- R 的所有 blob 均存储在本地(下载或复制)。
- L 的子软件包列表包含对
meta.far
blob 的引用 - 当 L 发布到代码库时,R 的所有 blob(以及 R 子软件包的 blob(以递归方式)也包含在内。
然后,RFC-0154 中定义的子软件包解析将 适用于软件包 L。
此步骤可确保资源包在产品专用 并且可能会受到离线 blob 压缩和 并在此过程中进行其他优化远程 TUF 代码库 不得直接用作 软件包源代码(因为其中包含的 blob) 可能需要进一步处理,以符合 产品)。
请注意,此步骤将使用现有的软件包和 blob 发布工具(例如 ffx package
),这些工具可能会通过执行“insert-if-absent”操作来针对重复的 blob 进行优化。
性能
此设计不会影响设备端性能。
这种设计可能会对构建性能产生负面影响,因为它会导致创建新的归档文件。不过,这么做的影响
仅在创建 SDK 归档文件 (build_sdk_archives=true
) 时才会发生
)。与花在完整构建上的时间相比,
创建新归档的开销应该可以忽略不计。
工效学设计
对于平台所有者来说,这种设计提供了一个符合语言习惯的界面
定义应将哪些软件包作为 Fuchsia 的一部分进行分发
平台版本此外,为此用例引入 .api
文件可减少预测代码更改对一组 SDK 软件包的影响的认知工作量。此外,内置的自动检查可确保平台所有者对 SDK 软件包所代表的合同保留完全控制权。
向后兼容性
发布软件包以供开箱使用 语义和命名方式保持一致。此 RFC 建议使用 API 来为软迁移提供支持,并提供移除 或对合同进行重大更改。
安全注意事项
此方案允许使用针对 Fuchsia SDK 编写的 OOT 软件 直接添加和参考 SDK 版本。虽然软件本身是开源的, 在编译和分发二进制文件时采用
该过程可能与分发预构建二进制文件的过程类似。 IDK 中,我们提供了包含 我们生成的二进制文件由于归档包含 包含 SDK 软件包的辅助归档文件(IDK) 此外,发行版还会提交到 辅助归档。
隐私注意事项
此设计不会改变用户数据的处理方式。
测试
在树内,我们将提供软件包的黄金文件 (.api
) 测试
将从树中公开(请参阅上面的“验证”)。
我们将向示例代码库添加 OOT 测试,以展示如何使用从 SDK 中包含的软件包。我们将进行一项树内测试
尝试以最佳方式模仿此 OOT 测试的行为
可能性;这将是 OOT 阶段的端到端测试
但就底层 ffx
命令而言
而无需运行 Bazel 支持
如果树内结构支持 Bazel,我们将进一步 一项用于注入生成的 SDK bundle 的测试 使用包含的工具对 SDK 软件包进行子打包。
文档
从命令行添加、修改和移除软件包的流程:
https://fuchsia.dev 上会记录 SDK 软件包集。
其中包括有关如何配置 .api
测试的说明。
缺点、替代方案和未知问题
直接在 SDK 中分发软件包的替代方案
此 RFC 的先前版本提出了一种 通过 CIPD 单独归档和上传 SDK 软件包; 减少对 SDK Bundle 的任何大小更改。同样,SDK 用户 不需要软件包分发,因此提供可选的归档 意味着这些用户不需要下载
虽然这种模式适合未来的工作,但并不是
SDK build,并且需要新的构建工具来强制执行 SDK 类别,
.api
测试以及用于下载和使用这些测试的新工具
SDK 软件包开箱即可。
为了解决 SDK 大小问题,我们将仅包含一小部分用于启用测试用例的初始 SDK 软件包。例如,我们可能会认为 以包含:
- driver_test_realm
- Archiveist-for-embedding
- log-encoding-validator
- realm_builder_server
- test_ui_stack
这些作为 x64 的发布 build 构建的软件包加总约为 43 MiB 未压缩或使用默认 gzip 设置压缩后 18 MiB。这似乎是 这对目前的 SDK 而言比较合理(压缩后 700 MiB)。
随附的任何其他软件包将接受 Fuchsia API 委员会的审核 更改的内容和大小。此外,这一功能的未来迭代 框架可以指向远程存储的 blob,以进一步帮助解决 。
使用目录结构的替代方案
不使用目录结构来强制执行架构和 API 那么顶层的文件可以在 JSON 格式中呈现同样的信息 格式。与相比之下,这种设计的优点在于文件的可伸缩性 更改目录结构然而,解析此类文件 其他 SDK 工具,但这种工具的优势目前尚未得到大家的理解。
此类工具的未来迭代版本可能会不再采用目录结构,如果出现这种情况,我们会构建并提供所需的工具。
先验技术和参考资料
Fuchsia 软件传送是一种更新软件系统的新方法,该领域有许多先前技术,这些文档对此进行了详细说明: