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.