Google is committed to advancing racial equity for Black communities. See how.

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.

Background

You should familiarize yourself with the following topics:

Migrate the component manifest

Create a minimal CML CML file and configure it with GN so that it gets compiled and installed in your package.

  1. 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",
        ],
    }
    
  2. 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" ]
    }
    
  3. Update the manifest element of the associated fuchsia_component rule to point to your new .cml file instead:

    fuchsia_component("fonts") {
      manifest = "meta/fonts.cml"
      deps = [ ":font_provider" ]
    }
    
  4. Build the target for your package:

    fx build
    

You are ready to start writing your v2 component manifest.

Adding the executable

Add the program section of your CML file along with the appropriate runner declaration.

// fonts.cmx
{
    "include": [
        "syslog/client.shard.cmx"
    ],
    "program": {
        "binary": "bin/font_provider"
    },
    ...
}
// fonts.cml
{
    include: [
        // Enable system logging
        "syslog/client.shard.cml",
    ],
    program: {
        runner: "elf",
        binary: "bin/font_provider",
    },
}

Declaring required services

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"
    },
    "sandbox": {
        "services": [
            "fuchsia.logger.LogSink",
            "fuchsia.pkg.FontResolver"
        ]
        ...
    }
}

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",
    },
    use: [
        {
            protocol: [ "fuchsia.pkg.FontResolver" ],
        },
    ],
}

Exposing 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 .

  1. 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",
            ...
        }
    }
    
  2. 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",
        },
        capabilities: [
            {
                protocol: [ "fuchsia.fonts.Provider" ],
            },
        ],
        use: [
            {
                protocol: [ "fuchsia.pkg.FontResolver" ],
            },
        ],
        expose: [
            {
                protocol: "fuchsia.fonts.Provider",
                from: "self",
            },
        ],
    }
    
  3. Build your updated package:

    fx build
    
  4. Verify that your package includes the compiled v2 component manifest (with a .cm extension).

    ffx scrutiny shell "search.components --url my_component.cm$"
    

Migrate the 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 component. 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 as injected-services or system-services?
  • Does your test create environments in-process? If so, does it create a separate environment for each test case?

Update the test configuration

The migration procedure varies depending on the testing framework features in your v1 component:

No service dependencies

Test components that do not use fuchsia.test facets can migrate to Components v2 using the same basic procedure described in Migrate the component manifest.

Consider the following example test component:

// my_component_test.cmx
{
    "include": [
        "syslog/client.shard.cmx"
    ],
    "program": {
        "binary": "bin/my_component_test"
    }
}

To migrate this test to the Test Runner Framework, do the following:

  1. Create a CML file that points to the test binary that includes the appropriate test runner:

    // my_component_test.cml
    {
        include: [
            // Select the appropriate test runner shard here:
            // rust, gtest, go, etc.
            "//src/sys/test_runners/rust/default.shard.cml",
            // For common includes such as the below, typically you will find
            // a `.cmx` and a `.cml` equivalent at the same path.
            "syslog/client.shard.cml",
        ],
        program: {
            binary: "bin/my_component_test",
        }
    }
    
  2. Update the build definitions for your test to reference the new CML file:

    fuchsia_unittest_package("my_component_tests") {
      manifest = "meta/my_component_test.cml"
      deps = [ ":my_component_test" ]
    }
    

In the example above, the test component's manifest is simple enough that it can be generated for you at build time by fuchsia_unittest_package() or fuchsia_unittest_component(). This is the preferred practice for simple unit tests. To allow the GN target to generate your CML file, simply edit it to remove the manifest.

fuchsia_unittest_package("my_component_tests") {
  manifest = "meta/my_component_test.cml"
  deps = [ ":my_component_test" ]
}

System service dependencies

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.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
fuchsia.sys.Loader CFv1 loader service to help with migration.
fuchsia.sys.Environment CFv1 environment service to help with migration.

Consider the following example test component that uses a single system service, fuchsia.sysmem.Allocator:

