blob: d619621d5259f3dfb4dbfe19a119ac9ed799cdb9 [file] [log] [blame] [view]
# Provide data files to components
## Goal
In the guide on [building components][build-components] we saw several examples
of defining components for executables and then packaging them. In this document
we review ways of making data files available to components in the same package.
## Hermetic data files with `resource()`
At runtime, components are able to read the contents of their own package by
accessing the path `/pkg/` in their namespace. The `resource()` template may be
used to add contents to the package that may be accessed this way.
{# note: the verbatim tags below are to avoid issues with the fuchsia.dev template engine #}
```gn
import("//build/dist/resource.gni")
{% verbatim %}
# List of greetings
resource("greetings") {
sources = [ "greetings.txt" ]
outputs = [ "data/{{source_file_part}}" ]
}{% endverbatim %}
```
See the [resource][resource] template for more usage instructions.
For information about `{% verbatim %}{{source_file_part}}{% endverbatim %}`
and similar syntax see [GN placeholders][gn-placeholders]{:.external}.
Using `resource()` is also demonstrated in [additional packaged resources][additional-packaged-resources].
### Including resources with a component
Add a dependency on the resource target from a component in order to ensure that
the resource(s) are included in the same package.
```gn
import("//build/components.gni")
# Sends a random greeting to a client
executable("greeter") {
sources = [ "greeter.cc" ]
deps = [ ... ]
}
fuchsia_component("greeting_server") {
deps = [
":greeter",
":greetings",
]
manifest = "meta/greeting_server.cml"
}
```
In the example above, at runtime the component will be able to read the file
in its namespace at the path `/pkg/data/greetings.txt`. This will work regardless
of what package(s) (defined with `fuchsia_package()`) this component is
included in.
### Packaging conventions
* Small data files (less than 4kb) should be packaged under `meta/`, though
this is not required. Packaging small files under `meta/` allows the
packaging system to archive them in a single [`meta.far` file][metafar],
which is a more efficient way to store small files.
* Otherwise, data files are usually packaged under `data/` by convention,
though again this is not a technical requirement.
### Using different resources in different packages
Sometimes it's desirable to package the same component with different data
files.
```gn
import("//build/dist/resource.gni")
import("//build/components.gni")
# Sends a random greeting to a client
executable("greeter") {
sources = [ "greeter.cc" ]
deps = [ ... ]
}
fuchsia_component("greeting_server") {
deps = [ ":greeter" ]
manifest = "meta/greeting_server.cml"
}
# List of production greetings.
# Contains only the finest greetings and the best regards.
resource("greetings") {
sources = [ "greetings.txt" ]
outputs = [ "data/greetings.txt" ]
}
fuchsia_package("greeting") {
deps = [
":greeting_server",
":greetings",
]
}
# Greetings for testing.
# Contains exactly one greeting so that tests are reproducible.
resource("test_greeting") {
testonly = true
sources = [ "test_greeting.txt" ]
outputs = [ "data/greetings.txt" ]
}
# Connects to the greeting server.
# Ensures that the expected greeting is sent back.
fuchsia_test_component("greeting_test_client") {
...
}
fuchsia_test_package("greeting_integration_test") {
test_components = [ ":greeting_test_client" ]
deps = [
":greeting_server",
":test_greeting",
]
}
```
In the example above, the same `greeting_server` component is added to two
packages, one for production and another for testing. In both cases the
component will find a file under `/pkg/data/greetings.txt`. However the
contents of this file will vary between the production version and the testing
version, depending on the package association.
## Product-specific configuration with `config_data()`
Sometimes a component is defined in one repository but its data is defined in
another repository. For instance `fuchsia.git` defines a font provider service,
but the `workstation_eng` product configuration (defined in a different repository)
defines which fonts are available to the font provider.
The `config_data()` template allows developers to make files available to
components in a different package without having to directly change the contents
of that package.
{# note: the verbatim tags below are to avoid issues with the fuchsia.dev template engine #}
```gn
import("//build/config.gni")
{% verbatim %}
config_data("workstation_fonts") {
for_pkg = "fonts"
sources = [
"RobotoMono-Light.ttf",
"RobotoMono-Medium.ttf",
"RobotoMono-Regular.ttf",
]
outputs = [ "fonts/{{source_file_part}}" ]
}{% endverbatim %}
```
### Using `config_data()` in your component
Note: If you are using [legacy components][legacy-components],
see [configuration data][config-migration] in the components migration guide.
Include the following in your component manifest (`.cml`) file:
```json5
{
use: [
{
directory: "config-data",
rights: [ "r*" ],
path: "/config/data",
},
],
}
```
At runtime your component will be able to read the config files at the path
`/config/data`.
For the above to work, `"config-data"` must be offered to your component.
For instance your parent may have a declaration that looks like this:
```json5
{
children: [
{
name: "font-provider",
url: "fuchsia-pkg://fuchsia.com/fonts#meta/font-provider.cm",
},
],
offer: [
{
directory: "config-data",
from: "parent",
to: [ "#font-provider" ],
subdir: "fonts",
},
],
}
```
Note that both `for_pkg = ...` and `subdir: ...` above are coordinated in that
they set the same value `"fonts"`.
### Testing `config_data()`
A component under test in a test realm can have a `"config-data"` directory
routed to it in much the same way as a production component would.
If you would like to offer a component under test different configuration data,
simply use the appropriate value for `for_pkg` and `subdir` that would route
your test data to your test component.
### How `config_data()` works
All `config_data()` targets that are defined in your build configuration collect
their files into a single package called `config-data`. This package is defined
in the system assembly as part of the base package set. The contents of this
package replicate the parameters in `config_data()` definitions, so that they
can be routed as subdirectories to components that expect them.
### Known issues
* The `config-data` package that collects all files from `config_data()`
definitions is part of the base set of packages. As a result its contents do
not update in the `fx serve` developer workflow. To update data files you
must repave or OTA your device, or if using an emulator rebuild the system
image and restart the emulator.
* Defining `config_data()` also requires making changes to component manifest
files as shown above. Some of the strings used are repeated in multiple
places, which is error-prone. When mistakes are made they can be difficult
to troubleshoot.
* `config_data()` target definitions know about the name of the package(s)
of components that are expected to use this data. This promotes brittle
contracts that are difficult and perilous to evolve. For instance in order
for the platform to offer [ICU data][icu-data] to out-of-tree components and
their tests, there exists a
[hard-coded list of out-of-tree package names][icu-data-configs] in the
Fuchsia source tree.
Due to the above, always prefer using `resource()` if possible.
[additional-packaged-resources]: /docs/development/components/build.md#additional-packaged-resources
[build-components]: /docs/development/components/build.md
[config-migration]: /docs/development/components/v2/migration/features.md#config-data
[gn-placeholders]: https://gn.googlesource.com/gn/+/HEAD/docs/reference.md#placeholders
[icu-data]: /docs/development/internationalization/icu_data.md
[icu-data-configs]: /src/lib/icu/tzdata/BUILD.gn
[legacy-components]: /docs/concepts/components/v1/README.md
[metafar]: /docs/concepts/packages/package.md#metafar
[resource]: /build/dist/resource.gni