- Migrating system components
- Prerequisites {#prerequisites}
- Migrate the component manifest {#create-component-manifest}
- Migrate the tests {#migrate-tests}
- Add the new component {#add-component-to-topology}
- Diagnostics capabilities {#diagnostics}
- Other common capabilities {#other-capabilities}
- Converting CMX features {:#cmx-features}
Migrating system components
This document provides instructions for migrating a system component from Components v1 to Components v2. A system component is a component that exists to provide services to other components in the system. Typically, in Components v1 the mapping of service to component is registered in a sysmgr configuration file.
To migrate your system component from v1 to v2, do the following:
Depending on the features your component supports, you may need to explore the following sections for additional guidance:
For more details on the components migration effort, see State of the Components v2 Migration.
Prerequisites {#prerequisites}
Before you begin, ensure that your component uses the latest build templates.
If your component still uses the legacy package()
in your BUILD.gn
,
migrate your package templates before continuing.
You should also familiarize yourself with the following topics:
- Introduction to the Fuchsia Component Framework: Components v2 comprises a set of concepts and APIs that are distinct from Components v1 or traditional OS program models.
- Introduction to the Test Runner Framework: Test Runner Framework is built on the Component Framework. You need to be familiar with these concepts before you migrate tests.
Migrate the component manifest {#create-component-manifest}
Create a minimal CML file and configure it with GN so that it gets compiled and installed in your package.
Note: Unlike CMX, CML is JSON5, which allows comments and trailing commas. Take advantage of this when writing your CML file!
Determine where your CMX file is located in the source tree (for example,
fonts.cmx
). Create a file in the same directory that has the same filename but with a.cml
extension, with the following contents:// fonts.cml
{
include: [
// Enable system logging
"sdk/lib/diagnostics/syslog/client.shard.cml",
],
}
Note: Your CML file will live side-by-side with the CMX file for now. Do not delete the CMX file yet.
Find the build rule that defines your component. Normally, this is a
fuchsia_component
rule. For example, see the fontsBUILD.gn
.fuchsia_component("fonts") {
manifest = "meta/fonts.cmx"
deps = [ ":font_provider" ]
}
Update the
manifest
element of the associatedfuchsia_component
rule to point to your new.cml
file instead:fuchsia_component("fonts") {
manifest = "meta/fonts.cml"
deps = [ ":font_provider" ]
}
Build the target for your package:
fx build
You are ready to start writing your v2 component manifest.
Adding the executable {#component-executable}
Add the program
section of your CML file along with the
appropriate runner declaration.
Note: The runner declaration is necessary even if your component is launched using the ELF runner. This is the default in CMX but must be explicitly specified in CML.
// fonts.cmx
{
"include": [
"sdk/lib/diagnostics/syslog/client.shard.cmx"
],
{{ '<strong>' }}"program": {
"binary": "bin/font_provider"
},{{ '</strong>' }}
...
}
// fonts.cml
{
include: [
// Enable system logging
"sdk/lib/diagnostics/syslog/client.shard.cml",
],
{{ '<strong>' }}program: {
runner: "elf",
binary: "bin/font_provider",
},{{ '</strong>' }}
}
Declaring required services {#required-services}
Add use
declarations to your CML file. These are the
approximate equivalent of the services
list in CMX.
// fonts.cmx
{
"include": [
"sdk/lib/diagnostics/syslog/client.shard.cmx"
],
"program": {
"binary": "bin/font_provider"
},
{{ '<strong>' }}"sandbox": {
"services": [
"fuchsia.logger.LogSink",
"fuchsia.pkg.FontResolver"
]
...
}{{ '</strong>' }}
}
Convert each element of the services
list to a use
declaration for the
corresponding service protocol
.
// fonts.cml
{
include: [
// Enable system logging
"sdk/lib/diagnostics/syslog/client.shard.cml",
],
program: {
runner: "elf",
binary: "bin/font_provider",
},
{{ '<strong>' }}use: [
{
protocol: [ "fuchsia.pkg.FontResolver" ],
},
],{{ '</strong>' }}
}
Exposing available services {#available-services}
In Components v1, you typically declare information
about services exposed by a component in a sysmgr configuration file.
These files are referenced by config_data
targets in the build, and specify
mappings of services to components in the sys
environment.
Note: The most common location of this service mapping is
services.config
, which defines service mappings
that apply to every product configuration.
Identify all service mappings, if any, for your component. You can use CodeSearch to find service mappings. Here is a sample search.
// services.config
{
"services": {
...
"fuchsia.fonts.Provider": "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx",
...
}
}
For each service mapping, add an
expose
declaration and a correspondingcapabilities
entry with the serviceprotocol
.// fonts.cml
{
include: [
// Enable system logging
"sdk/lib/diagnostics/syslog/client.shard.cml",
],
program: {
runner: "elf",
binary: "bin/font_provider",
},
{{ '<strong>' }}capabilities: [
{
protocol: [ "fuchsia.fonts.Provider" ],
},
],{{ '</strong>' }}
use: [
{
protocol: [ "fuchsia.pkg.FontResolver" ],
},
],
{{ '<strong>' }}expose: [
{
protocol: "fuchsia.fonts.Provider",
from: "self",
},
],{{ '</strong>' }}
}
Build your updated package:
fx build
Verify that your package includes the compiled v2 component manifest (with a
.cm
extension).ffx scrutiny shell "search.components --url {{ '<var label="component">my_component.cm</var>' }}$"
Migrate the tests {#migrate-tests}
In most cases, tests for v1 components are themselves v1 components. The first
step is to identify all tests that exercise your component’s functionality.
Typically this is a fuchsia_test_package
or fuchsia_unittest_package
rule.
For example, see the fonts BUILD.gn
.
A test may include or depend on components that are separate from the test driver. Here are some things to look for:
- Is your test self-contained in one file (a unit test)? Or does it launch other components (an integration test)?
- Does your test have a CMX with
fuchsia.test facets
, such asinjected-services
orsystem-services
? - Does your test create environments in-process? If so, does it create a separate environment for each test case?
Update the test configuration {#update-test-config}
The migration procedure varies depending on the testing framework features in your v1 component:
- Test has no external services: The test is
generated by a
fuchsia_unittest_package
GN rule, or its CMX does not contain fuchsia.test facets. - Test uses system services: The test has a CMX
that contains
system-services
test facets. - Test has injected services: The test has a CMX
that contains
injected-services
test facets.
Note: For more details on the services and capabilities provided to components by the Test Runner Framework, see the test manager documentation.
Test with no injected services {#no-injected-services}
For tests that use no injected services, your test root can be the same component as the test driver. The v2 test’s component manifest should be distributed in the same package that contains the test binary. Follow the same instructions from Migrate the component manifest that you used to package your component.
Consider the following example test component:
// fonts_test.cmx
{
"include": [
"sdk/lib/diagnostics/syslog/client.shard.cmx"
],
"program": {
"binary": "bin/font_test"
}
}
To migrate this test to the Test Runner Framework, do the following:
Create a CML file that points to the test binary that includes the appropriate test runner:
Note: See test runners that are provided by the framework.
// fonts_test.cml
{
include: [
// Select the appropriate test runner shard here:
// rust, gtest, go, etc.
"src/sys/test_runners/rust/default.shard.cml",
],
program: {
binary: "bin/font_test",
}
}
Update the
fuchsia_component
rule for your test component to reference the new CML file:fuchsia_component("fonts_test_driver") {
testonly = true
manifest = "meta/fonts_test.cml"
deps = [ ":font_test" ]
}
fuchsia_test_package("font_provider_tests") {
test_components = [ ":fonts_test_driver" ]
}
Test with system services {#system-services}
For tests that use system-services
test facets, consider
if they can be converted to injected services instead.
Injecting services is the preferred method because it promotes hermetic test
behavior.
For certain non-hermetic tests, the Test Runner Framework provides the test realm with the following services:
Service | Description |
---|---|
fuchsia.hardware.display.Provider |
Provider for display controllers |
fuchsia.scheduler.ProfileProvider |
Profile provider for scheduler |
fuchsia.sysmem.Allocator |
Allocates system memory buffers |
fuchsia.tracing.provider.Registry |
Register to trace provider |
fuchsia.vulkan.loader.Loader |
Vulkan library provider |
Consider the following example test component that uses a single system service,
fuchsia.sysmem.Allocator
:
// my_test.cmx
{
"facets": {
"fuchsia.test": {
"system-services": [
"fuchsia.sysmem.Allocator"
]
}
},
"program": {
"binary": "bin/my_test"
},
"sandbox": {
"services": [
"fuchsia.sysmem.Allocator"
]
}
}
To migrate this test to the Test Runner Framework, declare each available system service with the other required services in your test component manifest.
// my_test.cml
{
include: [
// Select the appropriate test runner shard here:
// rust, gtest, go, etc.
"src/sys/test_runners/rust/default.shard.cml",
],
program: {
binary: "bin/my_test",
},
use: [
{
protocol: [ "fuchsia.sysmem.Allocator" ],
},
],
}
Test with injected services {#injected-services}
For tests that use other fuchsia.test facets, such as
injected-services
, your test root and test driver
must be split into different components to enable proper capability routing.
In this example, suppose there’s a single injected service,
fuchsia.pkg.FontResolver
:
// font_provider_test.cmx
{
"facets": {
"fuchsia.test": {
"injected-services": {
"fuchsia.pkg.FontResolver":
"fuchsia-pkg://fuchsia.com/font_provider_tests#meta/mock_font_resolver.cmx"
}
}
},
"program": {
"binary": "bin/font_provider_test"
},
"sandbox": {
"services": [
"fuchsia.pkg.FontResolver"
]
}
}
To migrate this test to the Test Runner Framework, do the following:
Create a CML file for the test driver that points to the test binary, and includes the appropriate test runner:
Note: See test runners that are provided by the framework.
// test_driver.cml (test driver)
{
include: [
// Select the appropriate test runner shard here:
// rust, gtest, go, etc.
"src/sys/test_runners/rust/default.shard.cml",
],
program: {
binary: "bin/font_provider_test",
}
}
You need CML files for each component that provides a capability needed in the test. If there is an existing CML file for the component providing the injected service, you may be able to reuse it. Otherwise, create a new CML file.
// mock_font_resolver.cml (capability provider).
{
program: {
runner: "elf",
binary: "bin/mock_font_resolver",
},
capabilities: [
{
protocol: [ "fuchsia.pkg.FontResolver" ],
},
],
expose: [
{
protocol: "fuchsia.pkg.FontResolver",
from: "self",
},
],
}
Note: The CML files for the capability providers can be distributed in the same package that contained the v1 test. Follow the same instructions in Migrate the component manifest that you used to package your component.
Create a new CML file for the test root that includes the test driver and capability provider(s) as children and offers the capabilities from the provider(s) to the driver. This component should also expose the test suite protocol.
// font_provider_test.cml (test root)
{
children: [
{
name: "test_driver",
url: "fuchsia-pkg://fuchsia.com/font_integration_test#meta/test_driver.cm",
},
{
name: "font_resolver",
url: "fuchsia-pkg://fuchsia.com/font_integration_test#meta/mock_font_resolver.cm",
},
],
expose: [
{
protocol: "fuchsia.test.Suite",
from: "#test_driver",
},
],
offer: [
{
protocol: "fuchsia.pkg.FontResolver",
from: "#font_resolver",
to: [ "#test_driver" ],
},
],
}
Add
fuchsia_component
rules for each CML file, and update thefuchsia_package
to reference the child components as dependencies:fuchsia_component("test_driver") {
testonly = true
manifest = "meta/test_driver.cml"
deps = [ ":font_provider_test_bin" ]
}
fuchsia_component("mock_font_resolver") {
testonly = true
manifest = "meta/mock_font_resolver.cml"
deps = [ ":mock_font_resolver_bin" ]
}
fuchsia_component("font_provider_test") {
testonly = true
manifest = "meta/font_provider_test.cml"
}
fuchsia_test_package("font_provider_tests") {
test_components = [ ":font_provider_test" ]
deps = [
":fonts_test_driver",
":mock_font_resolver",
]
}
Verify the migrated tests {#verify-tests}
Build and run your test and verify that it passes. Like any other test, use
fx test
to invoke the test:
fx build && fx test font_provider_tests
Your component is now tested in Components v2.
If your test doesn’t run correctly or doesn’t start at all, try following the advice in Troubleshooting components.
Add the new component {#add-component-to-topology}
Note: This section assumes that your component is not in apps
or
startup_services
. If it is, reach out to component-framework-dev
for guidance.
Now you’re ready to add your new component to the v2 component topology. This defines the relationship between your component and the rest of the system.
Take another look at any sysmgr configuration file(s) that defines service mappings to your component, which you identified while migrating the component manifest. The steps below refer to the collection of all these services as your component’s “exposed services”.
// services.config
{
"services": {
...
"fuchsia.fonts.Provider": "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx",
...
}
}
Add the component to core {#add-component-to-core}
Add your component as a child instance of the core.cml
component, and offer its exposed services to appmgr. You need to choose
a name for your component instance and identify its component URL (you should
be able to get this from the config mapping).
// core.cml
{
children: [
...
{
name: "font_provider",
url: "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cm",
},
],
offer: [
...
{
protocol: "fuchsia.fonts.Provider",
from: "#font_provider",
to: [ "#appmgr" ],
},
],
}
Learn your component moniker {#component-moniker}
If you added your component to core.cml
as explained here, then it’s easy to
infer your component [moniker][moniker] as /core/component_name
where component_name
is
the name of the child you added to core.cml
.
You can see this hierarchy using ffx component list
as well:
.
bootstrap
archivist
...
core
...
appmgr
app
sysmgr.cmx
sys
build-info.cmx
cobalt.cmx
...
battery_manager
font_provider
...
startup
Expose services to sys environment {#expose-services}
Declare each of these services in appmgr.cml
to make them
available to v1 components under the sys
environment.
Change appmgr.cml
as follows:
// appmgr.cml
{
use: [
...
{
protocol: "fuchsia.fonts.Provider",
path: "/svc_for_sys/fuchsia.fonts.Provider",
},
],
}
Offer services to your component {#offer-services}
To work properly, your component must be offered all services that appear in
its use
declarations. These services may be provided by
v1 or v2 components. Look in the sysmgr config files and core.cml
to find the
originating components (example search).
There are three possible cases:
- v1 component provides service: The provider of the service is a v1 component.
- v2 component in
core.cml
provides service: The provider of the service is a v2 component that’s a child ofcore.cml
. - The provider of the service is a v2 component that’s not child of
core.cml
. If this is the case, reach out to component-framework-dev for assistance.
Note: You must also route all services requested by any manifest shards listed
in your manifest’s include
.
v1 component provides service {#v1-component-provides-service}
You’ll reach this case if a mapping for the service exists in a sysmgr config
file. Take a look at appmgr.cml
, and search for the service.
If it’s already exposed, no modifications are required. If not, you’ll need to
change appmgr.cml
to expose the service and route it from appmgr
to your
component:
// appmgr.cml
{
expose: [
...
{
protocol: [
... // (Any services already exposed from appmgr are here)
"fuchsia.pkg.FontResolver",
],
from: "self",
},
...
],
}
// core.cml
{
offer: [
...
{
protocol: "fuchsia.logger.LogSink",
from: "parent",
to: [ "#font_provider" ],
},
{
protocol: [
"fuchsia.pkg.FontResolver",
],
from: "#appmgr",
to: [ "#font_provider" ],
},
...
],
}
v2 component in core.cml provides service {#v2-core-cml-provides-service}
Route the service from the component in core
that exposes it to your component
in core.cml
:
// core.cml
{
offer: [
...
{
protocol: [ "fuchsia.pkg.FontResolver" ],
from: "#font_resolver",
to: [ "#font_provider" ],
},
...
],
}
Resolve dependency cycles {#dependency-cycles}
In Components v1, appmgr
represents a collection of multiple components with
many capabilities. This increases the chance that a v2 component routes multiple
capabilities into and out of appmgr
for a given component. Components that
both offer services to appmgr
and consume services offered by appmgr
create
a dependency cycle that you may need to resolve during the migration.
Strong dependency cycles were found. Break the cycle by removing a dependency or
marking an offer as weak. Cycles: { { ... }, { ... } }
To avoid build-time errors resulting from dependency cycles, apply the
weak_for_migration
tag to one of the capability routes. For example:
// core.cml
{
offer: [
{
protocol: [ "fuchsia.pkg.FontResolver" ],
from: "#appmgr",
to: [ "#font_provider" ],
{{ '<strong>' }}dependency: "weak_for_migration",{{ '</strong>' }}
},
{
protocol: "fuchsia.fonts.Provider",
from: "#font_provider",
to: [ "#appmgr" ],
},
]
}
You can apply weak_for_migration
to either capability in a dependency cycle.
Determine which side is most appropriate for your component.
In most cases, the convention is to apply weak_for_migration
on the capability
offered from appmgr
until everything is migrated out of Components v1.
Remove sysmgr configuration entries {#remove-config-entries}
Before you test your component, remove the service mappings in
services.config
and other sysmgr configuration
files you identified previously.
Without this step, sysmgr will report errors attempting to load services from
your v1 component instead of using the new capabilities routed to it through
core.cml
.
// services.config
{
"services": {
...
// Delete these lines
"fuchsia.fonts.Provider": "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx",
...
}
}
Test your component {#test-component}
Manually verify that your component and its dependencies still work.
Perform manual verification of capability routing as it is usually outside the
scope of hermetic tests.
The verify routes
command built into scrutiny reports routing
errors in the static component topology of the current build. This can help you
find missing offer
or expose
declarations before performing runtime tests.
ffx scrutiny verify routes
Note: Scrutiny can only verify routes in the v2 component topology. It cannot
look into appmgr
and the sys
environment to review usage from v1 components.
If your component manifest contains additional system features that haven’t been migrated at this point, see Other common capabilities and Converting CMX features for additional guidance.
If your component or one of the components that depends on it isn’t working correctly, try following the advice in Troubleshooting components.
Once your component has been registered in the v2 topology and all tests have been converted, you can delete the Components v1 definition of your component. Find and remove any CMX files for your component and its tests, including any remaining references to it from the package rule(s) you modified when you migrated the component manifest.
Diagnostics capabilities {#diagnostics}
This section provides guidance on migrating the capabilities for diagnostics features to Components v2.
Inspect {#inspect}
{% dynamic if user.is_googler %}
Note: If your component shares Inspect data in product feedback reports, you may also need to update the approved selectors to reference the new component moniker. For more details on updating feedback selectors, see go/tq-feedback-privacy.
{% dynamic endif %}
If your component is using Inspect, you’ll need to expose additional
information to the framework.
You can quickly determine if your component uses Inspect by looking for one of
the following library dependencies in the component’s BUILD.gn
:
//sdk/lib/sys/inspect/cpp
//src/lib/diagnostics/inspect/rust
dart_package_label.fuchsia_inspect
In Components v1, appmgr
provides access to the component’s /diagnostics
directory, which contains Inspect data.
Components v2 requires a component to explicitly expose /diagnostics
to the
framework.
This allows the Archivist to read Inspect data for snapshots,
iquery, etc.
Note: For more details on the differences in data collection between Components v1 and Components v2, see the Archivist documentation.
When migrating the component manifest, you can add Inspect capabilities to your v2 component by including the following manifest shard:
// my_component.cml
{
// Expose the diagnostics directory capability for Inspect
include: [ "sdk/lib/diagnostics/inspect/client.shard.cml" ],
...
}
Component moniker for selectors
As [explained previously][component-moniker], it’s possible to infer the
component moniker using ffx component list
. Alternatively you can
use fx iquery list
to see available components for querying inspect data. Your
component moniker should appear in the iquery
output up after adding the
client.shard.cml
above.
Inspect data in tests {#inspect-tests}
If your test components read Inspect diagnostics data, migrate to the
fuchsia.diagnostics.ArchiveAccessor
service provided by the Archivist.
Consider the following approaches you may be currently using from Components v1 to accomplish this:
Injected services. The test CMX contains
fuchsia.diagnostics.ArchiveAccessor
as aninjected-service
, reading isolated inspect data from an embedded Archivist limited to test components:{
"fuchsia.test": {
"injected-services": {
"fuchsia.diagnostics.ArchiveAccessor":
"fuchsia-pkg://fuchsia.com/archivist-for-embedding#meta/archivist-for-embedding.cmx",
...
}
},
...
It means the test is reading isolated inspect data from an embedded Archivist that only sees test components.
Directly from the Hub.
In v2, there’s an Archivist running inside each test. Instead of instantiating another Archivist in your test, you can use that embedded Archivist Accessor protocol directly in your test. Therefore you’ll need to do the following:
When migrating tests, add the protocol capability to your test root or test driver:
// test_driver.cml (test driver)
{
use: [
{
protocol: [
"fuchsia.diagnostics.ArchiveAccessor",
],
},
]
}
Update your program to use the
ArchiveReader
library, which is available in C++, Rust, and Dart.Note: For components in other languages, use the
ArchiveAccessor
FIDL protocol directly.
Logging {#logging}
If your component requires access to logging, you’ll need to declare the
fuchsia.logger.LogSink
capability in your manifest. In Components v1, you may
have included diagnostics/syslog/client.shard.cmx
or referenced the protocol
directly under services
in your CMX file.
You can add syslog capabilities to your v2 component by including the following manifest shard:
// my_component.cml
{
// Expose the LogSink capability for syslog
include: [ "sdk/lib/diagnostics/syslog/client.shard.cml" ],
...
}
Additionally, Components v1 redirects stderr
and stdout
to debuglog
,
but in Components v2 they have no default destination.
The debuglog
is typically used for low-level debugging information from the
kernel and device drivers. If your component writes log data to these streams,
consider the following:
- Redirect to syslog: Forward print statements to use the
system’s standard
syslog
buffer instead. This buffer is larger and capable of attributing logs per-component. - Redirect to debuglog: If you have integration tests or
other use cases that require preserving the default Components v1 behavior
for log messages, direct the streams back to the
debuglog
buffer.
Redirecting to syslog {#syslog-redirect}
To send stderr
and stdout
to syslog in your v2 component, you’ll
need to configure the ELF runner to forward the streams. This enables forwarding
for all print statements, including those generated by libraries or runtime code.
When migrating your component manifest, include the following manifest shard to enable forwarding:
// my_component.cml
{
// Enable forwarding of stdio to syslog
include: [ "sdk/lib/diagnostics/syslog/elf_stdio.shard.cml" ],
...
}
Note: Logging directly to syslog
from your code provides additional features
to your component, such as severity levels. To take advantage of these features,
consider migrating your code to use the logging libraries highlighted in the
syslog
documentation.
Redirecting to debuglog {#debuglog-redirect}
Your component may have external dependencies that rely on log messages in
debuglog. One common use case is integration tests that directly
parse log messages from the stdout
of an emulator process using the
emulatortest framework. In these cases, you’ll need to manually
direct log messages back to the debuglog
buffer.
When migrating your component manifest, request the
fuchsia.boot.WriteOnlyLog
capability.// my_component.cml
{
use: [
...
{
protocol: [
"fuchsia.boot.WriteOnlyLog",
],
},
],
}
When adding your component, add the following to offer this capability to your component from
core
:// core.cml
{
offer: [
...
{
protocol: [ "fuchsia.boot.WriteOnlyLog" ],
from: "parent",
to: [ "#my_component" ],
},
],
}
Direct
stderr
andstdout
todebuglog
in your program. You can use libraries for the initialization if your component is written in Rust or C++.Note: If the component isn’t written in C++ or Rust you can use the existing libraries as a template for how to perform the initialization.
Hub {#hub}
The hub provides access to detailed structural information about component
instances at runtime.
In Components v1, appmgr
provides the v1 Hub through a specific
directory structure populated in your component’s namespace under /hub
.
In Components v2, many v1 Hub use cases have preferred alternative approaches.
When migrating to Components v2, consider the following alternatives:
- Observing lifecycle events: Clients watching the filesystem to observe component instance changes should use event capabilities instead.
- Reading inspect data: Clients reading Inspect data from
out/diagnostics
should migrate to thefuchsia.diagnostics.ArchiveAccessor
service instead. - Connecting to exposed services: Clients connecting
to services exposed through a component’s
out/svc
directory should route these services and capability providers into their tests instead, similar toinjected-services
.
For other use cases, follow the instructions in this section to migrate to the v2 Hub provided by Component Manager.
Note: Features of the Hub are designed to support test components only. If you need to access the Hub outside of the test realm, reach out to component-framework-dev for assistance.
Route the hub directory
When migrating tests, you’ll need to route the hub
directory capability to your test if the test driver
or any other components in the test realm need to read data from the v2 Hub.
Following the example in Test uses injected services,
add the hub
directory capability to your CML file:
//test_driver.cml
{
use: [
{
directory: "hub",
from: "framework",
rights: [ "r*" ],
path: "/hub",
},
]
}
Update hub reference paths
Update your code to reference the content path from the v2 Hub directory structure. Here are some examples of path differences between the Hub implementations:
v1 Hub Path | v2 Hub Path |
---|---|
/hub/c/{{ '<var>' }}component-name{{ '</var>' }}/{{ '<var>' }}instance-id{{ '</var>' }}/url |
/hub/url |
/hub/c/{{ '<var>' }}component-name{{ '</var>' }}/{{ '<var>' }}instance-id{{ '</var>' }}/in/{{ '<var>' }}svc-path{{ '</var>' }} |
/hub/exec/in/{{ '<var>' }}svc-path{{ '</var>' }} |
/hub/c/{{ '<var>' }}component-name{{ '</var>' }}/{{ '<var>' }}instance-id{{ '</var>' }}/process-id |
/hub/exec/runtime/elf/process-id |
/hub/c/{{ '<var>' }}child-component{{ '</var>' }} |
/hub/children/{{ '<var>' }}child-component{{ '</var>' }} |
Note: The hub
directory routed to your component is scoped to the current
realm. To access hub contents from the parent realm, route the hub from parent
instead of framework
. This feature is not available with the v1 Hub.
Other common capabilities {#other-capabilities}
This section provides guidance on migrating other capabilities that are common to many components.
Resolvers
If your component is not part of the base
package set for your product,
you must route the universe
resolver to it. Resolvers are routed to components
using environments, and core.cml
has a shared environment named
universe-resolver-env
for components outside of base
.
Use the list-packages
command to report the package sets where your component
package is included.
fx list-packages --verbose {{ '<var label="package name">my-package</var>' }}
If the package is not listed with the base
tag, follow the remaining
instructions in this section.
When adding your component, assign the shared
universe-resolver-env
as your component’s environment
.
// core.cml
{
children: [
...
{
name: "my_component",
url: "fuchsia-pkg://fuchsia.com/my-pkg#meta/my_component.cm",
{{ '<strong>' }}environment: "#universe-resolver-env",{{ '</strong>' }}
},
],
}
Shell binaries
Your project may contain a fuchsia_shell_package()
build target designed to
execute in a shell environment. Many of these packages also contain a CMX file
to support invoking the binary as a v1 component.
When exposing your services to the sys
environment,
include any services required by shell binaries.
Note: If your component requires shell-commands
directory access to invoke
shell binaries, see directory features for more details.
Shell binaries are run in the sys
environment,
and have access to all the capabilities provided there.
Capabilities are not defined by the CMX manifest file unless shell binaries are
invoked as a component using the run
command.
When working with shell binaries, consider the following:
- If you only need access to the binary through a shell interface, remove the unused CMX file entirely. Do not replace it with a corresponding CML file.
- If you need to access the binary from somewhere else in the v2 component topology (such as tests), migrate the functionality into a new v2 component instead.
Note: There is no v2 equivalent of using run
to invoke a shell binary
as a component. If you require this feature for your component,
reach out to component-framework-dev.
Lifecycle
If your component is a client of the fuchsia.process.lifecycle.Lifecycle
protocol, then follow the instructions in this section to migrate lifecycle
access.
- Remove your component’s entry in the
appmgr
allowlist:
// Remove this entry.
lifecycle_allowlist.insert(component::Moniker{
.url = "fuchsia-pkg://fuchsia.com/my_package#meta/my_component.cmx", .realm_path = {"app", "sys"}});
- When migrating your component manifest, add the lifecycle stop event:
// my_component.cml
{
include: [
"sdk/lib/diagnostics/syslog/client.shard.cml",
],
program: {
runner: "elf",
binary: "bin/my_binary",
{{ '<strong>' }}lifecycle: { stop_event: "notify" },{{ '</strong>' }}
},
...
}
Converting CMX features {:#cmx-features}
This section provides guidance on migrating additional CMX sandbox
features. If there’s a feature in your CMX file that’s not in this list,
reach out to component-framework-dev.
Storage features {#storage-features}
If your component uses any of the following features, follow the instructions in this section to migrate storage access:
Feature | Description | Storage Capability | Path |
---|---|---|---|
isolated-persistent-storage |
Isolated persistent storage directory | data |
/data |
isolated-cache-storage |
Managed persistent storage directory | cache |
/cache |
isolated-temp |
Managed in-memory storage directory | temp |
/tmp |
These features are supported in v2 components using storage capabilities.
Declare the required storage capabilities
When migrating your component manifest, add the following to your CML file:
// my_component.cml
{
use: [
...
{
storage: "{{ '<var label="storage">data</var>' }}",
path: "{{ '<var label="storage path">/data</var>' }}",
},
],
}
Route storage from the parent realm
When adding your component, you’ll need to offer the appropriate storage path to your component from its parent realm.
// core.cml
{
children: [
...
{
name: "my_component",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm",
},
],
offer: [
...
{{ '<strong>' }}{
storage: "{{ '<var label="storage">data</var>' }}",
from: "self",
to: [ "#my_component" ],
},{{ '</strong>' }}
]
}
Note: If the appropriate storage capability is not currently provided by your component’s parent realm, reach out to component-framework-dev for assistance.
Update component storage index
Components that use storage use a component ID index to
preserve access to persistent storage contents across the migration, such as
core_component_id_index.json5
.
You must update the component index to map the new component moniker to the same
instance within the component that provides the storage capability.
Find any instances of your current v1 component in component index files:
// core_component_id_index.json5
{
instances: [
...
{
instance_id: "...",
appmgr_moniker: {
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cmx",
realm_path: [ ... ]
}
}
]
}
Replace the appmgr_moniker
for your component instance with the new moniker in
the migrated v2 realm, keeping the same instance_id
:
// core_component_id_index.json5
{
instances: [
...
{
instance_id: "...",
moniker: "/core/my_component"
}
]
}
Note: If you are migrating your component to a realm other than core
, the
moniker should reflect that.
Inject storage capabilities into tests
When migrating tests, you will need to inject storage access into your test component if the test driver or any of the other components in the test realm access a storage path.
Following the example in Test uses injected services, add the following to route storage access to your test driver from the test root:
// test_root.cml
}
children: [
{
name: "test_driver",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm",
},
],
offer: [
...
{{ '<strong>' }}{
storage: "{{ '<var label="storage">data</var>' }}",
from: "parent",
to: [ "#test_driver" ],
},{{ '</strong>' }}
],
}
Note: Storage capabilities are backed by in-memory storage in tests and contents will not persist once the test exits.
Directory features {#directory-features}
If your component uses any of the following features, follow the instructions in this section to migrate directory access:
Feature | Description | Directory Capability | Path |
---|---|---|---|
factory-data |
Read-only factory partition data | factory |
/factory |
durable-data |
Persistent data that survives factory reset | durable |
/durable |
shell-commands |
Executable directory of shell binaries | bin |
/bin |
root-ssl-certificates |
Read-only root certificate data | root-ssl-certificates |
/config/ssl |
These features are supported in v2 components using directory capabilities.
Declare the required directory capabilities
When migrating your component manifest, add the following to your CML file:
// my_component.cml
{
use: [
...
{
directory: "{{ '<var label="directory">root-ssl-certificates</var>' }}",
rights: [ "r*" ],
path: "{{ '<var label="directory path">/config/ssl</var>' }}",
},
],
}
Note: Unlike storage locations, which are isolated per-component, directories are a shared resource. You may need to also determine the subdirectory your component needs to access in order to complete this migration.
Route directory from the parent realm
When adding your component, you’ll need to offer the directory capabilities to your component.
// core.cml
{
children: [
...
{
name: "my_component",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm",
},
],
offer: [
...
{{ '<strong>' }}{
directory: "{{ '<var label="directory">root-ssl-certificates</var>' }}",
from: "parent",
to: [ "#my_component" ],
},{{ '</strong>' }}
],
}
Note: If the appropriate directory capability is not currently provided by your component’s parent realm, reach out to component-framework-dev for assistance.
Inject directory path into tests
When migrating tests, you need to inject the directory capabilities in your test if the test driver or any of the other components in the test realm require directory access.
Test Runner Framework only allows the following directory capabilities to be used by non-hermetic tests:
Capability | Description | Path |
---|---|---|
root-ssl-certificates |
Read-only root certificate data | /config/ssl |
Following the example in Test uses injected services, add the following to route directory access to your test driver from the test root:
// test_root.cml
{
children: [
{
name: "test_driver",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm",
},
],
offer: [
...
{{ '<strong>' }}{
directory: "{{ '<var label="directory">root-ssl-certificates</var>' }}",
from: "parent",
to: [ "#test_driver" ],
},{{ '</strong>' }}
],
}
Note: If the appropriate directory capability is not currently provided by the Test Runner Framework, reach out to component-framework-dev for assistance.
Configuration data {#config-data}
If your component uses any of the following features, follow the instructions in this section to migrate directory access:
Feature | Description | Directory Capability | Path |
---|---|---|---|
config-data |
Read-only configuration data | config-data |
/config/data |
These features are supported in v2 components using directory capabilities.
Declare the required directory capabilities
When migrating your component manifest, add the following to your CML file:
// my_component.cml
{
use: [
...
{
directory: "config-data",
rights: [ "r*" ],
path: "/config/data",
},
],
}
Route directory from the parent realm
When adding your component, you’ll need to offer the directory capability with the appropriate subdirectory to your component.
// core.cml
{
children: [
...
{
name: "my_component",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm",
},
],
offer: [
...
{{ '<strong>' }}{
directory: "config-data",
from: "parent",
to: [ "#my_component" ],
subdir: "{{ '<var label="package name">my-package</var>' }}",
},{{ '</strong>' }}
],
}
Inject directory path into tests
When migrating tests, you need to inject the directory capability with the appropriate subdirectory in your test if the test driver or any of the other components in the test realm require directory access. The name of the subdirectory should match the name of the package that contains the component.
Following the example in Test uses injected services, add the following to route directory access to your test driver from the test root:
// test_root.cml
{
children: [
{
name: "test_driver",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm",
},
],
offer: [
...
{{ '<strong>' }}{
directory: "config-data",
from: "parent",
to: [ "#test_driver" ],
subdir: "{{ '<var label="package name">my-package</var>' }}",
},{{ '</strong>' }}
],
}
Device directories {#devices}
If your component uses any of the following features, follow the instructions in this section to migrate device access:
Feature | Description | Path |
---|---|---|
dev |
Device driver entries in devfs |
/dev/class/* |
Device filesystem access is supported in Components v2 using directory capabilities.
Consider the following example using Components v1 to access /dev/class/input-report
:
// my_component.cmx
{
"program": { ... },
"sandbox": {
"dev": [
"{{ '<var label="device subpath">class/input-report</var>' }}"
]
}
}
Declare the required device capabilities
When migrating your component manifest, add the device path as a directory capability to your CML file:
// my_component.cml
{
use: [
...
{
directory: "{{ '<var label="device">dev-input-report</var>' }}",
rights: [ "r*" ],
path: "/dev/{{ '<var label="device subpath">class/input-report</var>' }}",
},
],
}
Route device subdirectory from the parent realm
When adding your component, you’ll need to offer the appropriate device path to your component from its parent realm.
// core.cml
{
children: [
...
{
name: "my_component",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm",
},
],
offer: [
...
{{ '<strong>' }}{
directory: "dev",
from: "parent",
as: "{{ '<var label="device">dev-input-report</var>' }}",
to: [ "#my_component" ],
subdir: "{{ '<var label="device subpath">class/input-report</var>' }}",
},{{ '</strong>' }}
],
}
Inject device directory into tests
When migrating tests, you need to inject the directory capabilities in your test if the test driver or any of the other components in the test realm require directory access.
Test Runner Framework only allows the following device directories to be used by non-hermetic tests:
Capability | Description |
---|---|
dev-input-report |
Input method events |
dev-display-controller |
Graphical display controller |
dev-goldfish-address-space |
Goldfish address space device |
dev-goldfish-control |
Goldfish control device |
dev-goldfish-pipe |
Goldfish pipe device |
dev-gpu |
GPU device |
Following the example in Test uses injected services, add the following to route directory access to your test driver from the test root:
// test_root.cml
{
children: [
{
name: "test_driver",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm",
},
],
offer: [
...
{{ '<strong>' }}{
directory: "{{ '<var label="device">dev-input-report</var>' }}",
from: "parent",
to: [ "#test_driver" ],
},{{ '</strong>' }}
],
}
Note: If the appropriate device directory is not currently provided by the Test Runner Framework, reach out to component-framework-dev for assistance.
Event capabilities {#events}
If your component uses any of the following features, follow the instructions in this section:
Feature | Description | Path |
---|---|---|
hub |
Observing component path changes | /hub/c/* |
hub |
Observing realm path changes | /hub/r/* |
These features are supported in v2 components using event capabilities.
Inject event sources into tests
When migrating tests, you’ll need to inject any components you wish to observe into the test realm and route the apppropriate lifecycle events for those components to your test driver.
Following the example in Test uses injected services,
offer the fuchsia.sys2.EventSource
capability and the appropriate events to
your test driver from the test root:
// test_root.cml
{
children: [
{
name: "test_driver",
url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm",
},
],
offer: [
{{ '<strong>' }}{
protocol: "fuchsia.sys2.EventSource",
from: "parent",
to: [ "#test_driver" ],
},
{
event: [ "{{ '<var label="event name">started</var>' }}" ],
from: "framework",
to: [ "#test_driver" ],
},{{ '</strong>' }}
],
}
Note: The EventSource
capability comes from the test realm (parent
), but the
events come from the Component Manager (framework
). This sets the event scope
to only components in the test root’s topology. For more details on event scope,
see Using events.
In your test driver, consume the events routed by the test root:
// test_driver.cml
{
use: [
{
protocol: "fuchsia.sys2.EventSource",
from: "parent"
},
{
event: [ "{{ '<var label="event name">started</var>' }}" ],
from: "parent",
modes: [ "async" ],
},
],
}
Build Info {#build-info}
When migrating the build-info
feature, consider moving from using a v1,
read-only config directory, to the fuchsia.buildinfo.Provider
protocol.
This protocol is the preferred method of retrieving build information. To use
this protocol, add it while declaring required services.
If migrating to using the protocol is not possible, then an alternative method
is to use the build-info
directory capability. To use this directory,
follow the instructions for migrating directory features,
using the directory capability build-info
and path /config/build-info
.