RFC-0154:子软件包

RFC-0154:子软件包
状态已接受
领域
  • 软件交付
说明

允许软件包声明对特定于版本的子软件包的依赖项,以提高封闭性和可再定位性。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-01-25
审核日期(年-月-日)2022-03-23

摘要

此提案旨在在 Fuchsia 中添加对定义和解析的支持, 子软件包。子包是从一个包(“A”)到另一个包的指定引用 软件包(“B”),指定一个单向依赖项(从“A”到“B”)。子文件包 还可以包含子软件包,从而创建 从顶级软件包中获取版本固定的软件包。对于子文件包 软件包“A”中的资源可以引用软件包“B”按其子文件包名称命名 由包含的软件包定义。如果为“B”是“A”的子包,那么任何 使“A”可用(例如 Fuchsia 存档、Blob 存储区) 也必须使“B”可用。因此,子软件包支持两个重要的 系统属性:

  • 封闭性 - 将程序(或测试)与特定软件包打包 依赖项
  • 原子性 - 系统应保证如果包含子包的包 其所有子包都将成功解析 已解决。(本 RFC 中提出的方法将尽快 先解析子软件包的整个层次结构,然后再返回成功的 返回的结果。)
  • 可再定位性 - 软件包可以分发,具有所有必需的 存储到单个归档(例如进行下载)中, Fuchsia 软件包服务器。

设计初衷

此 RFC 提出了一种表达和支持嵌套 软件包间依赖项(子软件包)。而软件包目前 允许依赖于其他软件包,此 RFC 将限制进行了修改, 软件包不得依赖于其他软件包,除非通过包含来依赖于其他软件包。

请注意,虽然本 RFC 中讨论的初始激励用例 与测试相关的子软件包在其他场景中可能很有用, 。子包可以先进行测试,因为我们能够 以在生产环境中无法接受的方式进行测试。 除了安排时间考虑因素之外,其目的是完全支持 测试和生产场景。

为何此时推荐?

当前最普遍的应用场景推动了软件包间的需求 依赖项处于测试场景中。测试需要执行特定组件 (通常是虚构的或模拟的)放在一个封闭环境中, 测试。请务必依赖并包含依赖项的确切版本 以防出现全局系统状态(即刚好 安装)影响测试的正确性。我们称之为属性 “hermetic packaging”。

我们通过将所有组件依赖项包含在 一个软件包然而,树外规则 (OOT) 则是 。这些测试没有经过封闭打包, 作用是将平台软件包网址设为隐式 ABI。

例如,目前,Flutter 集成测试声明了对 Fuchsia 的 “风景”组件(按组件网址表示); fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cm。这里仅声明了 超出预期的 Fuchsia 平台会带来一些风景 可通过该网址引用获取。如果是“风景”,或其某个依赖项( 测试开发者还必须发现和声明)更改其接口, 则测试可能会在运行时失败。这已经是一种常见的 测试中断的根源,而情况只会随着我们扩大规模而变得更糟。

相比之下,使用子软件包,Flutter 集成测试作者可以:

  1. 在构建时获取包含特定版本的 Anthos 的软件包。 (其机制不在范围内)。
  2. 将该软件包添加为其测试软件包的子软件包。
  3. 在其测试的scenic#meta/scenic.cm 组件清单,而不是 fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cm

此版本的测试更加严格,将始终以相同的方式运行, 无论安装在设备上的哪些 代码。

注意:子软件包支持像这样的 OOT 测试场景,但 可在测试中重复使用的 Fuchsia 平台软件包分发 RFC 范围。不过,在 RFC 获得批准后,我们会 将此过程定义为 Fuchsia SDK 的扩展。

我们致力于在树内和 OOT 之间提供一致的体验 组件:单个测试软件包,明确声明其所有封闭 依赖项,作为原子组提供给测试。

您无需从树内(需要 构建包含多个组件的新软件包),我们应改为 提议以子软件包嵌套的形式支持软件包间依赖项。 这使我们能够独立打包和依赖于组件,而无需 复杂的命名空间注意事项。

利益相关方

教员:hjfreyer@google.com

审核者

名称 聚焦区域
wittrock@google.com 塞尔维亚第纳尔
jsankey@google.com 塞尔维亚第纳尔
geb@google.com 组件框架
shayba@google.com 构建
etryzelaar@google.com 工具
aaronwood@google.com 组装/包装移动
cgonyeo@google.com RealmBuilder
ampearce@google.com 安全

已咨询:Computerdruid@google.com

社交化

在发布此 RFC 之前,SWD 团队参与了该 RFC 的初稿。 组件框架团队会及时获知工作进展 并在每周例会上就相关范围和功能提供反馈 CF 团队的成员之一。

设计

