| # Migrating system components |
| |
| This document provides instructions for migrating a system component from |
| [Components v1][glossary-components-v1] to [Components v2][glossary-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][sysmgr-config]. |
| |
| To migrate your system component from v1 to v2, do the following: |
| |
| - [Prerequisites](#prerequisites) |
| - [Migrate the component manifest](#create-component-manifest) |
| - [Migrate the tests](#migrate-tests) |
| |
| Depending on the features your component supports, you may need to explore the |
| following sections for additional guidance: |
| |
| - [Diagnostics capabilities](#diagnostics) |
| - [Other common capabilities](#other-capabilities) |
| - [Converting CMX features](#cmx-features) |
| |
| For more details on the components migration effort, see |
| [State of the Components v2 Migration][components-migration-status]. |
| |
| ## 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][build-migration] before continuing. |
| |
| You should also familiarize yourself with the following topics: |
| |
| - [Introduction to the Fuchsia Component Framework][components-intro]: |
| 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][ftf-intro]: |
| 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][glossary-component-manifest] 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! |
| |
| 1. Determine where your CMX file is located in the source tree |
| (for example, [`fonts.cmx`][example-fonts]). |
| Create a file in the same directory that has the same filename but with a `.cml` |
| extension, with the following contents: |
| |
| ```json5 |
| { |
| 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. |
| |
| 1. Find the build rule that defines your component. Normally, this is a |
| `fuchsia_component` rule. For example, see the fonts |
| [`BUILD.gn`][example-package-rule]. |
| |
| ```gn |
| fuchsia_component("fonts") { |
| manifest = "meta/fonts.cmx" |
| deps = [ ":font_provider" ] |
| } |
| ``` |
| |
| 1. Update the `manifest` element of the associated `fuchsia_component` rule to |
| point to your new `.cml` file instead: |
| |
| ```gn |
| fuchsia_component("fonts") { |
| manifest = "meta/fonts.cml" |
| deps = [ ":font_provider" ] |
| } |
| ``` |
| |
| 1. Build the target for your package: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| You are ready to start writing your v2 component manifest. |
| |
| ### Adding the executable {#component-executable} |
| |
| Add the [`program`][manifests-program] section of your CML file along with the |
| appropriate runner declaration. |
| |
| Note: The [runner][glossary-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. |
| |
| ```json5 |
| // fonts.cmx |
| { |
| "program": { |
| "binary": "bin/font_provider" |
| } |
| ... |
| } |
| ``` |
| |
| ```json5 |
| // fonts.cml |
| { |
| program: { |
| runner: "elf", |
| binary: "bin/font_provider", |
| } |
| } |
| ``` |
| |
| ### Declaring required services {#required-services} |
| |
| Add [`use`][manifests-use] declarations to your CML file. These are the |
| approximate equivalent of the [`services`][cmx-services] list in CMX. |
| |
| ```json5 |
| // fonts.cmx |
| { |
| "program": { |
| "binary": "bin/app" |
| } |
| "sandbox": { |
| "services": [ |
| "fuchsia.logger.LogSink", |
| "fuchsia.pkg.FontResolver" |
| ] |
| ... |
| } |
| } |
| ``` |
| |
| Convert each element of the `services` list to a `use` declaration for the |
| corresponding service `protocol`. |
| |
| ```json5 |
| // fonts.cml |
| { |
| include: [ |
| // Enable system logging |
| "sdk/lib/diagnostics/syslog/client.shard.cml", |
| ], |
| program: { |
| runner: "elf", |
| binary: "bin/font_provider", |
| }, |
| use: [ |
| { |
| protocol: [ |
| "fuchsia.pkg.FontResolver", |
| ], |
| }, |
| ], |
| } |
| ``` |
| |
| ### Exposing available services {#available-services} |
| |
| In [Components v1][glossary-components-v2], you typically declare information |
| about services exposed by a component in a [sysmgr configuration file][sysmgr-config]. |
| These files are referenced by `config_data` targets in the build, and specify |
| mappings of services to components in the `sys` [environment][glossary-environment]. |
| |
| Note: The most common location of this service mapping is |
| [`services.config`][example-services-config], which defines service mappings |
| that apply to every product configuration. |
| |
| 1. Identify all service mappings, if any, for your component. |
| You can use [CodeSearch][code-search] to find service mappings. Here is a |
| [sample search][sysmgr-config-search]. |
| |
| ```json5 |
| // services.config |
| { |
| "services": { |
| ... |
| "fuchsia.fonts.Provider": "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx", |
| ... |
| } |
| } |
| ``` |
| |
| 1. For each service mapping, add an [`expose`][manifests-expose] declaration |
| and a corresponding [`capabilities`][manifests-capabilities] entry with the |
| service `protocol`. |
| |
| ```json5 |
| // fonts.cml |
| { |
| include: [ |
| // Enable system logging |
| "sdk/lib/diagnostics/syslog/client.shard.cml", |
| ], |
| program: { |
| runner: "elf", |
| binary: "bin/font_provider", |
| }, |
| capabilities: [ |
| { |
| protocol: [ "fuchsia.fonts.Provider" ], |
| }, |
| ], |
| use: [ |
| { |
| protocol: [ |
| "fuchsia.pkg.FontResolver", |
| ], |
| }, |
| ], |
| expose: [ |
| { |
| protocol: "fuchsia.fonts.Provider", |
| from: "self", |
| }, |
| ], |
| } |
| ``` |
| |
| 1. Build your updated package: |
| |
| ```posix-terminal |
| fx build |
| ``` |
| |
| 1. Verify that your package includes the compiled v2 component manifest |
| (with a `.cm` extension). |
| |
| ```posix-terminal |
| 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`][example-package-rule]. |
| |
| 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`][fuchsia-test-facets]? |
| - Does your test create environments in-process? If so, does it create a |
| separate environment for each test case? |
| - Does your test have a CMX containing |
| [`system-services`][system-services]? |
| |
| ### Update the test configuration {#update-test-config} |
| |
| The migration procedure varies depending on the testing framework features in |
| your v1 component: |
| |
| - [Test has no injected services](#no-injected-services): The test is |
| generated by a `fuchsia_unittest_package` GN rule, or its |
| CMX does not contain [`injected-services`][fuchsia-test-facets]. |
| - [Test has injected services](#injected-services): The test has a CMX |
| that contains [fuchsia.test facets][fuchsia-test-facets]. |
| |
| #### Test with no injected services {#no-injected-services} |
| |
| For tests that use no injected services, your [test root][ftf-roles] can be the |
| same component as the [test driver][ftf-roles]. |
| 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](#create-component-manifest) |
| that you used to package your component. |
| |
| Consider the following example test component: |
| |
| ```json5 |
| // fonts_test.cmx |
| { |
| "program": { |
| "binary": "bin/font_test" |
| } |
| } |
| ``` |
| |
| To migrate this test to the v2 testing framework, do the following: |
| |
| 1. Create a CML file that points to the test binary that includes the |
| appropriate [test runner][ftf-test-runners]: |
| |
| Note: See [test runners][ftf-provided-test-runners] that are provided by the |
| framework. |
| |
| ```json5 |
| // 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", |
| } |
| } |
| ``` |
| |
| 1. Update the `fuchsia_component` rule for your test component to reference the |
| new CML file: |
| |
| ```gn |
| 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 injected services {#injected-services} |
| |
| For tests that use [fuchsia.test facets][fuchsia-test-facets], such as |
| `injected-services`, your [test root][ftf-roles] and [test driver][ftf-roles] |
| must be split into different components to enable proper capability routing. |
| |
| In this example, suppose there's a single injected service, |
| `fuchsia.pkg.FontResolver`: |
| |
| ```json5 |
| // 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 v2 testing framework, do the following: |
| |
| 1. Create a CML file for the test driver that points to the test binary, |
| and includes the appropriate [test runner][ftf-test-runners]: |
| |
| Note: See [test runners][ftf-provided-test-runners] that are provided by the |
| framework. |
| |
| ```json5 |
| // 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", |
| } |
| } |
| ``` |
| |
| 1. 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. |
| |
| ```json5 |
| // 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](#create-component-manifest) that you |
| used to package your component. |
| |
| 1. 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][ftf-test-suite]. |
| |
| ```json5 |
| // 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" ], |
| }, |
| ], |
| } |
| ``` |
| |
| 1. Add `fuchsia_component` rules for each CML file, and update the |
| `fuchsia_package` to reference the child components as dependencies: |
| |
| ```gn |
| 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: |
| |
| ```posix-terminal |
| 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][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][cf-dev-list] |
| for guidance. |
| |
| Now you're ready to add your new component to the |
| [v2 component topology][components-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](#create-component-manifest). |
| The steps below refer to the collection of all these services as your |
| component’s "exposed services". |
| |
| ```json5 |
| // 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`][cs-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). |
| |
| ```json5 |
| // 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" ], |
| }, |
| ], |
| } |
| ``` |
| |
| ### Expose services to sys environment {#expose-services} |
| |
| Declare each of these services in [`appmgr.cml`][cs-appmgr-cml] to make them |
| available to v1 components under the `sys` environment. |
| Change `appmgr.cml` as follows: |
| |
| ```json5 |
| // 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`][manifests-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][sysmgr-config-search]). |
| |
| There are three possible cases: |
| |
| - [v1 component provides service](#v1-component-provides-service): |
| The provider of the service is a v1 component. |
| - [v2 component in `core.cml` provides service](#v2-core-cml-provides-service): |
| The provider of the service is a v2 component that's a child of `core.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][cf-dev-list] for |
| assistance. |
| |
| Note: You must also route all services requested by any manifest shards listed |
| in your manifest's [`include`][manifests-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`][cs-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: |
| |
| ```json5 |
| // appmgr.cml |
| { |
| expose: [ |
| ... |
| { |
| protocol: [ |
| ... // (Any services already exposed from appmgr are here) |
| "fuchsia.pkg.FontResolver", |
| ], |
| from: "self", |
| }, |
| ... |
| ], |
| } |
| ``` |
| |
| ```json5 |
| // 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`: |
| |
| ```json5 |
| // core.cml |
| { |
| offer: [ |
| ... |
| { |
| protocol: [ "fuchsia.pkg.FontResolver" ], |
| from: "#font_resolver", |
| to: [ "#font_provider" ], |
| }, |
| ... |
| ], |
| } |
| ``` |
| |
| ### Remove sysmgr configuration entries {#remove-config-entries} |
| |
| Before you test your component, remove the service mappings in |
| [`services.config`][example-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`. |
| |
| ```json5 |
| // 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][fx-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. |
| |
| ```posix-terminal |
| 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](#other-capabilities) |
| and [Converting CMX features](#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][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](#create-component-manifest). |
| |
| ## Diagnostics capabilities {#diagnostics} |
| |
| This section provides guidance on migrating the capabilities for diagnostics |
| features to Components v2. |
| |
| ### Inspect {#inspect} |
| |
| If your component is using [Inspect][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][archivist] to read Inspect data for snapshots, |
| [iquery][iquery], etc. |
| |
| Note: For more details on the differences in data collection between |
| Components v1 and Components v2, see the [Archivist documentation][archivist]. |
| |
| You can add Inspect capabilities to your v2 component by including the following |
| manifest shard: |
| |
| ```json5 |
| // my_component.cml |
| { |
| // Expose the diagnostics directory capability for Inspect |
| include: [ "sdk/lib/diagnostics/inspect/client.shard.cml" ], |
| ... |
| } |
| ``` |
| |
| ### Logging {#logging} |
| |
| If your component requires access to [logging][logs], 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: |
| |
| ```json5 |
| // 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](#syslog-redirect): 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](#debuglog-redirect): 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][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](#create-component-manifest), include |
| the following manifest shard to enable forwarding: |
| |
| ```json5 |
| // 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][syslog]. |
| |
| #### Redirecting to debuglog {#debuglog-redirect} |
| |
| Your component may have external dependencies that rely on log messages in |
| [debuglog][debug-log]. One common use case is integration tests that directly |
| parse log messages from the `stdout` of an emulator process using the |
| [emulatortest][emulatortest] framework. In these cases, you'll need to manually |
| direct log messages back to the `debuglog` buffer. |
| |
| 1. When [migrating your component manifest](#create-component-manifest), |
| request the `fuchsia.boot.WriteOnlyLog` capability. |
| |
| ```json5 |
| // my_component.cml |
| { |
| use: [ |
| ... |
| { |
| protocol: [ |
| "fuchsia.boot.WriteOnlyLog", |
| ], |
| }, |
| ], |
| } |
| ``` |
| |
| 1. When [adding your component](#add-component-to-topology), add the following |
| to offer this capability to your component from `core`: |
| |
| ```json5 |
| // core.cml |
| { |
| offer: [ |
| ... |
| { |
| protocol: [ "fuchsia.boot.WriteOnlyLog" ], |
| from: "parent", |
| to: [ "#my_component" ], |
| }, |
| ], |
| } |
| ``` |
| |
| 1. Direct `stderr` and `stdout` to `debuglog` in your program. |
| You can use libraries for the initialization if your component is written in |
| [Rust][debug-log-rust] or [C++][debug-log-cpp]. |
| |
| 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. |
| |
| ## 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 you must route the |
| `universe` resolver to it. You can check if your package is in the `base` set. |
| |
| ```posix-terminal |
| fx list-packages --base |
| ``` |
| |
| Note: You can use `--cache` and `--universe` to inspect what is in the `cache` |
| and `universe` sets, respectively. |
| |
| Resolvers are routed to components via environments. First define the |
| environment in the `environments` section. |
| |
| ```json5 |
| // core.cml |
| { |
| environments: [ |
| ... |
| { |
| name: "my_component_env", |
| // inherits things from your parent's realm, almost always the right thing |
| extends: "realm", |
| resolvers: [ |
| { |
| resolver: "universe-resolver", |
| scheme: "fuchsia-pkg", |
| // "#universe-resolver" is a child of core |
| from: "#universe-resolver", |
| }, |
| ], |
| }, |
| ] |
| } |
| ``` |
| |
| Then assign the `environment` to your component. |
| |
| ```json5 |
| // core.cml |
| { |
| children: [ |
| ... |
| { |
| name: "my_component", |
| url: "fuchsia-pkg://fuchsia.com/my-pkg#meta/my_component.cm", |
| environment: "#my_component_env", |
| }, |
| ], |
| } |
| ``` |
| |
| ### 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][#expose-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][#directory-features] for more details. |
| |
| Shell binaries are run in the `sys` [environment][glossary-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, |
| please reach out to [component-framework-dev][cf-dev-list]. |
| |
| ## Converting CMX features {:#cmx-features} |
| |
| This section provides guidance on migrating additional CMX [`sandbox`][cmx-services] |
| features. If there's a feature in your CMX file that's not in this list, |
| please reach out to [component-framework-dev][cf-dev-list]. |
| |
| ### 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][storage-capabilities]. |
| |
| #### Declare the required storage capabilities |
| |
| When [migrating your component manifest](#create-component-manifest), add |
| the following to your CML file: |
| |
| ```json5 |
| // 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](#add-component-to-topology), |
| you'll need to offer the appropriate storage path to your component from its |
| parent realm. |
| |
| ```json5 |
| // core.cml |
| { |
| children: [ |
| ... |
| { |
| name: "my_component", |
| url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm", |
| }, |
| ], |
| offer: [ |
| ... |
| { |
| storage: "{{ '<var label="storage">data</var>' }}", |
| from: "self", |
| to: [ "#my_component" ], |
| }, |
| ] |
| } |
| ``` |
| |
| Note: If the appropriate storage capability is not currently provided by your |
| component's parent realm, reach out to [component-framework-dev][cf-dev-list] |
| for assistance. |
| |
| #### Update component storage index |
| |
| Components that use storage use a [component ID index][component-id-index] to |
| preserve access to persistent storage contents across the migration, such as |
| [`core_component_id_index.json5`][example-component-id-index]. |
| 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: |
| |
| ```json5 |
| // 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`: |
| |
| ```json5 |
| // 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](#migrate-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](#injected-services), add |
| the following to route storage access to your test driver from the test root: |
| |
| ```json5 |
| // test_root.cml |
| } |
| children: [ |
| { |
| name: "test_driver", |
| url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm", |
| }, |
| ], |
| offer: [ |
| ... |
| { |
| storage: "{{ '<var label="storage">data</var>' }}", |
| from: "parent", |
| to: [ "#test_driver" ], |
| }, |
| ], |
| } |
| ``` |
| |
| 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 | `config-ssl` | `/config/ssl` | |
| |
| These features are supported in v2 components using |
| [directory capabilities][directory-capabilities]. |
| |
| #### Declare the required directory capabilities |
| |
| When [migrating your component manifest](#create-component-manifest), add |
| the following to your CML file: |
| |
| ```json5 |
| // my_component.cml |
| { |
| use: [ |
| ... |
| { |
| directory: "{{ '<var label="directory">config-ssl</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](#add-component-to-topology), you'll need to offer |
| the directory capabilities to your component. |
| |
| ```json5 |
| // core.cml |
| { |
| children: [ |
| ... |
| { |
| name: "my_component", |
| url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm", |
| }, |
| ], |
| offer: [ |
| { |
| directory: "{{ '<var label="directory">config-ssl</var>' }}", |
| from: "parent", |
| to: [ "#my_component" ], |
| }, |
| ... |
| ], |
| } |
| ``` |
| |
| Note: If the appropriate directory capability is not currently provided by your |
| component's parent realm, reach out to |
| [component-framework-dev][cf-dev-list] for assistance. |
| |
| #### Inject directory path into tests |
| |
| When [migrating tests](#migrate-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 Framework only allows following pre-approved global directories to be used |
| by the test: |
| |
| | Feature | Description | Directory Capability | Path | |
| | ------- | ----------- | -------------------- | ---- | |
| | `root-ssl-certificates` | Read-only root certificate data | `config-ssl` | `/config/ssl` | |
| |
| Following the example in [Test uses injected services](#injected-services), add |
| the following to route directory access to your test driver from the test root: |
| |
| ```json5 |
| // test_root.cml |
| { |
| children: [ |
| { |
| name: "test_driver", |
| url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm", |
| }, |
| ], |
| offer: [ |
| ... |
| { |
| directory: "{{ '<var label="directory">config-ssl</var>' }}", |
| from: "parent", |
| to: [ "#test_driver" ], |
| }, |
| ], |
| } |
| ``` |
| |
| Note: If the appropriate directory capability is not currently provided by |
| testing framework, reach out to [component-framework-dev][cf-dev-list] 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][directory-capabilities]. |
| |
| #### Declare the required directory capabilities |
| |
| When [migrating your component manifest](#create-component-manifest), add |
| the following to your CML file: |
| |
| ```json5 |
| // my_component.cml |
| { |
| use: [ |
| ... |
| { |
| directory: "config-data", |
| rights: [ "r*" ], |
| path: "/config/data", |
| }, |
| } |
| ``` |
| #### Route directory from the parent realm |
| |
| When [adding your component](#add-component-to-topology), you'll need to offer |
| the directory capability with the appropriate subdirectory to your component. |
| |
| ```json5 |
| // core.cml |
| { |
| children: [ |
| ... |
| { |
| name: "my_component", |
| url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm", |
| }, |
| ], |
| offer: [ |
| { |
| directory: "config-data", |
| from: "parent", |
| to: [ "#my_component" ], |
| subdir: "{{ '<var label="package name">my-package</var>' }}", |
| }, |
| ... |
| ], |
| } |
| ``` |
| |
| #### Inject directory path into tests |
| |
| When [migrating tests](#migrate-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](#injected-services), add |
| the following to route directory access to your test driver from the test root: |
| |
| ```json5 |
| // test_root.cml |
| { |
| children: [ |
| { |
| name: "test_driver", |
| url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component_test.cm", |
| }, |
| ], |
| offer: [ |
| ... |
| { |
| directory: "config-data", |
| from: "parent", |
| to: [ "#test_driver" ], |
| subdir: "{{ '<var label="package name">my-package</var>' }}", |
| }, |
| ], |
| } |
| ``` |
| |
| ### 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][device-model] access is supported in Components v2 using |
| [directory capabilities][directory-capabilities]. |
| |
| Consider the following example using Components v1 to access `/dev/class/input-report`: |
| |
| ```json5 |
| // my_component.cmx |
| { |
| "program": { ... }, |
| "sandbox": { |
| "dev": [ |
| "{{ '<var label="device subpath">class/input-report</var>' }}" |
| ] |
| } |
| } |
| ``` |
| |
| #### Declare the required device capabilities |
| |
| When [migrating your component manifest](#create-component-manifest), add |
| the device path as a directory capability to your CML file: |
| |
| ```json5 |
| // 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](#add-component-to-topology), |
| you'll need to offer the appropriate device path to your component from its |
| parent realm. |
| |
| ```json5 |
| // core.cml |
| { |
| children: [ |
| ... |
| { |
| name: "my_component", |
| url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm", |
| }, |
| ], |
| offer: [ |
| ... |
| { |
| directory: "dev", |
| from: "parent", |
| as: "{{ '<var label="device">dev-input-report</var>' }}", |
| to: [ "#my_component" ], |
| subdir: "{{ '<var label="device subpath">class/input-report</var>' }}", |
| }, |
| ] |
| } |
| ``` |
| |
| |
| [archivist]: /docs/reference/diagnostics/inspect/tree.md#archivist |
| [build-migration]: /docs/development/components/build.md#legacy-package-migration |
| [cf-dev-list]: https://groups.google.com/a/fuchsia.dev/g/component-framework-dev |
| [cmx-services]: /docs/concepts/components/v1/component_manifests.md#sandbox |
| [code-search]: https://cs.opensource.google/fuchsia |
| [component-id-index]: /docs/development/components/component_id_index.md |
| [components-intro]: /docs/concepts/components/v2/introduction.md |
| [components-topology]: /docs/concepts/components/v2/topology.md |
| [components-migration-status]: /docs/concepts/components/v2/migration.md |
| [cs-appmgr-cml]: /src/sys/appmgr/meta/appmgr.cml |
| [cs-core-cml]: /src/sys/core/meta/core.cml |
| [debug-log]: /docs/development/diagnostics/logs/recording.md#debuglog_handles |
| [debug-log-cpp]: /src/sys/lib/stdout-to-debuglog/cpp |
| [debug-log-rust]: /src/sys/lib/stdout-to-debuglog/rust |
| [device-model]: /docs/concepts/drivers/device_driver_model/device-model.md |
| [directory-capabilities]: /docs/concepts/components/v2/capabilities/directory.md |
| [emulatortest]: /tools/emulator/emulatortest |
| [example-component-id-index]: /src/sys/appmgr/config/core_component_id_index.json5 |
| [example-fonts]: https://fuchsia.googlesource.com/fuchsia/+/cd29e692c5bfdb0979161e52572f847069e10e2f/src/fonts/meta/fonts.cmx |
| [example-package-rule]: https://fuchsia.googlesource.com/fuchsia/+/cd29e692c5bfdb0979161e52572f847069e10e2f/src/fonts/BUILD.gn |
| [example-services-config]: /src/sys/sysmgr/config/services.config |
| [ftf-intro]: /docs/concepts/testing/v2/test_runner_framework.md |
| [ftf-roles]: /docs/concepts/testing/v2/test_runner_framework.md#test-roles |
| [ftf-test-runners]: /docs/concepts/testing/v2/test_runner_framework.md#test-runners |
| [ftf-provided-test-runners]: /src/sys/test_runners |
| [ftf-test-suite]: /docs/concepts/testing/v2/test_runner_framework.md#test-suite-protocol |
| [fuchsia-test-facets]: /docs/concepts/testing/v1_test_component.md |
| [fx-scrutiny]: https://fuchsia.dev/reference/tools/fx/cmd/scrutiny |
| [glossary-component-manifest]: /docs/glossary.md#component-manifest |
| [glossary-components-v1]: /docs/glossary.md#components-v1 |
| [glossary-components-v2]: /docs/glossary.md#components-v2 |
| [glossary-environment]: /docs/glossary.md#environment |
| [glossary-runner]: /docs/glossary.md#runner |
| [inspect]: /docs/development/diagnostics/inspect/README.md |
| [iquery]: /docs/reference/diagnostics/consumers/iquery.md |
| [logs]: /docs/development/diagnostics/logs/README.md |
| [manifests-capabilities]: /docs/concepts/components/v2/component_manifests.md#capabilities |
| [manifests-expose]: /docs/concepts/components/v2/component_manifests.md#expose |
| [manifests-program]: /docs/concepts/components/v2/component_manifests.md#program |
| [manifests-use]: /docs/concepts/components/v2/component_manifests.md#use |
| [manifests-include]: /docs/concepts/components/v2/component_manifests.md#include |
| [storage-capabilities]: /docs/concepts/components/v2/capabilities/storage.md |
| [syslog]: /docs/development/diagnostics/logs/recording.md#logsinksyslog |
| [sysmgr-config-search]: https://cs.opensource.google/search?q=fuchsia-pkg:%2F%2Ffuchsia.com%2F.*%23meta%2Fmy_component.cmx%20-f:.*.cmx$%20%5C%22services%5C%22&ss=fuchsia |
| [sysmgr-config]: /src/sys/sysmgr/sysmgr-configuration.md |
| [system-services]: /docs/concepts/testing/v1_test_component.md#services |
| [troubleshooting-components]: /docs/development/components/v2/troubleshooting.md |