Honoring Asian Pacific American Heritage Month. See how.

Structured Configuration

Structured configuration allows C++/Rust components to declare configuration schemas directly in their manifest. Benefits of using structured configuration include:

  • Errors in configuration are detected at build and assembly time.
  • Multiple packages can be created using the same component and different configuration values.
  • Components read their configuration with statically-typed libraries.
  • Component Framework only starts components with valid configuration.
  • Configuration can be viewed at runtime with ffx tooling.
  • Values can be set at runtime in tests with RealmBuilder.

To use structured configuration in your component, you must update build rules, declare a schema, define values, and generate a client library.

Update build rules

To prevent cyclic dependencies when generating client libraries, define a fuchsia_component_manifest rule that compiles the component manifest. Pass this compiled manifest GN label into the fuchsia_component rule.

fuchsia_component_manifest("manifest") {
  component_name = "config_example"
  manifest = "meta/config_example.cml"
}

fuchsia_component("component") {
  cm_label = ":manifest"
  deps = [ ":bin" ]
}

Declare configuration schema

You must declare a configuration schema in a component's manifest. Structured config supports booleans, integers, strings and vectors of these types. The CML reference doc describes the complete syntax for a config schema.

{
    ...
    config: {
        greeting: {
            type: "string",
            max_size: 512,
        },
    },

}

Define configuration values

You must define configuration values for a component's schema. The fuchsia_structured_config_values GN template validates the defined values against the config schema and compiles them into a .cvf file that must be packaged with your component.

There are two ways to define config values: in a JSON5 file or inline in GN.

JSON5 file

You can write a component's configuration values in a JSON5 file. Because JSON5 is a strict superset of JSON, existing JSON configuration files can also be reused for structured config.

Each key in the JSON object must correspond to a config key in the schema and the value must be of a compatible JSON type:

{
    // Print "Hello, World!" by default.
    greeting: "World",
}

Provide the path to the JSON5 file in a fuchsia_structured_config_values rule.

fuchsia_structured_config_values("values_from_json_file") {
  cm_label = ":manifest"
  values_source = "../config_example_default_values.json5"
}

Inline values

The fuchsia_structured_config_values template also supports defining configuration values inline:

C++

declare_args() {
  # Set this in args.gn to override the greeting emitted by this example.
  config_example_cpp_greeting = "World"
}

fuchsia_structured_config_values("values_from_gn") {
  cm_label = ":manifest"
  values = {
    greeting = config_example_cpp_greeting
  }
}

Rust

declare_args() {
  # Set this in args.gn to override the greeting emitted by this example.
  config_example_rust_greeting = "World"
}

fuchsia_structured_config_values("values_from_gn") {
  cm_label = ":manifest"
  values = {
    greeting = config_example_rust_greeting
  }
}

By using declare_args, you can change configuration values on the command line at build time:

C++

$ fx set core.qemu-x64 \
  --with //examples/components/config \
  --args='config_example_cpp_greeting="C++ CLI Override"'

Rust

$ fx set core.qemu-x64 \
  --with //examples/components/config \
  --args='config_example_rust_greeting="Rust CLI Override"'

Package the component and values

To package a component and a set of values together, add the fuchsia_component and fuchsia_structured_config_values rules as dependencies of a fuchsia_package.

C++

fuchsia_package("cpp_config_example") {
  deps = [
    ":component",
    ":values_from_gn",
  ]
}

Rust

fuchsia_package("rust_config_example") {
  deps = [
    ":component",
    ":values_from_gn",
  ]
}

The build system verifies your component's configuration schema and value file. A component with a faulty configuration (for example: field mismatch, bad constraints, missing value file) will fail to build.

Checking the configuration

Component manager validates a component's configuration when the component is resolved.

Use ffx component show to print out a components configuration key-value pairs. The component does not have to be running for this to work.

$ ffx component show config_example
                Moniker: /core/ffx-laboratory:config_example
        Component State: Resolved
                      ...
          Configuration: greeting -> "World"
                      ...

Reading the configuration

Components read their resolved configuration values with a generated library. Generate a library using the following build templates:

C++

executable("bin") {
  # ...
  deps = [
    ":example_config",

    # ...
  ]
}

fuchsia_structured_config_cpp_elf_lib("example_config") {
  cm_label = ":manifest"
}

Rust

rustc_binary("bin") {
  # ...
  deps = [
    ":example_config",

    # ...
  ]
}

fuchsia_structured_config_rust_lib("example_config") {
  cm_label = ":manifest"
}

Use the following functions from the library to read configuration values:

C++

// Import the header as if it's located in the same directory as BUILD.gn:
#include "examples/components/config/cpp/example_config.h"

...

// Retrieve configuration
auto c = example_config::Config::TakeFromStartupHandle();

Rust

use example_config::Config;

...

// Retrieve configuration
let config = Config::take_from_startup_handle();

Export configuration to Inspect

You can export a components configuration to Inspect so that it is available in crash reports. The client libraries have functions to export a component's configuration to an Inspect tree:

C++

// Record configuration to inspect
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
sys::ComponentInspector inspector(context.get());
inspect::Node config_node = inspector.root().CreateChild("config");
c.RecordInspect(&config_node);

Rust

// Record configuration to inspect
let inspector = fuchsia_inspect::component::inspector();
config.record_inspect(inspector.root());

Use ffx inspect show to print out the component's exported configuration:

$ ffx inspect show core/ffx-laboratory\*config_example
core/ffx-laboratory\:config_example:
  ...
  payload:
    root:
      config:
        greeting = World

Testing with Realm Builder

You can use Realm Builder to dynamically replace the configuration values of a component.

C++

realm_builder.ReplaceConfigValue(name, "greeting", "Fuchsia");

Rust

builder
    .replace_config_value_string(&config_component, "greeting", "Fuchsia")
    .await
    .unwrap();

Realm Builder validates the replaced value against the component's configuration schema.