受子软件包影响的用例

  • 嵌套软件包依赖项的软件包内声明 <ph type="x-smartling-placeholder">
      </ph>
    • 软件包可以包含列出其子软件包的元数据文件, 系统必须能够根据 软件包目标的依赖项
  • 相对组件分辨率 <ph type="x-smartling-placeholder">
      </ph>
    • 例如,开发者可以替换对组件的引用, fuchsia-pkg://servername/some-package#meta/child_component.cm 替换为 相对引用 child_package#meta/child_component.cm;其中 child_package 是引荐来源网址(或“父级软件包的”)的指定引用 到 some-package,在构建时被版本锁定(通过软件包哈希)。
  • 软件包依赖项树遍历 <ph type="x-smartling-placeholder">
      </ph>
    • 例如,通过发布一个顶级文件包, 可以使用嵌入的软件包引用自动查找和加载 软件包依赖项
  • 软件包树归档 <ph type="x-smartling-placeholder">
      </ph>
    • 若要从给定的顶级包构建单个文件,其中包含 软件包和所有依赖项。

子软件包表示法

软件包通过 子软件包,映射到 所定义的软件包的 package hash 相同的软件包存储区和软件包集中。(例如,对于一位家长 从“宇宙”解决的其子软件包时,还必须解析其子软件包 来自“宇宙”软件包集)。RFC 的其余部分描述了 将这些声明表示为一个名为 meta/subpackages 的文件 匹配 meta/contents 当前使用的格式 (=)。

如果存在,此类文件必须至少包含子软件包的名称及其 软件包哈希(引用的 meta.far 的“内容地址”) 子软件包)。

子软件包名称在单个 meta/subpackages 文件中必须是唯一的,但 它们无需在软件包之间保持唯一因此,一个包 可以控制其子包的命名,而且多个包可能会包含 同一个子包下,以不同的本地名称命名。

子文件包引用被视为“私有数据”引用软件包 例如,父级软件包 A 可能包含两个子软件包:软件包 BC(通过各自的软件包哈希引用)。软件包 B 可能还需要 添加对同一软件包哈希 C 的子软件包引用。既不是 顶级父级 A 及其子软件包 B 都知道共同的 依赖项。(换句话说,父级软件包只能解析其子软件包, 再往下一级父级无法直接将软件包网址解析为子文件包 子文件包的一个子文件包事实上,此 RFC 没有定义用于表示 子包的子包中。)

注意:单个子文件包也可以独立发布和提供 顶级软件包(例如,正式版组件可以发布为 由 fuchsia-pkg 网址标识的顶级组件,但该组件的 软件包也可作为子软件包包含在一个或多个封闭测试中。) 按 Fuchsia 软件包 URI 解析顶级软件包时,默认行为 检索该软件包最近发布的版本。子文件包 另一方面,请始终以“软件包哈希”来表示特定版本。

构建包含子软件包的软件包

向文件包添加子文件包的模式与添加常规文件包类似 文件,但需要注意一些特殊事项。

各种 Fuchsia SDK 构建系统(目前包括 GN 和 Bazel)通常都支持 创建“package”目标,后者可将对其他目标的依赖性 包含在软件包中。在这些构建系统中,添加对 软件包会通知构建系统也生成依赖软件包,但是 目前未对生成的软件包中的该依赖项进行编码。例如: 以下 GN 代码将只导致软件包“B”生成 Deployment 的 文件包“A”

# GN format
fuchsia_package("A") {
  deps = [
    ":B",
    other_deps,
    ...
  ]
}

fuchsia_package("B") {
  ...
}

对 fuchsia.git 中现有的软件包间依赖项进行的粗略调查 发现,在大多数情况下,如果目标软件包依赖于另一个软件包, 目标软件包中的组件需要加载依赖的软件包。在 在这些情况下,依赖软件包可以与目标捆绑在一起, 子软件包。但是,由于现有的 GN 规则并不强制实施这种解释, 无法推断。

因此,此 RFC 建议使用显式变量 subpackages 声明子软件包目标。subpackages 列表中的每个目标都必须导致 生成的 fuchsia_packagemeta/subpackages 中的相应条目 文件。

此列表中的目标也推断出一个 build 依赖项,因此 subpackages 不需要同时显示在 deps 中。要将软件包转换为 声明了软件包依赖项,并关联到包含子软件包的软件包; 将所有(或已选择的)软件包目标从 deps 移至 subpackages。从概念上讲, 对 fuchsia_package 的更改可能如下所示:

# GN format
fuchsia_package("A") {
  subpackages = [ ":B" ]

  deps = [
    other_deps,
    ...
  ]
}

fuchsia_package("B") {
  ...
}

构建系统应将子软件包名称默认为包含目标的名称, 但可以支持使用常用习语覆盖此名称。

为了便于使用现有工具构建包含子软件包的软件包,请构建 必须在 Fuchsia 树内构建系统中更新规则和脚本; 应在 Fuchsia GN SDK 和下游构建环境中更新。 (在已知程度上,受影响的构建环境和所需的更改 实现部分中的说明)。

解析子软件包

子软件包解析可解析对某资源包的已命名子软件包的相对引用, 已知软件包。

