若要选择哪些功能进入其 namespace,组件会在其 manifest 中为每项功能指定 use 声明。不过,由于功能路由涉及一系列组件,因此即使组件 use 具有某项功能,也不意味着该功能始终可用。它可能会因各种原因而变得不可用:例如,如果链中的某个组件未路由该功能,或者其中一个组件未能解析。
当组件 use 或路由功能时,它可能对功能是否必须可用有各种期望。在某些情况下,capability 对组件的运行至关重要:如果有人创建的拓扑无法将 capability 路由到组件,最好能尽早检测到该故障,以防止出现 bug。不过,在其他情况下,组件可能能够容忍缺少相应功能,或者在某些配置中,相应功能可能本来就应该缺失。为了适应这些不同的场景,cml 在 use、offer 和 expose 上提供了 availability 选项,组件可以使用该选项来声明其对功能可用性的预期。
选项
该框架支持以下 availability 选项:
必需
最常用的 availability 选项是 required,表示组件始终需要相应功能,如果缺少该功能,则无法正常运行。如果未指定任何使用情况,系统会自动设置此默认选项。
可选
optional 表示某些配置可能不支持相应功能。如果组件 use 是一项可选功能,但最终发现该功能不可用,则组件应能够在没有该功能的情况下正常运行,例如通过停用需要该功能的任何功能。
不过,如果某些配置中没有 optional 功能,框架会预期拓扑仍然包含以 void 结尾的完整功能路由。这样一来,产品所有者就需要明确选择相应功能是否可用(其路由在某个提供程序组件处终止)或不可用(其路由在 void 处终止)。如果路由不完整,工具和诊断程序会将此情况报告为错误。
过渡性
transitional 类似于 optional 的弱化版本。与 optional 类似,如果某个组件使用 transitional 功能,则应容忍该功能缺失的情况。不过,与 optional 不同,transitional 路由不需要以 void 结束,如果 transitional 路由不完整或无效,工具和诊断程序不会报告任何错误。
“过渡”一词表示此选项适用于软过渡。例如,假设您有一个 Echo 协议,想要将其替换为 EchoV2。在此过渡的早期阶段,您可以将客户端更改为 use 新协议,并提供 transitional 可用性。之后,一旦建立端到端路线,您就可以将可用性升级为 required 或 optional。如果您尝试使用 optional,工具和诊断程序会抱怨客户端组件的父级缺少 Echo2 的功能路由,而 transitional 会抑制此类警告。
与目标相同
same_as_target 充当直通:它会导致可用性从路由的目标组件设置的任何可用性继承。这对于传递大量功能的中间组件非常有用,这样当目标中路由的 availability 发生更改时,来源中无需进行任何更改。
由于 use 功能的组件是其最终目的地,因此 same_as_target 不是 use 的有效选项。
可用性比较
各种可用性选项之间存在相对的“强度”顺序。从最强到最弱,顺序为:required > optional > transient。
same_as_target 不属于此范围,因为它是一个直通功能:same_as_target 功能路由的强度实际上等于它从目标继承的任何可用性。
将可用性从来源降级到目标是有效且在许多情况下都很有用的。例如,如果组件 X 向 Y 公开 required 功能,Y 可以选择将该功能 use 为 optional,也可以将该功能 offer 为 optional 给另一个子级。不过,从来源升级可用性是一种路由错误。如果发生这种情况,任何使用或路由该功能的操作都会失败,就像路由链不完整时一样。这是一个错误,因为这意味着您尝试对可用性建立比来源所保证的更强的保证。例如,如果某个组件的父组件将某项功能 offer 为 optional,那么该组件将该功能 use 为 required 就没有意义了,因为这意味着父组件已声明该功能可能不可用。
工具和诊断
设置可用性的主要作用是控制主机工具和诊断报告如何路由错误。简而言之,这意味着 availability 越弱,他们就越不严格。详细规范如下:
- 如果
required功能路由不完整、无效或以void结尾:- 审查会将此报告为路由错误。如果 build 配置要求此值通过,则会导致 build 错误。
component_manager将在描述路由错误的组件的范围内记录WARNING消息。
- 如果
optional功能路由不完整或无效:- 审查会将此报告为路由错误。如果 build 配置要求此值通过,则会导致 build 错误。
component_manager将在使用组件的范围内记录一条INFO消息,用于描述路由错误。
- 如果
optional功能路线以void结尾:- Scrutiny 不会报告错误。
component_manager可能会记录INFO消息,但不会记录错误。
- 如果
transitional功能路由不完整、无效或以void结尾:- 系统不会记录任何错误或其他信息。
对于一般运行时行为,availability 没有影响。例如,假设某个组件尝试 use 其命名空间中路由已损坏的协议。如果协议的路由方式为 required 或 optional,最终结果将相同:
- 相应协议将显示在组件的命名空间中。
- 连接到功能的初始调用将成功(假设使用了标准单向 API,例如 Rust 中的 connect_to_protocol)。
component_manager将尝试路由功能。最终,路由将失败,并且通道将关闭并显示NOT_FOUND墓志铭。
source_availability: unknown
cml 具有一项附加功能,可用于自动生成 required 或 optional void offer,具体取决于是否包含具有来源子声明的 cml shard。如需详细了解此功能,请参阅build 文档。
target_availability:未知
cml 具有一项附加功能,可用于省略 offer,具体取决于是否包含具有目标子声明的 cml 分片。如需详细了解此功能,请参阅build 文档。
示例
以下示例说明了本文档中介绍的概念。
在此示例中,有三个组件:
echo_client,它会尝试使用echo_server提供的各种协议echo_server,提供了一些协议echo_realm,echo_client和echo_server的父级,用于将它们链接在一起
我们来检查一下它们的 CML 文件:
// echo_client.cml
{
...
use: [
{ protocol: "fuchsia.example.Echo" },
{
protocol: "fuchsia.example.EchoV2",
availability: "transitional",
},
{
protocol: "fuchsia.example.Stats",
availability: "optional",
},
],
}
// echo_server.cml
{
...
capabilities: [
{ protocol: "fuchsia.example.Echo" },
],
expose: [
{
protocol: "fuchsia.example.Echo",
from: "self",
},
],
}
// echo_realm.cml
{
offer: [
{
protocol: "fuchsia.example.Echo",
from: "#echo_server",
to: "#echo_client",
availability: "required",
},
{
protocol: "fuchsia.example.Stats",
from: "void",
to: "#echo_client",
availability: "optional",
},
],
children: [
{
name: "echo_server",
url: "echo_server#meta/echo_server.cm",
},
{
name: "echo_client",
url: "echo_client#meta/echo_client.cm",
},
],
}
请注意,如果省略 availability,则默认为 required。在这种拓扑中,行为如下:
echo_client将能够成功连接到fuchsia.example.Echo。echo_client将无法连接到fuchsia.example.EchoV2或fuchsia.example.Stats。- 工具和诊断不会记录错误。
fuchsia.example.Stats没有错误,因为它是optional并路由到void。fuchsia.example.Stats没有错误,因为它是transient。
现在,我们来看看使用其他版本会发生什么情况:
// echo_client.cml
{
...
use: [
{ protocol: "fuchsia.example.Echo" },
{
protocol: "fuchsia.example.EchoV2",
availability: "transitional",
},
{
protocol: "fuchsia.example.Stats",
availability: "optional",
},
],
}
// echo_server.cml
{
...
capabilities: [
{
protocol: [
"fuchsia.example.Echo",
"fuchsia.example.EchoV2",
"fuchsia.example.Stats",
],
},
],
expose: [
{
protocol: [
"fuchsia.example.Echo",
"fuchsia.example.EchoV2",
],
from: "self",
},
{
protocol: "fuchsia.example.Stats",
from: "self",
availability: "optional",
},
],
}
// echo_realm.cml
{
offer: [
{
protocol: [
"fuchsia.example.Echo",
"fuchsia.example.EchoV2",
],
from: "#echo_server",
to: "#echo_client",
availability: "same_as_target",
},
{
protocol: "fuchsia.example.Stats",
from: "#echo_server",
to: "#echo_client",
availability: "optional",
},
],
children: [
{
name: "echo_server",
url: "echo_server#meta/echo_server.cm",
},
{
name: "echo_client",
url: "echo_client#meta/echo_client.cm",
},
],
}
现在:
echo_client将能够成功连接到fuchsia.example.Echo、fuchsia.example.EchoV2和fuchsia.example.Stats。- 每个路由虽然具有不同的
availability,但都是完整的,并且最终会到达一个真实组件。 - 对于每条路线,源和目标之间的可用性通过了比较检查
- 每个路由虽然具有不同的
- 工具和诊断信息不会记录错误,因为所有路线都已完成。