// my_component_test.cmx
{
    "facets": {
        "fuchsia.test": {
            "system-services": [
                "fuchsia.sysmem.Allocator"
            ]
        }
    },
    "program": {
        "binary": "bin/my_component_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_component_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_component_test",
    },
    use: [
        {
            protocol: [ "fuchsia.sysmem.Allocator" ],
        },
    ],
}

Injected service dependencies

Injecting v2 components

For tests that use other fuchsia.test facets, such as injected-services, your test component manifest must declare each dependent component and route the provided capabilities to the test component.

In the following example, suppose there's a single injected service, fuchsia.pkg.FontResolver:

// my_component_test.cmx
{
    "facets": {
        "fuchsia.test": {
            "injected-services": {
                "fuchsia.pkg.FontResolver":
                    "fuchsia-pkg://fuchsia.com/font_provider_test#meta/mock_font_resolver.cmx"
            }
        }
    },
    "program": {
        "binary": "bin/my_component_test"
    },
    "sandbox": {
        "services": [
            "fuchsia.pkg.FontResolver"
        ]
    }
}

To migrate this test to the Test Runner Framework, do the following:

  1. Create a CML file for the test component that points to the test binary and includes the appropriate test runner:

    // my_component_test.cml (test component)
    {
        include: [
            // Select the appropriate test runner shard here:
            // rust, gtest, go, etc.
            "//src/sys/test_runners/rust/default.shard.cml",
        ],
        program: {
            // Binary containing tests
            binary: "bin/font_provider_test",
        },
        use: [
            ...
        ],
    }
    
  2. 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",
            },
        ],
    }
    
  3. Add the capability provider(s) as children of the test component, and route the capabilities from each provider.

    // my_component_test.cml (test component)
    {
        ...
    
        // Add capability providers
        children: [
            {
                name: "font_resolver",
                url: "#meta/mock_font_resolver.cm",
            },
        ],
        // Route capabilities to the test
        use: [
            {
                protocol: [ "fuchsia.pkg.FontResolver" ],
                from: "#font_resolver",
            },
        ],
    }
    
  4. Package the test component and capability provider(s) together into a single hermetic fuchsia_test_package():

    # Test component
    fuchsia_component("my_component_test") {
      testonly = true
      manifest = "meta/my_component_test.cml"
      deps = [ ":bin_test" ]
    }
    
    # Capability provider component
    fuchsia_component("mock_font_resolver") {
      testonly = true
      manifest = "meta/mock_font_resolver.cml"
      deps = [ ":mock_font_resolver_bin" ]
    }
    
    # Hermetic test package
    fuchsia_test_package("my_component_tests") {
      test_components = [ ":my_component_test" ]
      deps = [ ":mock_font_resolver" ]
    }
    

For more details on providing external capabilities to tests, see Integration testing topologies.

Injecting v1 components

If your test depends on injected services from v1 components that have not been migrated to Components v2 first, you need to use Realm Builder in your test to inject those components.

In the following example, suppose there's a single injected service, fuchsia.pkg.FontResolver, that has not been migrated to a v2 component:

// my_component_test.cmx
{
    "facets": {
        "fuchsia.test": {
            "injected-services": {
                "fuchsia.pkg.FontResolver":
                    "fuchsia-pkg://fuchsia.com/font_provider_test#meta/mock_font_resolver.cmx"
            }
        }
    },
    "program": {
        "binary": "bin/my_component_test"
    },
    "sandbox": {
        "services": [
            "fuchsia.pkg.FontResolver"
        ]
    }
}