fuchsia.pkg.PackageResolver 协议的 Resolve 方法将为 修改为返回 ResolutionContextResolve 只能解析 软件包网址。将添加一个额外的方法 ResolveWithContext,以接受 一个额外的 context 参数 (ResolutionContext),还会返回一个新的 ResolutionContext(针对已解析的软件包)。ResolveWithContext 将会解析 绝对软件包网址(忽略 context)或相对软件包 网址。这些更改由以下概念性 FIDL 代码段表示:

   library fuchsia.pkg;

   ...

   const MAX_RESOLUTION_CONTEXT_LENGTH = <TBD>;
   type ResolutionContext = bytes:MAX_RESOLUTION_CONTEXT_LENGTH;

   protocol PackageResolver {

     /// Resolves an absolute component URL.
     /// ...
     Resolve(resource struct {
         package_url string;
         dir server_end:fuchsia.io.Directory;
     }) -> (struct {
         resolved_context ResolutionContext;
     }) error ResolveError;

     /// Resolves a component URL, which may be absolute or relative. If
     /// relative, the component will be resolved relative to the supplied
     /// `context`.
     ResolveWithContext(resource struct {
         package_url string;
         context ResolutionContext;
         dir server_end:fuchsia.io.Directory;
     }) -> (struct {
         resolved_context ResolutionContext;
     }) error ResolveError;

     ...
   }

注意:针对上下文选择字节数组是 一些 FIDL 限制,并仔细考虑不同意见。对于 其他背景信息,请参阅“替代方案”部分: 使用特定于上下文的 Resolver替代方案:用于解析上下文值的 FIDL 类型表示法

对于 package_url 中声明了子软件包的软件包(“父级”软件包), 必须将返回的 resolved_context 作为 context 输入参数进行传递 在解析子软件包时对 ResolveWithContext 进行后续调用。(如果 调用方未解析所解析软件包的子软件包,则该调用方可以 忽略返回的 resolved_context。)

子软件包实现不得降低稳健性和存续性 现有服务和组件也就是说,子包不得导致 需要标记为“关键”的非关键(可重启)组件,以及 无状态协议(例如 fuchsia.pkg.Resolve()ResolveWithContext,它将替换对 Resolve 的某些调用)必须保持 无状态

context 值的实现可能会受此限制条件的影响。 ResolveWithContext 必须接受 Resolve 退回的所有context,或 ResolveWithContext,前提是父软件包在 系统。(从概念上讲,父级软件包的软件包哈希足以 无状态解析同一 Merkle-hash-indexed 软件包中的子软件包 store.)

注意:确定父级是否仍处于“正在使用”状态是 需要在实现过程中解决的软件包垃圾回收 软件包解析器中的子软件包的总数。

对于绝对 package_urlResolveWithContextcontext 实参为 已忽略。

注意:由于此 RFC 将子软件包解析限制为声明的子软件包 父路径(向下一级)时,相对路径不得包含斜杠 (/)。

从子软件包加载组件

组件解析器负责解析组件网址,并对其进行转换 一个软件包网址,然后对其进行解析,然后加载一个组件清单 来自已解析的软件包已加载的清单和软件包内容用于 创建一个新组件,其中包括用于解析子软件包的上下文, 相对路径。

fuchsia.component.resolution.Resolver 协议的 Resolve 方法 只会解析绝对组件网址。还有一种方法, ResolveWithContext,将添加一个额外的 context 参数 (a fuchsia.component.resolution.Context)。ResolveWithContext 将会解析 是绝对组成部分网址(忽略 context)或相对组成部分 组件网址。这些更改由以下概念性 FIDL 表示 片段:

   library fuchsia.component.resolution;

   ...

   // Note the context length, in bytes, must be at least the size of
   // fuchsia.pkg.MAX_RESOLUTION_CONTEXT_LENGTH, plus the size required to
   // accommodate additional component context information, if any.

   /// The maximum number of bytes for a `Context`.
   const MAX_RESOLUTION_CONTEXT_LENGTH uint32 = 8192;

   /// A byte array that persists a value used by the target `Resolver` to locate
   /// and resolve a component by relative URL (for example, by a subpackage
   /// name).
   alias Context = bytes:MAX_RESOLUTION_CONTEXT_LENGTH;

   protocol Resolver {

     /// Resolves a component with the given absolute URL.
     /// ...
     Resolve(struct {
         component_url string;
     }) -> (resource struct {
         component Component;
     }) error ResolverError;

     /// Resolves a component with the absolute or relative URL. If relative, the
     /// component will be resolved relative to the supplied `context`.
     ///
     /// `component_url` is the unescaped URL of the component to resolve, the
     /// format of which can be either:
     ///
     ///   * a fully-qualified absolute component URL
     ///   * a subpackaged-component reference, prefixed by a URI relative
     ///     path to its containing subpackage (for example,
     ///     `child_package#meta/some_component.cm`); or
     ///   * a URI fragment to a component in the current package (for
     ///     example,`#meta/other_component.cm`)
     ///
     /// `context` is the `resolution_context` of a previously-resolved
     /// `Component`, providing the context for resoving a relative URL.
     ResolveWithContext(struct {
         component_url string;
         context Context;
     }) -> (resource struct {
         component Component;
     }) error ResolverError;
   }

