Manifest File Formats Used by The Fuchsia Build System

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 own label 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.