To migrate your system component, follow these steps:
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{: .external}, 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 "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 fonts BUILD.gn
.
fuchsia_component("fonts") { manifest = "meta/fonts.cmx" deps = [ ":font_provider" ] }
Update the manifest
element of the associated fuchsia_component
rule to point to your new .cml
file instead:
fuchsia_component("fonts") { {{ '<strong>' }}manifest = "meta/fonts.cml"{{ '</strong>' }} deps = [ ":font_provider" ] }
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": [ "syslog/client.shard.cmx" ], {{ '<strong>' }}"program": { "binary": "bin/font_provider" },{{ '</strong>' }} ... }
// fonts.cml { include: [ // Enable system logging "syslog/client.shard.cml", ], {{ '<strong>' }}program: { runner: "elf", binary: "bin/font_provider", },{{ '</strong>' }} }
Add use
declarations to your CML file. These are the approximate equivalent of the services
list in CMX.
// fonts.cmx { "include": [ "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 "syslog/client.shard.cml", ], program: { runner: "elf", binary: "bin/font_provider", }, {{ '<strong>' }}use: [ { protocol: [ "fuchsia.pkg.FontResolver" ], }, ],{{ '</strong>' }} }
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 corresponding capabilities
entry with the service protocol
.
// fonts.cml { include: [ // Enable system logging "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>' }}$"
Note: it is valid to use
from self
in the unusual case that your component both consumes and publishes the same protocol. You‘ll know this is the case when the “services” section in your .cmx
references a protocol that is mapped to the same component’s URL in a services.config
file.
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", ... } }
core.cml
.Create a manifest shard. A manifest shard uses generally the syntax as a manifest, but may reference objects that don't exist within the manifest itself. In this case we reference the session-manager
child which is not defined here, but we know is defined in core
's manifest.
// component.core_shard.cml { children: [ { name: "font_provider", url: "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cm", }, ], offer: [ { protocol: "fuchsia.fonts.Provider", from: "#font_provider", to: [ "#session-manager" ], }, ], }
Create a target in your BUILD.gn
that defines the core_shard
.
# font_provider/BUILD.gn import("//src/sys/core/build/core_shard.gni") core_shard("font_provider_shard") { shard_file = "component.core_shard.cml" }
Add the core realm shard to the appropriate products. For example, you can add the component to the workstation product by modifying //products/workstation.gni
by adding the build target path to the core_realm_shards
array. If your component is present on all products that derive from core and you are adding it via a shard, modify //products/core.gni
.
# //products/workstation.gni ... core_realm_shards += [ ... "//path/to/font_provider:font_provider_shard", ] ...
Add your component as a child instance of the core.cml
component, and offer its exposed services to dependent components. 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: [ "#session-manager" ], }, ], }
It is very common for there to be components in the v1 sys
realm that depend on your component‘s exposed services. Make your component’s services available to the v1 sys
realm by adding a declaration like the following to your core realm shard or core.cml
.
// core.cml / component.core_shard.cml { use: [ { protocol: "fuchsia.fonts.FontProvider", from: "#font_provider", }, ], }
Note: You do not need to offer the service to appmgr
. core
itself proxies to and from the v1 sys
realm, which is why we have core
use
the service.
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:
core.cml
provides service: The provider of the service is a v2 component that's a child of core.cml
.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
.
You’ll reach this case if a mapping for the service exists in a sysmgr config file. To make the service available to your component, do the following.
Make sure a declaration like the following is present in appmgr.core_shard.cml
(if the service is configured in fuchsia.git) or your core realm shard (if it's configured outside fuchsia.git):
// appmgr.core_shard.cml / component.core_shard.cml { capabilities: [ ... { protocol: "fuchsia.pkg.FontResolver" }, ... ], }
Add the following to your component's core realm shard or core.cml
:
// core.cml / component.core_shard.cml { offer: [ ... { protocol: "fuchsia.pkg.FontResolver", from: "self", to: [ "#font_provider" ], }, ... ], }
Note: You do not offer the service from appmgr
. core
itself proxies to and from the v1 sys
realm, which is why we have core
offer the 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" ], }, ... ], }
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 / component.core_shard.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.
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", ... } }
Explore the following sections for additional migration guidance on specific features your system components may support:
Build the target for your package and manually verify that your component and its dependencies still work as expected.
fx build
Perform manual verification of capability routing using the verify routes
command built into scrutiny.
ffx scrutiny verify routes
This command 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.
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 or one of the components that depends on it isn't working correctly, try following the advice in Troubleshooting components.