返回的 Component 类型将修改为包含 resolution_context,在解析与此相关的其他组件时使用 Component

   type Component = resource table {

       ...

       /// The context used to resolve `component_url`s relative to this
       /// component.
       5: resolution_context Context;
   };

例如,在解析名为 parent 的组件后, 通过对组件的后续调用来解析 ResolveWithContext(subpackaged_url, parent.resolution_context)

对于绝对 component_urlResolveWithContextcontext 实参为 已忽略。

对于相对 component_url

  • 客户端必须使用 ResolveWithContext。使用亲属调用 Resolve 组件网址将返回错误代码 ResolveError::INVALID_ARGS

对于子打包的相对组件网址:

  • component_url 以相对路径(子软件包名称后跟 特定组件的基于 # 的 fragment,例如 child_package#meta/some_component.cm)。
  • 如果软件包解析器返回 PACKAGE_NOT_FOUND(或一些等效项) 软件包存储相关错误),组件解析器必须返回 ResolveError:PACKAGE_NOT_FOUND

对于相对资源组件网址(来自与另一个软件包相同的软件包) 组件):

  • component_url 是 URI 片段(例如, #meta/other_component.cm,如 RFC-0104:相对组件 网址
  • 该 fragment 必须引用与另一个 fragment 属于同一软件包中的组件 组件(“对等”组件)。
  • context 值必须引用相同的软件包版本(相同的软件包) 哈希值)。

