软件包可以“包含”其他软件包(称为 子软件包 s),从而生成嵌套软件包的层次结构。 组件可以利用子打包来整理由 组件,其中每个组件 封装在其自己的软件包中,并附带自己的一组依赖项。
子软件包支持:
- 封装的依赖项(封装组件声明其直接 依赖项)
- 隔离的
/pkg
目录(已分组的组件不需要合并其 文件、库和元数据移到一个共享命名空间) - 可靠的依赖项解析(系统和构建工具可确保子软件包 始终“旅行”他们的软件包)
与 Fuchsia 组件的关系
Fuchsia 利用软件包来“分发”(例如,加载 安装到设备上)。不包含依赖项的单个组件 通常包含在单个软件包中。一个组件,用于启动 组件可以定义包含特定版本(确定于 构建时间)。
以这种方式组织时,子软件包层次结构会镜像组件 父子关系。儿童 然后,系统会从父文件已声明的子软件包中加载 组件包。它封装了 ABI 依赖项 软件包边界
组件还可以使用子软件包来声明对
非可执行组件(不含 program
声明的组件),并获取访问权限
并使用目录功能向其中的 /pkg
数据添加数据。通过公开软件包
数据作为标准目录功能,组件使用
功能路由,用于限制对特定软件包子目录的访问权限,
因此遵守最小特权原则。
软件包依赖项镜像组件依赖项
Fuchsia 系统由组件层次结构定义。首先,
第一个组件(层次结构的根部),组件用于向
通过启动提供这些功能的 children
(子组件)来启动系统。
每个组件都有机会启动自己的组件子树。
为了实例化 child
组件,父项需要标识子项的来源
(实施软件)按其在软件包系统中的位置使用
“组件网址”(软件包网址与软件包内资源位置相结合)
组件清单的名称);例如
fuchsia-pkg://fuchsia.com/package#meta/component.cm
。
重要的是,在组件框架下,组件只能引用 一个运行时依赖项。组件 网址不用于定义对对等组件或任何其他 位于其本地子树外
当子组件由绝对组件网址(如
fuchsia-pkg://fuchsia.com/package#meta/component.cm
,组件开发者
转移对该依赖项实现的控制,待确定
(有可能)在产品组装时或运行时使用临时来源
(软件包服务器)。
借助子打包功能,开发者可以使用 构建时解决方案,即“烘焙”预期的组件实现 包括已知的 ABI 和行为,而不影响封装和 软件包边界的隔离优势。这样可以确保 组件依赖项的实现是封闭的,其行为 如果不重新构建父组件的 软件包。
子打包的组件网址还可以避免绝对组件固有的问题
网址:如果父组件是从(例如)备用仓库加载的
如 fuchsia-pkg://alt-repo.com/package#meta/parent.cm
,则其
该 alt-repo
中也可能包含子项,因此无法静态地
定义可从 fuchsia.com
或
alt-repo.com
(或其他)在运行时未知。
通过使用相对软件包路径,子打包的子组件的实现
由包含子包名称(子包)的相对组件网址标识。
网址,其中包含指定组件清单路径的 URI 片段,例如
some-child#meta/default.cm
。子软件包名称“some-child
”的映射为
并在构建时进行解析,方法是将
子软件包的软件包哈希(位于父组件的软件包元数据中),映射到
子软件包名称
依赖项具有传递性且已封装
组件软件实现不会 use
其他组件。组件
use
功能。组件的功能可能来自其父级
(由父级直接或间接转送,而对方不知情
组件)或从子文件导入。重要的是,由子节点公开的 capability 可以
也可以是直接或间接子级的实现已封装,
因此它公开的功能可以由该子级实现,也可以作为路由
。
子打包允许组件完全封装其实现, 包括子组件的任何依赖项。
当组件使用绝对组件网址声明子组件时, 会在运行时选择该子级的实现方式。对于 但需要权衡的是父组件 封闭:在新环境中很难重复使用父组件。 分发和移植非封闭代码需要跟踪 同时确保依赖项始终 新环境中的可用情况
children: [
{
name: "intl_property_provider",
url: "fuchsia-pkg://fuchsia.com/intl_property_manager#meta/intl_property_manager.cm",
},
...
]
当不需要运行时解析时,父组件可以更新其 子项使用相对路径网址,并声明子组件的软件包 作为子软件包依赖项,在构建时解析。这样,当某个组件 子软件包与子组件相关联,那么子级的软件包会将其所有 而不需要将这些依赖关系暴露给其他云环境, 可能使用它的组件和运行时环境。
children: [
{
name: "intl_property_provider",
url: "intl_property_manager#meta/intl_property_manager.cm",
},
...
]
未通过 /pkg
目录进行环境授权
为了满足 Fuchsia 组件的基本运行时要求,
组件可以通过
/pkg
目录功能。
如上所述,子打包允许软件包声明其组件 封装为分层的组件包此模型 不要求每个组件使用单独的软件包,但确实鼓励这样做,并且 Fuchsia 运行时和工具旨在完成声明、 以自然、高性能的方式构建和运行单独打包的组件。
相反,单个软件包中组合的多个组件共享一个
合并了 /pkg
目录。将多个组件捆绑到一个软件包中
每个组件不仅可以访问相同的数据,还可以访问
该软件包中的其他组件,
路由。
在某些情况下,当多个组件共享相同数据的访问权限时, 可能很方便不过,如果组件需要访问 一组数据,或者某个组件使用了不应向 其他,将组件组合在一起可能会破坏最低限度原则 权限,因而更适合子软件包。
<ph type="x-smartling-placeholder">组件可能无法充分利用 更重要的是特权而非救济,因为这种特权并不一定总能得到 而这一特权也为一个组件带来了意想不到的机会 来利用另一个组件的数据
<ph type="x-smartling-placeholder">相对于在单个软件包中使用多个组件的优势
目前,Fuchsia 允许单个软件包包含多个组件。这个
功能早在子包存在之前就已出现,它提供了一种
通过相对网址声明子组件;通过 URI 片段
通过组件清单的资源路径标识组件。答
#meta/some-child.cm
形式的组件网址会通知 Fuchsia 组件
以便从同一个some-child
包含父组件清单的 包中。
用于共享软件包资源的内置访问权限控制
组件框架有助于强制执行 Fuchsia 的功能访问控制 政策,要求组件明确声明其功能需求, 并通过使父组件负责路由任何外部 功能(包括资源)从已知功能来源(来自 父亲的父亲或子女),或其他子女的一样。
如果某个组件需要另一个组件包中的资源, 组件框架功能路由声明允许源组件 公开特定的子目录,以便目标组件可以访问 仅包含必需的内容,并且由其父组件明确提供。
这支持任何可通过
从常用软件包访问共享 /pkg
目录时,不会公开
整个 /pkg
目录。
与组件框架结合使用的子软件包隔离 /pkg
目录
功能路由提供与 Fuchsia 架构一致的方法来控制访问
资源包资源和共享资源
更改了传递依赖项以避免破坏封装
将组件依赖项合并到单个软件包中时,所有组件 共享单个平面命名空间,并且传递依赖项也必须 包括在内。
例如,如果单个软件包 SP
捆绑了组件 A
和组件 B
,但
B
还依赖于相对 URI 片段 (#meta/C.cm
) 的 C
(软件包 SP
)
必须捆绑 A
、B
和 C
。如果稍后修改了 B
,将 C
替换为两个
新组件 D
和 E
,软件包 SP
的定义必须更改为 bundle
A
、B
、D
和 E
,并丢弃 C
,除非(出于参数原因)
D
和 E
(或两者)也依赖于 C
。
尽管有些构建环境允许组件构建目标 传递组件依赖项,这种做法会增加合并风险 将这些组件的内容转移到单个命名空间中。如果组件或任何 依赖项发生更改,新文件可能会覆盖来自其他 位于该软件包中组件子树任何部分的任何部分,从而破坏 以不确定且可能具有灾难性的方式实现。
子软件包通过将其封装在
每个子软件包的定义,因此软件包 SP
可替换为软件包
A
(包含组件 A
)仅依赖于子软件包 B
(包含组件 B
)。软件包 A
不需要其他依赖项,并且
不会改变,即使 B
的依赖项发生变化也是如此。
子打包实现可保证构建时
使用相对 URI 片段组件网址(例如 #meta/some-child.cm
)会
实际上并不能保证父级和子级之间的 ABI 甚至 API 兼容性
组件“在同一个软件包中”因为它们实际上可以从
该软件包的不同版本
(从软件包服务器)临时解析软件包。新版 可以重新发布同一资源包 并在需要和加载子组件时进行解析。通过 子级实现可能与 原始版本的文件包。
这种情况并不罕见或虚构的用例:在组件框架中,组件是
(默认情况下)仅在需要时解析。公开一个
服务 S
不会加载,除非有其他组件
需要服务:S
。根据程序的业务逻辑,S
可能
在父组件被调用的几分钟或几小时(或更长时间)后调用
。
示例
向子软件包声明 build 依赖项
支持 Fuchsia 的构建框架应包含一个用于声明 Fuchsia 软件包及其内容。如果也启用了子包支持功能 软件包声明将直接列出其所依赖的子软件包 限制。
例如,在 fuchsia.git 中,用于声明 Fuchsia 软件包的 GN 模板
支持两个可选列表:subpackages
和(不太常用)
renameable_subpackages
。可以包含其中之一或同时添加两者。renameable_
version 可允许包为子包分配包特定名称,
在通过包网址或组件网址引用子包时使用:
fuchsia_test_package("subpackage-examples") {
test_components = [ ":subpackage-examples-component" ]
subpackages = [
"//examples/components/routing/rust/echo_client",
":echo_client_with_subpackaged_server",
"//src/lib/fuchsia-component-test/realm_builder_server:pkg",
]
renameable_subpackages = [
{
name = "my-echo-server"
package = "//examples/components/routing/rust/echo_server"
},
]
}
subpackages
列表包含一系列 GN fuchsia_package
build 目标。修改者
默认情况下,子软件包名称(包含的软件包将用于引用的名称)
传递到软件包)来自子软件包的已定义的 package_name
fuchsia_package
目标。
您也可以使用 package
变量来声明子软件包目标,
renameable_subpackages
列表。renameable_targets
还包含一个可选的
name
变量,用于替换子软件包的默认名称。
声明子打包的子项
子文件包仅对其父文件包可见,且子文件包中的组件
软件包。因此,子软件包名称必须在
父级软件包。如果两个子文件包目标具有相同的名称(或
父级可以自由分配自己的子软件包名称(通过
例如 GN 中的 renameable_subpackages
)。
在 CML 中声明子打包的子组件时,url
应为
相对的子打包组件网址,如以下示例所示:
children: [
{
name: "echo_server",
url: "echo_server#meta/default.cm",
},
],
子打包的子组件也可以在运行时声明中引用, 例如在通过 Realm Builder API 声明子项时。例如:
// Add the server component to the realm, fetched from a subpackage
let echo_server = builder
.add_child("echo_server", "my-echo-server#meta/default.cm", ChildOptions::new())
.await
.unwrap();
// Add the client component to the realm, fetched from a subpackage, using a
// name that is still scoped to the parent package, but the name matches the
// package's top-level name. In `BUILD.gn` the subpackage name defaults to
// the referenced package name, unless an explicit subpackage name is
// declared.
let echo_client = builder
.add_child("echo_client", "echo_client#meta/default.cm", ChildOptions::new().eager())
.await
.unwrap();