To migrate this test to the Test Runner Framework, do the following:

  1. Create a CML file for the test component that includes the appropriate test runner and Realm Builder:

    // my_component_test.cml (test component)
    {
        include: [
            // Include support for Realm Builder
            "//src/lib/fuchsia-component-test/meta/fuchsia_component_test.shard.cml",
            // Select the appropriate test runner shard here:
            // rust, gtest, go, etc.
            "//src/sys/test_runners/rust/default.shard.cml",
        ],
        program: {
            binary: "bin/my_component_test",
        },
    }
    
  2. Use the RealmBuilder API inside of your test code to add the capability provider(s) as children of the test component, and route the capabilities from each provider:

    let mut builder = RealmBuilder::new().await?;
    
    builder
        .add_component("font_resolver",
            ComponentSource::LegacyUrl("#meta/mock_font_resolver.cmx".to_string()))
        .await?
        .add_route(CapabilityRoute {
            capability: Capability::protocol("fuchsia.pkg.FontResolver"),
            source: RouteEndpoint::component("font_resolver"),
            targets: vec![RouteEndpoint::AboveRoot],
        })?;
    
  3. Update your test code to connect to the protocol(s) exposed by the created realm instance:

    let realm_instance = builder.build().create().await?;
    
    // Connect to the exposed protocol
    let resolver_proxy = realm_instance.root
        .connect_to_protocol_at_exposed_dir::<FontResolverMarker>()?;
    

    If you are unable to modify your test to connect to the exposed protocol, you may be able to update your test to accept a sys::ServiceDirectory (C++), or ProtocolConnector (Rust) instead.

  4. Package the test component and capability provider(s) together into a single hermetic fuchsia_test_package():

    # Test component
    fuchsia_component("my_component_test") {
      testonly = true
      manifest = "meta/my_component_test.cml"
      deps = [ ":bin_test" ]
    }
    
    # Capability provider component
    fuchsia_component("mock_font_resolver") {
      testonly = true
      manifest = "meta/mock_font_resolver.cmx"
      deps = [ ":mock_font_resolver_bin" ]
    }
    
    # Hermetic test package
    fuchsia_test_package("my_component_tests") {
      test_components = [ ":my_component_test" ]
      deps = [ ":mock_font_resolver" ]
    }
    

There are some key differences between Components v1 and using Realm Builder for your tests:

  • The injected component will be a child of the realm constructed by Realm Builder and the service won't appear in the test component's namespace.
  • Each test case will use a separate instance of injected component and the topology of the components can be modified for each test case.

For more details and additional strategies for providing external capabilities to tests, see Integration testing topologies.

Verify the migrated 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 my_component_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

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 a core realm shard: Your component is not present in all products (eg. maybe it is present on workstation, but not terminal). Using a core realm shard allows the component to be safely excluded where it isn't available.
  • Add directly to core: Your component is present on all product and test build configurations. In this case you can add the component directly to core.cml.

Add a core realm shard

  1. 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 appmgr 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: [ "#appmgr" ],
            },
        ],
    }
    
  2. 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"
    }
    
  3. 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 directly 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

If you added your component to core.cml as explained here, then it's easy to infer your component 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

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

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

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

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

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" ],
            dependency: "weak_for_migration",
        },
        {
            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

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

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

If your component manifest contains additional system features that haven't been migrated at this point, see Other common situations 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

This section provides guidance on migrating the capabilities for diagnostics features to Components v2.

Inspect

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, ffx inspect, and more.

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: [ "inspect/client.shard.cml" ],
    ...
}

Component moniker for selectors

As explained previously, it's possible to infer the component moniker using ffx component list. Alternatively you can use ffx inspect list to see available components for querying inspect data. Your component moniker should appear in the ffx inspect list output up after adding the client.shard.cml above.

Inspect data in 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 an injected-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:

  1. When migrating tests, add the protocol capability to your test component:

    // my_component_test.cml (test component)
    {
        use: [
            {
                protocol: [ "fuchsia.diagnostics.ArchiveAccessor" ],
            },
        ]
    }
    
  2. Update your program to use the ArchiveReader library, which is available in C++, Rust, and Dart. See the Inspect Codelab for more details on using these libraries.

  3. Update any monikers used in your test's diagnostic selectors.

    • If you declare a child component in the test CML, check the Integration Testing docs to learn more about your moniker.
    • If you are using RealmBuilder, check the Realm Builder docs to learn more about your moniker.

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: [ "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

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: [ "syslog/elf_stdio.shard.cml" ],
    ...
}