注意:使用 context 解析相对资源组件网址(通过 URI) fragment) 用于改变相对解析器的当前行为。目前的相对 解析器通过其父组件构造绝对包网址,以及 附加相对 fragment 部分。此行为不能保证 检索同一软件包中的父组件和子组件 版本。对于子软件包,并非总能构建一个绝对 来自父组件或对等组件的软件包网址。另一方面,将 特定软件包哈希(用于解析 软件包可能是期望的行为;在这种情况下,使用上下文 改进。

代表使用相对 URI(路径或片段)解析组件网址时 (即“父组件”),组件管理器必须 将分辨率委托给用于解析 Resolver 父组件。

上下文值应被视为 组件解析器,对客户端不透明。Resolver 可以 转发 resolved_context 值(从 PackageResolver::Resolve...())作为组件分辨率 context 值。 (API 不会阻止 Resolver 返回不同或 增强的上下文值,但可能没必要这样做。)

解析包含子软件包的组件的过程的概念示例 子文件(假设为启动后环境)如下所示:

  1. 如需从顶级软件包 P 加载组件 A,请执行以下操作:

    a. 组件管理器为以下各项获取已注册的 Resolver 功能: fuchsia-pkg 架构,并调用 Resolver::Resolve("fuchsia-pkg://fuchsia.com/package-p#meta/component-a.cm") ,了解所有最新动态。

    b. Resolver 会提取软件包网址并调用 PackageResolver::Resolve("fuchsia-pkg://fuchsia.com/package-p", ...), 返回用于加载组件的 fuchsia.io.Directory, 和 resolved_context 值。

    c. Resolver::Resolve() 构造并返回解析后的 Componentresolution_context,组件管理器会缓存在哪个位置 该组件的状态。

  2. 如需从子软件包 child-package 加载子组件 B,请采用相对路径 至上述组件 A

    a. 组件管理器会获取 Resolver 功能,用于解析 component-a和通话 Resolver::ResolveWithContext("child-package#meta/component-b.cm", component_a.resolution_context)

    b. Resolver 会提取相对软件包路径(即 子包名称),并将 组件 context 输入参数中的 package_context,以及调用 PackageResolver::ResolveWithContext("child-package", package_context), 返回用于加载组件的 fuchsia.io.Directory 以及子包自己的 resolved_context 值。

    c. Resolver::ResolveWithContext() 会构造并返回 通过新组件的 resolution_context 解析了 Component, 组件管理器会在该组件的状态下缓存哪些组件。

未来工作

  • 合并 subpackagescontents 文件 - 如果及 针对永久性 FIDL 软件包的提议 RFC 内容为 子软件包文件应进行更新,以匹配新的 格式替换 contents,或者将文件合并,使用展开后 字段以区分内容条目和子包条目。
  • 使用汇总统计信息为 subpackages 文件条目添加注释 - "永久性 FIDL 软件包内容"RFC 将提供一种机制 通过每个子软件包到软件包的哈希映射来获取额外数据。它的 这有助于加入汇总统计信息,例如内容大小(包括 每个子软件包及其嵌套依赖项的汇总大小)。可以是 例如,在文件包提取操作期间用于生成进度报告 。
  • 改进运行时依赖项解析 - 另一个概念 我们考虑(对于未来的 RFC)支持带有 绝对路径(即以正斜线“/”为前缀),用于请求顶级 “同一个软件包服务器”中的软件包投放了“当前”软件包(或 组件)。(当前软件包服务器的可知方式相同 从当前软件包中解析子软件包网址是可理解的, 具体说明。)
  • 用于解析和加载资产包内容的通用 FIDL 服务 - 目前,Fucsia 软件从文件包中加载内容的唯一方法是 其中包含一个组件自定义组件将是 负责路由目录或提供 FIDL 协议以预配 资源包中的资源。正在考虑在未来的提案中包含 更易使用的 FIDL API,用于加载其中的子软件包和/或资产 软件包。例如,这可用于分发视觉资源或 可选择的用户界面“主题”资源包,而不需要 添加实现组件中介的额外步骤。
  • 延迟加载子软件包 - 此 RFC 提议将一个软件包和所有 顶级软件包将(以递归方式)加载 即,即刻加载。建议的未来延期将允许指定的 子软件包声明为“可延迟加载”。此方法可用于避免 将软件包从远程服务器导入存储空间受限的费用 直到明确请求指定的软件包为止。(请参阅 Eager 软件包加载。)
  • 对子打包组件的热重载 - 此 RFC 提出了一个严格的 软件包间版本控制的实现:软件包哈希通过 其内容的哈希值,包括其子软件包哈希值。因此,如果 子包的变化(以子包的包哈希的变化表示); 父级软件包也已更改。为实现上述目标 而无需重新加载父软件包(例如“热重载”组件) (反之亦然)。如果允许此类行为, 会削弱子软件包的一些预期优势,包括可靠性和 安全性。此外,组件管理器如何解析 并重新加载新版组件(请参阅 https://fxbug.dev/42145182)。如果 这些已知问题已经得到解决, (在将来的提案中)进行了修改,允许父级软件包声明某些 (行为、API 和/或 ABI 保证、 )。

实现

Fuchsia 树内构建系统(GN 规则和脚本)

构建规则、脚本和文件格式(包括 必须更新 Fuchsia 软件包生成)以转发 subpackages 列表 并最终生成 新的 meta/subpackages 文件以及软件包 contents 文件。

对于 subpackages 中的每个软件包目标,meta/subpackages 文件必须映射 声明的子包名称(默认为子包目标名称) 子包目标的包哈希(即 子软件包的 meta.far。)

一些可能需要更改格式(以便添加 子软件包引用)或同一阶段的补充文件(例如 contents 将与 subpackages 配对)包括:

  • 软件包“build 清单”(或 archive_manifest),该路径通常结束于 在扩展程序“.manifest”中
  • package_manifest.json

Fuchsia 树外构建环境(GN、Bazel 和支持脚本)

应以与 Fuchsia 类似的方式更新构建规则和脚本 树内构建规则和脚本,用于在树外启用子软件包 代码库受影响的代码库包括 chromium 和 flutter/engine。(注意 Flutter/engine 目前正在使用经过修改的 GN SDK 构建规则副本 因此即便是类似的脚本,也可以在不完全相同的情况下 。)

  • package.gni 使用 --manifest-path 调用 prepare_package_inputs.py,以 生成 ${package_name}.manifest(称为 archive_manifest 或“构建清单”(请参阅 pm CLI 文档)
  • pm_tool.gni 使用 -m ${archive_manifest} 调用 pm build-output-package-manifest,用于生成 package_manifest.json

软件包管理器软件包命令行界面 (CLI)

系统将通过更改 ffx package 来修改软件包管理器命令。

注意:pm 命令即将被废弃,取而代之的是 ffx package。变更 只有在工作流需要时才会执行,并且无法更新为 pm 请使用 ffx package 替换命令。

  • ffx package build(原价:pm build) <ph type="x-smartling-placeholder">
      </ph>
    • 此命令需要更改为接受其他参数和 文件或经过修订的输入和输出文件格式,以包含 额外的子软件包声明。
  • ffx package export(原价:pm archive) <ph type="x-smartling-placeholder">
      </ph>
    • 此命令会读取 contents 文件并生成 .far 文件 包含所有引用的 blob。
    • 对于包含 subpackages 的软件包,export 行为将会扩展 文件,以(以递归方式)将这些子包捆绑到生成的归档中 请参阅捆绑软件包依赖项 分发
  • 其他 ffx package 命令可能需要进行相应更改才能适应 subpackages(例如 downloadimport)。对这些问题的影响 命令会在实现时予以研究。这些更改 或受到其他计划内更改的影响, RFC-0124:分散式产品集成:工件说明和传播

捆绑软件包依赖项以进行分发

子软件包的主要用例之一是支持 确保软件包及其所有直接和间接依赖项 重新定位到软件包。与开发 以递归方式遍历子软件包依赖项的过程。

ffx package 工具是一种逻辑工具,用于实现识别和 查找每个子软件包的内容(展开后的软件包目录或 .far) 归档)。

您无需承担为封闭式实现自定义脚本的负担 打包,此 RFC 建议扩展软件包 具有子软件包感知能力, 子软件包依赖项。例如,ffx package 会扩展为 bundle 封装成一个单文件存档。

封闭式软件包存档的建议格式为单个文件 所有贡献包的扩展内容,生成一个索引, 所有软件包中所有 blob 的平面集合。

软件包解析器

必须更新 fuchsia.pkg.PackageResolver 协议的实现 来实现上述设计部分中所述的行为。

Eager 软件包加载

软件包解析器必须在内部以递归方式解析所有子软件包,直到 所有子包都已被抓取,然后返回 root 软件包。这种方法旨在简化 原子性属性,因此所有必需的子软件包都必须 之前已解析完毕

强制执行 Eager 软件包解析可能也支持 经批准的 RFC-0145: Eager Package Updates, 值得注意的是,软件包及其子软件包树可用于实现 Eager 软件包更新 RFC 的“软件包组”前提条件。

组件解析器

fuchsia.component.resolution.Resolver 协议的实现必须 进行了更新,以实现上述设计部分中所述的行为。

RealmBuilder

必须更新 RealmBuilder,以包含声明子软件包的功能,并且 响应在组件中使用相对路径的组件解析请求 网址。使用 RealmBuilder 的封闭测试目前无权访问 软件包解析器。支持通过以下方法加载封闭捆绑的软件包: 子软件包声明,必须实现并使其可供封闭式 和系统测试

性能

遍历子软件包可能会增加识别所有 blob 的延迟时间 通过遵循多个级别的间接下载,尽管这不太可能 在实践中具有重要意义。例如,与使用 N 加载单个软件包相比, blob 来加载包含子软件包(这些子软件包总共具有 N 个唯一 blob)的软件包:

  • 育有子女 <ph type="x-smartling-placeholder">
      </ph>
    • N 个 blob

  • 育有子女 <ph type="x-smartling-placeholder">
      </ph>
    • N_0 个 blob
    • Child1 <ph type="x-smartling-placeholder">
        </ph>
      • N_1 个 blob
      • Child2
      • N_2 个 blob

在第一种情况下,所有 N 个 blob 都是预先知道的,并且可以并行加载。 在第二种情况下,软件包解析器必须依次访问 以累积要加载的全部 N 个 blob。此遍历 会导致额外的延迟时间(取决于树的深度)。如果延迟时间 导致递归 blob 加载成为问题,在这种情况下, 来缩短延迟时间例如,子包链接可在 与加载 blob 或整组子软件包内容地址并行执行 都可以在构建软件包时包含。

向后兼容性

相对资源组件网址的解析行为

解析现有软件包网址和组件网址的行为是 只是一个例外:相对资源组件网址(以 URI fragment,例如 #meta/child.cm)将需要组件 context(该组件 建议的实现可以保证),但行为略有变化。 当前的相对解析器将该 fragment 连接到父 fragment 组件的软件包网址,然后重新解析软件包和组件。但是 可能正在加载新版本的软件包。我们认为这是 原始实现的潜在风险领域。context将 可以保证从同一个软件包中解析相对组件, “父级组件”已解决。

解释已解析组件的 fuchsia.component.resolution.Package

解析组件后,返回的 fuchsia.component.resolution.Component 类型包含 package 类型的字段 fuchsia.component.resolution.Package,其中包含对 包含所返回 Component 的软件包的 package_url

   type Package = resource table {
       /// The URL of the package itself.
       1: url string;

       /// The package's content directory.
       2: directory client_end:fuchsia.io.Directory;
   };

由于子软件包始终是相对的,因此 package_url 的任何现有用法 将受到影响本 RFC 中描述的子软件包解析过程不 使用存储在 Package 类型中的任何信息,因此对 Package 所做的任何更改或者 该类型的解读方式,不会对子包产生实质性影响 RFC 设计。

在实现时要考虑的替代方案包括:

  1. 存储仅引用软件包服务器和软件包的 url 哈希值(例如 fuchsia-pkg://fuchsia.com?hash=123456789),即 足以重新解析子包的内容,但不会 并保留有关父组件或其子软件包名称的所有信息。
  2. 将子软件包路径存储在 url 中,并添加可选的 解析上下文字段(即组件包的上下文, 解析为)到 Package

FIDL 方法变更

fuchsia.pkg.PackageResolverfuchsia.component.resolution.Resolver 时,Resolve() 方法会同时 以返回新的已解析上下文,以及调用 将添加 ResolveWithContext() 以支持 context 输入参数。 这可能不需要软转场。

软件包表示法的变更

在 Fuchsia 中添加子软件包不会改变未 代表所有子软件包现有的文件包网址和组件网址不会 。开发者可以选择替换 组件网址(使用 fuchsia-pkg://fuchsia.com/top-level-package#...,对于 实例)及其某个子软件包的引用(使用 child-package#...,将 child-package 映射到 top-level-package)。

工具变更

旧版 Fuchsia、软件包服务器和一些主机端工具(例如 pm) 和 ffx package)不支持通过子软件包发布的软件包,或者 使用软件包的相对路径来部署软件Fuchsia 系统和主机端工具 需要更新并重新编译。

软件包名称语法

子软件包名称的范围限定为父级软件包,实际上就是别名 。也就是说, 无需镜像软件包名称的语法。

不过,使用相同的名称来引用软件包可能很常见 作为子软件包使用

由于子软件包是分层的,因此可以很自然地考虑嵌套子软件包 可命名,或许可以使用斜杠作为分隔符。例如: fuchsia-pkg://fuchsia.com/parent/child/grandchild#gc-component.cm,或者 child/grandchild#gc-component.cm。请注意,此 RFC 既不会制裁, 会禁止此类表示法,但使用正斜线分隔符会暴露出 潜在陷阱

目前,软件包名称可包含一个斜杠。它们后面的内容 被视为“变体”,例如 /0,这目前在紫红色中很常见。

值得注意的是,/0 已被弃用,如果从常见用途中移除, 可以释放用于子软件包的 / 的含义(尽管这不是 且需要受将来的 RFC 的约束)。

如果存在针对软件包命名层次结构的竞争用例,则替代 分隔符(例如 :)可用于子软件包嵌套,或者 子软件包可以使用 /,而替代分隔符可用于 另一种含义。

此 RFC 建议从允许的子软件包名称中至少预留 /: 语法。

安全注意事项

可审核性和可执行性

迄今为止,提议的设计已考虑了来自 Fuchsia 的评论 安全负责人:需要最大程度地减少对 并简化可能影响可审核性和更改的审核 可执行性。

例如,父级及其所有子软件包(递归)必须源自 来自同一软件包存储区,并且被视为同一软件包集的一部分 (例如,全部来自“base”或“宇宙”)。使用相同的软件包 表示子软件包层次结构中的所有软件包都适用相同的 可执行政策

基于软件包或组件网址的系统许可名单

目前在特权许可名单中标识的软件包或组件 可能会以子软件包的形式包含在内,但需要一些 从而保留其所需特权。外部 父软件包不得包含符合以下条件的子组件的子软件包: 需要比父级更多的特权。在封闭测试中运行的组件 要求为此规则的例外情况,但如果某个已列入许可名单的软件包或组件 在封闭测试中用作子软件包时,特权功能应 如果可能,请替换为模拟文件或其他等效的替换文件。

如果这种限制成为其他系统改进措施的障碍, 应咨询安保团队,了解其他便利措施。

对特权软件包解析操作的受控访问

目前,鉴于采用平面软件包命名空间,PackageResolver 客户端能够 读取blobfs中的所有内容;因此,从这个网址向 软件包服务器以及读取其 BLOB 都是特权操作。

子软件包将仅提供 blobfs 软件包的指定子集 (目前为一层深度),但声明依赖于另一个 软件包将无法再读取该软件包的内容。此 RFC 建议对非特权客户端可以执行的操作保持相同的限制 从子软件包中请求功能或资源时。

从特定软件包运行的组件可能无法查看其内容 子软件包,但它们可以查看 meta/subpackages 文件并解析其 手动处理的内容。

与包含依赖项的现行方案相比,这种设计提高了安全性。 组件。在这个解决方案下, 组件可能会随意查看彼此的内容。因为组件 可能只能看到运行它们的软件包,而无法看到 子软件包,此设计会隐藏这些实现细节。

实施风险:主管屏蔽

如果我们实现深度优先软件包解析,则嵌套的子软件包 DAG 其他软件包的块解析这可能会导致解析器出现死锁 在解析其 子软件包。

由于 RFC 规定将仅实现 Eager 解析, 实施人员可以确保父级无需 在发布解析器资源以解析子软件包之前完全解决。

隐私注意事项

隐私状况预计不会受到任何影响。

测试

我们将添加单元测试,以验证附加功能。现有账号 有助于发现意外回归问题。

主机端打包测试将验证 ffx package(和/或 pm(如有必要)和相关工具。

将编写封闭的集成测试,用于从 子软件包。

文档

以下已知文档需要更新:

  • 描述 Fuchsia 软件包网址和组件网址的现有文档 (以说明如何引用子软件包)、软件交付文档 和组件框架概念文档。目前显示的示例 包含 fuchsia-pkg:// 个网址引用的 CML 示例可以通过 使用子软件包的示例)。
  • 应更新有关相对组件引用的文档,以说明 与子打包组件的相似之处和不同之处。

缺点、替代方案和未知问题

请参阅未来工作子部分(在“设计”部分),其中 介绍了未来将会进行的一些增强功能,并提供备选方法和其他 将范围缩小到此版本的当前版本之前考虑过的 RFC。

此外,以下小节介绍了 建议。

替代方案:在子组件网址中注入版本哈希

我们无需在软件交付堆栈中引入子包, 添加工具,使组件能够声明版本化依赖项 方法是向子组件网址添加软件包哈希限定符例如, children: 列表中封闭封装的依赖项 parent.cm 将显示为 url: "fuchsia-pkg://fuchsia.com/some_package?hash=1234#meta/a_component.cm"

只要工具和基础架构可以保证该版本的 some_package 在软件包存储区中,此引用可与现有解析器配合使用。一种新的 文件(例如此 RFC 中描述的 subpackages 文件),以及 SWD 不需要更改来解释该文件。

此替代方案被拒,因为需要工具和构建基础架构 使这项工作变得更加复杂,尤其是在工具和构建方面 工作流。例如:

  • 这种替代方案假定软件包哈希未嵌入到开发者 CML 中, 或在运行时解析组件网址的代码中,例如 将它们添加到 collection 中。需要添加某种机制 这些组件清单和静态引用的 source,在网址中注入 ?hash=<blobid> 限定符时, 开发者(通过某种方式)表明希望组件网址是 在构建时固定到特定哈希。
  • 组件清单中的封闭依赖项支持运行时解析, 需要一种单独的机制来支持 依赖项解析修改后的清单和代码可能不切实际, 因此无论使用哪种机制来确定进行这些修改的位置 还需要生成一些其他元数据,用于描述 依赖关系图然后更改工具 例如,归档某个软件包及其所有依赖项; 将软件包及其所有经过哈希固定的依赖项发布到软件包中 商店。

替代方案:使用针对特定上下文的解析器

在解析resolution_context 组件或软件包时,可以修改 Resolve 方法以包含 作为新的输入参数的 context_resolver 请求句柄 (server_end)。

从概念上讲,PackageResolver 和 Resolver 都可以 模拟此 FIDL 协议模式:

protocol Resolver {
  Resolve(struct {
      component_url string;
      context_resolver server_end:Resolver;
  }) -> (resource struct {
      component Component;
  }) error ResolverError;

  ...
}

在此替代方案中,必须在以下所有方法中使用 context_resolver 的客户端: 后续调用,以代表返回的 PackageComponent(例如,在解析 CML 声明的 children)。

这种方法遭拒的主要原因是:它增加了 保持与解析器的实时连接如果情况出现了问题, 需要重启任何或所有信道 这会增加不必要的复杂性。

上下文参数应对映射所需的信息 将指定子软件包名称添加到父级软件包的已固定软件包哈希 meta/subpackages 文件,允许 PackageResolverResolver 必要时,确保实现在解析器重启后继续有效。

替代方案:用于解析上下文值的 FIDL 类型表示法

我们考虑了几种方式来表达解决方案背景, 最终决定对 PackageResolverContextResolver。这种方法的优势之一是,字节数组是 一种通用类型,不会排除对任意内容进行编码 (包括 null 字节)。

FIDL 团队推荐使用字节数组,因为 此模式的显式 FIDL 类型(可以视为 定义)。

最终,我们总结了此类型的限制如下:

  • 类型应该可以编组,而不会引入 API 依赖项 fuchsia.pkg 和 fuchsia.component.solution 之间。
  • 上下文值无需在组件管理器重新启动后继续存在,但 子软件包实现不应要求转换现有的 “不重要”也就是可重启的) “关键”。(此限制条件似乎排除了使用服务句柄。) 请注意,如果组件管理器重新启动,所有组件(当前) 重新解析和重新开始,并出现新的上下文值。上下文值不 组件管理器重启后也无需再次访问,更不用说重新启动或 重启。
  • 从概念上来说,上下文可以很小(与软件包服务器名称差不多大) 和软件包哈希)。这意味着每个子软件包都有 VMO 的句柄 价格可能会非常高

替代方案:组件管理器使用 resolution_context 获取解析器

在解析相对的子打包组件网址时,我们会考虑将 URI scheme(例如 fuchsia-pkg),作为 Component::resolution_context,以允许组件管理器获取 通过 scheme 查找通过解析器注册表执行 Resolver。该请求被拒,因为 从理论上讲,这样做可让 Resolver 返回具有 用于解析相对子打包组件的不同方案, 架构风险相反,组件管理器会跟踪 用于解析组件的 Resolver。组件管理器将始终使用 通过相对网址请求另一个组件的组件的 Resolver

替代方案:使用相对组件,而不是使用相对软件包

在 Fuchsia 中,软件包是软件分发的单元,而软件包是 安装软件(可执行代码和其他文件)。虽然紫红色 组件提供了一些较为熟悉的用例, 软件包之间可以存在不涉及组件或 组件到组件依赖项(例如,Fucsia shell 软件包可能 依赖于另一个没有组件的资源包)。 此外,独立分发预构建版本并不方便 组件。

替代方案:使用特殊的 fuchsia-subpkg:// 架构引用子软件包

此 RFC 建议解释 URI 相对路径(以路径开头的 URI) 并省略架构和授权机构前缀)作为对 资源包中。当前提案还限制了子包引用 直接“子节点”软件包,因此路径不得包含斜杠。(使用 子包引用中的斜线已预留,以备将来使用。)

此外,还可以考虑要求使用特殊架构前缀(例如 fuchsia-subpkg://),以确保指定 字符串明确用于子包解析。

此外,使用架构前缀 fuchsia-subpkg 似乎意味着依赖于 处理 fuchsia-pkg 架构的相同解析器,可能会让人感到困惑。

URI 标准建议仅以相对路径开头。 如果需要特殊架构前缀,则可能意味着依赖于特定的 架构处理程序,限制一般性。无架构相对路径广泛应用 已实施且易于理解(例如,在 HTML 中, 将 <a href="sub-path/page"> 隐式处理为相对位置 参考,而无需特殊方案)。

先验技术和参考资料

标准

接受的 Fuchsia RFC

可能相关的 Fuchsia RFC 草稿