The Fuchsia build system uses a number of "manifest" files that indicate how to install files into containers, for instance copying them into Fuchsia package archives, or into the boot filesystem image of a ZBI file .
This page documents these file formats which are used within the build, but should not be exposed outside of it.
Note that the scripts and .gni
files that generate and process these files
are located under //build/dist/
in the Fuchsia source tree.
FINI (Fuchsia INI) manifest file format
The FINI manifest format is used by several tools invoked during the build: the
ffx package build
command (or the legacy pm
tool) that
generates Fuchsia package archives or the zbi
tool which generates
ZBI images.
The syntax is very simple: each line of text looks like <destination>=<source>
,
where <destination>
is a destination file path, relative to the top of the
final container, and <source>
is a file path to the source content, relative
to the current directory (i.e. the build directory in most cases).
Note that the destination path should never begin with a directory separator, and that FINI files do not support comments, sections, and other features of the Windows INI file format
For example:
bin/foo=foo
lib/ld.so.1=user.libc_x64/libc.so
meta/foo.cm=obj/src/foo/cml/foo_component/foo.cm
meta/package=gen/src/foo/foo_meta_package.txt
Distribution manifest file format
This is a JSON file that must contain a list, where each item is a JSON object as a single "distribution" entry, which describes how to install one source file into a container, as well as provide a GN label for the target that generated it. The schema used here is:
source
: A source path string (REQUIRED).destination
: A destination path string (REQUIRED).label
: A GN label pointing to the target that generated the source file. Only used for debugging (OPTIONAL).
Example:
{
"destination": "bin/foo",
"source": "x64-asan/foo",
"label": "//some/dir:foo"
}
While conceptually similar to FINI manifests, distribution manifests provide a little bit more information (the GN label), which is highly useful for debugging when things go wrong. They are also consumed by a different set of tools.
Partial distribution manifest file format
FINI and distribution manifests are generated by processing intermediate files called "partial distribution manifests".
A partial manifest is generated by GN metadata collection over targets in the same dependency tree. It is a JSON file that contains a list of object "entries".
Several types of entries are used to describe various aspects of the build and installation requirements. They are all merged into either a FINI or distribution manifest.
The Python module at //build/dist/distribution_manifest.py
contains
the functions used to process these files.
Regular entries
These entries match the schema used for distribution manifests. They correspond to a simple source file to install to a given destination path.
source
: A source path string (REQUIRED).destination
: A destination path string (REQUIRED).label
: A GN label pointing to the target that generated the source file. Only used for debugging (OPTIONAL).elf_runtime_dir
: Only used for ELF executables and loadable modules, an optional destination directory path that specifies where this binary's ELF dependencies should be at runtime. Default value is "lib"
When used to generate a distribution manifest, they are copied as-is,
without the elf_runtime_dir
key into the output file, unless one
of the following conditions are met:
they have the same source as another regular entry (see this section for details), in which case only one of the entries is preserved.
its source path is used by a renamed entry (see below).
Example:
{
"destination": "bin/foo",
"source": "x64-asan/foo",
"label": "//some/dir:foo",
"elf_runtime_dir": "lib/asan"
},
Copy entries.
These entries are used to indicate that the build copied a specific file to a new output location. This information is later used to process renamed entries properly (see below), and are otherwise ignored. Their schema is:
copy_from
: A source path string, relative to the build directory (REQUIRED).copy_to
: A destination path string, relative to the build directory (REQUIRED).label
: A GN label pointing to the target that generated this entry (OPTIONAL).
For example, the following entry is used to indicate that the 'foo'
executable binary, built with the "asan" build variant in
${BUILD_DIR}/x64-asan/foo
was also copied to ${BUILD_DIR}/foo
.
Example:
{
"copy_from": "x64-asan/foo",
"copy_to": "foo"
"label": "//some/dir:foo"
}
See the section below that explains how copy and renamed entries interact, with an actual example.
Renamed entries
These entries are used to indicate that a given regular entry should be
installed at an alternative destination location. This is useful for certain
programs (e.g. busybox
) which will behave differently, depending on the
program name used to launch them. The renamed_binary()
GN template relies on
them. Their schema is:
destination
: A destination path string (REQUIRED).renamed_source
: A source path that matches another regular entry (REQUIRED).label
: A GB label pointing to the target that generated this entry (OPTIONAL).keep_original
: A boolean flag, set to true to indicate that the original renamed binary should still be installed in the container (OPTIONAL).
Note that a renaming entry can only point to the source
path of a regular
entry, or to the copy_to
path of a copy entry. Using an unknown path, or the
destination path of another renaming entry is a build error.
It is possible to rename the same entry multiple times. This is useful when the same original binary needs to be installed several times under different names.
Here is an example where the busybox
binary is installed as bin/cp
,
bin/cat
and bin/ls
in the same container. Note that bin/busybox
will
not be installed to the container though.
{
"destination": "bin/busybox",
"source": "busybox",
"label": "//third_party/busybox:busybox"
},
{
"destination": "bin/cp",
"renamed_from": "busybox"
},
{
"destination": "bin/cat",
"renamed_from": "busybox"
},
{
"destination": "bin/ls",
"renamed_from": "busybox"
}
If any renaming entry sets the keep_original
flag to true, the original entry
is not removed from the manifest, keeping the original binary in it, as in:
{
"destination": "bin/busybox",
"source": "busybox",
"label": "//third_party/busybox:busybox"
},
{
"destination": "bin/cp",
"renamed_from": "busybox",
"keep_original": true
}
Which will ensure that both bin/busybox
and bin/cp
are installed into the
container.
File entries
These entries are used to include other distribution manifest files, which may be generated by a different target. Their schema is:
file
: A file path string, pointing to another distribution manifest file (REQUIRED).label
: A default GN label that will be applied to all entries in the included file, if they don't have their ownlabel
value (OPTIONAL).
Distribution file includes can be recursive.
Example:
{
"file": "path/to/other.dist_manifest",
"label": "//some/dir:label"
}
Duplicate entries in manifest files
Due to the way the Fuchsia build works, it is possible for manifest files (of any
format) to provide multiple entries that use the same <destination>
path. This is
however valid as long as the source paths, or their content, are identical; in which
case duplicates will simply be ignored.
The case where multiple entries have the same destination path, but different source path and content, is treated as a build error by the processing scripts.
Interaction between copy and renamed entries
To clarify how copy and rename entries interact, consider the example of a given
renamed_binary()
target used to rename the install location of a foo
binary.
When no build variants are selected, the corresponding build manifest will contain
two entries that look like this:
{
"destination": "bin/foo",
"source": "foo",
"label": "//src:foo",
},
{
"destination": "bin/foo_renamed",
"renamed_from": "foo",
},
The first regular entry tells that ${BUILD_DIR}/foo
was built by the
//src:foo
target in the default toolchain, and should be installed to
bin/foo
inside containers.
The second copy entry tells that the binary built as ${BUILD_DIR}/foo
should
really be instealled to bin/foo_renamed
instead of its default location
(i.e. bin/foo
).
Logically speaking, this is equivalent to a single entry:
{
"destination": "bin/foo_renamed",
"source": "foo",
"label": "//src/foo",
},
I.e. the first entry, where only the destination path was changed.
However, when building the same thing with the asan
variant is enabled,
the manifest content will be slightly different than before:
{
"destination": "bin/foo",
"source": "x64-asan/foo",
"label": "//src:foo(//build/toolchain:x64-asan)",
},
{
"copy_from": "x64-asan/foo",
"copy_to": "foo",
},
{
"destination": "bin/foo_renamed",
"renamed_from": "foo",
},
Now, the first entry tells that ${BUILD_DIR}/x64-asan/foo
was built
by the //src:foo
target in the asan toolchain (//build/toolchain:x64-asan
),
and still be installed by default to bin/foo
.
The second entry tells that ${BUILD_DIR}/foo
is an actual copy of
${BUILD_DIR}/x64-asan/foo
, because that's what our build system does.
The third entry is the same as before, because the renamed_binary()
target
doesn't know anything about which toolchain or variant was used to build
${BUILD_DIR}/foo
.
This is all logically equivalent to a single entry that looks like:
{
"destination": "bin/foo_renamed",
"source": "x64-shared/foo",
"label": "//src/foo(//build/toolchain:x64-asan)",
},
Copy entries are only used when build variants are enabled, they link renamed entries with the variant-specific regular entries. The reason all this information is spread out is that each entry is generated through a different target in our build graph, and that there is no way to link everything together during the GN generation pass.