Redirecting to debuglog

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.

  1. When migrating your component manifest, request the fuchsia.boot.WriteOnlyLog capability.

    // my_component.cml
    {
        use: [
            ...
            {
                protocol: [
                    "fuchsia.boot.WriteOnlyLog",
                ],
            },
        ],
    }
    
  2. 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" ],
            },
        ],
    }
    
  3. Direct stderr and stdout to debuglog in your program. You can use libraries for the initialization if your component is written in Rust or C++.

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 the fuchsia.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 to injected-services.

For other use cases, follow the instructions in this section to migrate to the v2 Hub provided by Component Manager.

Route the hub directory

When migrating tests, you'll need to route the hub directory capability to your test component if any 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:

//my_component_test.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/component-name/instance-id/url /hub/url
/hub/c/component-name/instance-id/in/svc-path /hub/exec/in/svc-path
/hub/c/component-name/instance-id/process-id /hub/exec/runtime/elf/process-id
/hub/c/child-component /hub/children/child-component

Other common situations

This section provides guidance on migrating components that use other common capabilities or features.

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 full-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 my-package

If the package is not listed with the base tag, follow the remaining instructions in this section.

When adding your component, assign the shared full-resolver-env as your component's environment.

// core.cml
{
  children: [
    ...
    {
      name: "my_component",
      url: "fuchsia-pkg://fuchsia.com/my-pkg#meta/my_component.cm",
      environment: "#full-resolver-env",
    },
  ],
}

Start on boot

If your component appears in a sysmgr config startup_services or apps block you should make your component an eager component in its parent's manifest.

// parent.cml
{
    children: [
        ...
        {
            name: "font_provider",
            url: "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cm",
            startup: "eager",
        },
    ],
}

Additionally you need to ensure that all your component's ancestors up to /core are eager components. If your component is present on all products that derive from the core you can add it to core directly, otherwise you need to use core realm variability to allow products without your component to continue to boot.

The eager component should be in the base package set; eager is generally incompatible with being outside the base package set.

For more details on how eager impacts component startup see, lifecycle and component manifests.

critical_components

critical_components is a sysmgr feature that allows a component to mark itself as critical to system operation:

{
  ...
  "critical_components": [
    "fuchsia-pkg://fuchsia.com/system-update-checker#meta/system-update-checker.cmx"
  ]
}

The equivalent feature in Components v2 is called "reboot-on-terminate". If your component appears in critical_components you should mark it as on_terminate: reboot in the parent component's manifest:

// core.cml
{
    children: [
        ...
        {
            name: "system-update-checker",
            url: "fuchsia-pkg://fuchsia.com/system-update-checker#meta/system-update-checker.cm",
            startup: "eager",
            on_terminate: "reboot",
        },
    ],
}

Also, you'll need to add the component's moniker to component manager's security policy allowlist at //src/security/policy/component_manager_policy.json5:

// //src/security/policy/component_manager_policy.json5
{
    security_policy: {
        ...
        child_policy: {
            reboot_on_terminate: [
                ...
                "/core/system-update-checker",
            ],
        },
    },
}

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.

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.

Lifecycle

If your component serves the fuchsia.process.lifecycle.Lifecycle protocol, follow these instructions to migrate to the lifecycle handle provided by the ELF runner in Components v2.

  1. 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"}});
    
  2. When migrating your component manifest, add the lifecycle stop event:

    // my_component.cml
    {
        include: [
            "syslog/client.shard.cml",
        ],
        program: {
            runner: "elf",
            binary: "bin/my_binary",
            lifecycle: { stop_event: "notify" },
        },
        ...
    }
    
  3. Get the fuchsia.process.lifecycle.Lifecycle channel provided by the ELF runner:

    Rust

    use fuchsia_runtime::{take_startup_handle, HandleInfo, HandleType};
    let lifecycle_channel = take_startup_handle(HandleInfo::new(HandleType::Lifecycle, 0));
    // Serve the Lifecycle protocol on `lifecycle_channel`
    

    A more complete sample is available in the examples.

    C++

    #include <lib/zx/channel.h>
    #include <zircon/processargs.h>
    zx::channel lifecyecle_channel = zx_take_startup_handle(PA_LIFECYCLE);
    // Serve the Lifecycle protocol on `lifecycle_channel`
    

    You can refer to fshost code for a sample implementation.

More information about the Lifecycle protocol is available in the ELF runner documentation.

Converting 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

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: "data",
            path: "/data",
        },
    ],
}

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: [
        ...
        {
            storage: "data",
            from: "self",
            to: [ "#my_component" ],
        },
    ]
}

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",
        },
    ],
}

Storage capabilities in tests

When migrating tests, you will need to route storage access to your test component if any of the 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 component:

// my_component_test.cml (test component)
}
    use: [
        ...
        {
            storage: "data",
            path: "/data",
        },
    ],
}

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: "root-ssl-certificates",
            rights: [ "r*" ],
            path: "/config/ssl",
        },
    ],
}

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: [
        ...
        {
            directory: "root-ssl-certificates",
            from: "parent",
            to: [ "#my_component" ],
        },
    ],
}

Directory paths in tests

When migrating tests, you need to route the directory capabilities to your test component if any of the 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 component:

// my_component_test.cml (test component)
{
    use: [
        ...
        {
            directory: "root-ssl-certificates",
            rights: [ "r*" ],
            path: "/config/ssl",
        },
    ],
}

Configuration 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.

For more details using data files, see product-specific configuration with config_data().

Consider packaging your data files hermetically with resource() if your component doesn't need to accept data files from arbitrary parts of the source tree. Using resource() is simpler and more efficient.

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: [
        ...
        {
            directory: "config-data",
            from: "parent",
            to: [ "#my_component" ],
            subdir: "my-package",
        },
    ],
}

Configuration data in tests

When migrating tests, you need to route the directory capability with the appropriate subdirectory to your test component if any of the 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 component:

// my_component_test.cml (test component)
{
    use: [
        ...
        {
            directory: "config-data",
            rights: [ "r*" ],
            path: "/config/data",
            subdir: "my-package",
        },
    ],
}

Device directories

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": [
            "class/input-report"
        ]
    }
}

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: "dev-input-report",
            rights: [ "r*" ],
            path: "/dev/class/input-report",
        },
    ],
}

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: [
        ...
        {
            directory: "dev",
            from: "parent",
            as: "dev-input-report",
            to: [ "#my_component" ],
            subdir: "class/input-report",
        },
    ],
}

Device directories in tests

When migrating tests, you need to route the directory capabilities to your test component if any of the 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 component:

// my_component_test.cml (test component)
{
    use: [
        ...
        {
            directory: "dev-input-report",
            rights: [ "r*" ],
            path: "/dev/class/input-report",
        },
    ],
}

Event capabilities

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.

Event sources in tests

When migrating tests, you'll need to inject any components you wish to observe into the test realm and route the appropriate lifecycle events for those components to your test component.

Following the example in Test uses injected services, route the fuchsia.sys2.EventSource capability and the appropriate events to your test component:

// my_component_test.cml (test component)
{
    children: [
        {
            name: "my_component",
            url: "fuchsia-pkg://fuchsia.com/my-package#meta/my_component.cm",
        },
    ],
    use: [
        {
            protocol: [ "fuchsia.sys2.EventSource" ],
        },
        {
            event: [ "started" ],
            from: "framework",
            modes: [ "async" ],
        },
    ],
}

Build Info

When migrating the build-info feature, instead use the fuchsia.buildinfo.Provider protocol. This protocol is the only supported method of retrieving build information moving forward. To use this protocol, add it while declaring required services.

Vulkan

When migrating the vulkan feature or code that uses a //src/lib/vulkan/*.shard.cmx shard, instead use the vulkan/client.shard.cml shard as described in the Vulkan documentation.