Codelab: Defining and building a product bundle

This codelab walks through the process of defining and building a new product bundle in Fuchsia using the Bazel build system. A product bundle is the distributable artifact that contains all the images and metadata needed to flash, update, or emulate a Fuchsia product.

As part of this process, you will also define a product configuration to specify the software features, settings, and packages that make up the product.

Prerequisites

What is a product configuration?

A product configuration in Fuchsia defines the user experience for a specific product. It is distinct from a board configuration, which specifies hardware-specific details. A product configuration typically includes:

  • Platform settings: Settings for the underlying Fuchsia operating system (e.g., build type, enabled system features).
  • Product settings: Settings specific to the product experience (e.g., session shell, specific packages).

Product configurations are defined using the fuchsia_product_configuration rule in BUILD.bazel files.

Writing a product configuration

Let's explore how to define a product configuration and use it to build a system image.

Basic setup

A product configuration is primarily a JSON object (represented as a dictionary in Starlark) passed to the fuchsia_product_configuration rule.

# //products/my_product/BUILD.bazel
load(
    "@rules_fuchsia//fuchsia:assembly.bzl",
    "fuchsia_product_configuration",
)

fuchsia_product_configuration(
    name = "product_config",
    product_config_json = {
        "platform": {
            "build_type": "eng",
            "feature_set_level": "standard",
        },
        "product": {},
    },
)

Platform settings

The platform key in the configuration dictionary controls various aspects of the Fuchsia system. Common fields include:

  • build_type: Controls the security and optimization level.
    • eng: Engineering build. This build is best for debugging as it has assertions enabled and optimizations disabled.
    • userdebug: This build is optimized like the user build, but with some debug features and tools enabled.
    • user: Production build. This build is fully optimized and secure.
  • feature_set_level: Defines the baseline set of features included in the system.
    • embeddable: Minimal subset of bootstrap. This is optimized for memory constrained environments and does not self-update.
    • bootstrap: Bootable, serial-only. Only the /bootstrap realm. No netstack or storage drivers. Primarily used for board-level bringup.
    • utility: Smallest configuration with the /core realm. Best for utility-type systems like recovery.
    • standard: A "full Fuchsia" configuration. Includes netstack and self-update capabilities. This is the default.
  • storage: Configures the filesystem (e.g., Fxfs) and storage layout.

For a full list of platform settings, see the PlatformSettings documentation.

For example, a Fuchsia product configuration with platform settings may look like the following:

fuchsia_product_configuration(
    name = "product_config",
    product_config_json = {

        "platform": {
            "build_type": "eng",
            "feature_set_level": "standard",
            "storage": {
                "filesystems": {
                    "volume": "fxfs",
                },
            },
        },
        "product": {},

    },
)

Product settings

The product key is used for higher-level product settings.

One of the most important settings is the session. A session is the top-level component that defines the product's user experience (e.g., the graphical shell or main application).

To use a session, you must specify its URL and ensure the package containing the component is included in the base packages.

Base packages are the set of packages that are included in the system image. They are available immediately at boot, are immutable (read-only), and are updated as part of the system OTA.

For example, a Fuchsia product configuration with product settings may look like the following:

Example:

fuchsia_product_configuration(
    name = "product_config",
    product_config_json = {
        "platform": {
            "build_type": "eng",
            "feature_set_level": "standard",
        },

        "product": {
            "session": {
                "url": "fuchsia-pkg://fuchsia.com/my_session#meta/my_session.cm",
            },
        },
    },
    base_packages = [
        "//src/my_session:my_session_package",
    ],

)

For a full list of product settings, see the ProductSettings documentation.

Assembling the product image

Once you have a fuchsia_product_configuration, you can use the fuchsia_product rule to assemble the final system image. This rule combines your product configuration with a specific board configuration.

load(
    "@rules_fuchsia//fuchsia:assembly.bzl",
    "fuchsia_product",
)

fuchsia_product(
    name = "image.x64",
    board_config = "//boards:x64",
    platform_artifacts = "//build/bazel/assembly/assembly_input_bundles:platform_eng",

    product_config = ":product_config",

)
  • board_config: Points to the board configuration target.
  • platform_artifacts: Points to the bundle of platform artifacts (AIBs) to use.

Creating a product bundle

To run your product on a device or emulator, you need to package it into a Product Bundle using the fuchsia_product_bundle rule.

load(
    "@rules_fuchsia//fuchsia:assembly.bzl",
    "fuchsia_product_bundle",
)

fuchsia_product_bundle(
    name = "my_product.x64",
    product_bundle_name = "my_product.x64",

    main = ":image.x64",

)

This target produces the artifacts needed for ffx product-bundle commands.

For example:

  • Run in emulator:

    ffx emu start my_product.x64
  • Flash to device:

    ffx target flash -b my_product.x64

Registering with the build system

To build your product bundle, it must be registered with the GN build system. This involves the following steps:

Bridging to GN

Fuchsia's build system uses GN, but your product bundle is defined in Bazel. You need to use the bazel_product_bundle GN template to create a bridge.

Create a BUILD.gn file in your product directory (e.g., //products/my_product/BUILD.gn):

# //products/my_product/BUILD.gn
import("//build/bazel/assembly/bazel_product_bundle.gni")

bazel_product_bundle("my_product.x64") {
  testonly = true
  product_bundle_name = "my_product.x64"
  bazel_product_bundle_target = ":my_product.x64"
  bazel_product_image_target = ":image.x64"
  bazel_inputs_from_gn = [
    "//boards/x64:x64.bazel_input",
    "//build/images/flash:esp.bazel_input",
  ]
  allow_eng_platform_bundle_use = true
}

Adding to allowlists

You may need to add your new product to the bazel_action_allowlist in //build/bazel/BUILD.gn to avoid visibility errors:

# //build/bazel/BUILD.gn
group("bazel_action_allowlist") {
  visibility = [
    # ...
    "//products/my_product:*",
  ]
  # ...
}

You may also need to add it to the non_hermetic_deps visibility list in //build/BUILD.gn:

# //build/BUILD.gn
group("non_hermetic_deps") {
  visibility = [
    # ...
    "//products/my_product:*",
  ]
  # ...
}

This GN target (:my_product.x64) now represents your Bazel product bundle in the GN build graph.

Adding to available products

Finally, add this new GN target to the global list of product bundles in //products/BUILD.gn to make it discoverable by fx:

# //products/BUILD.gn
group("product_bundles") {
  testonly = true
  deps = [
    # ... other products
    "//products/my_product:my_product.x64",
  ]
}

Building the product

Fuchsia supports multi-product builds, meaning you can have multiple products available in the same build directory.

Configuring the build

Configure your build environment using fx set. You should use a product configuration that enables the Bazel build system, such as fuchsia.x64:

fx set fuchsia.x64

Setting the main product

To work with your specific product, set it as the "main" product bundle. This configures fx tools to target your product by default:

fx set-main-pb my_product.x64

Building the product

To build your currently selected main product (and its dependencies):

fx build

If you want to explicitly build a specific product bundle regardless of the main setting:

fx build my_product.x64

Switching products

You can switch the active "main" product bundle at any time without re-running fx set by running fx set-main-pb again:

fx set-main-pb minimal.x64

GN Arguments vs Assembly

In a multi-product build environment, all products share the same GN arguments (defined by fx set). Therefore, you cannot use fx set ... --args to configure product-specific features. Instead, all product configuration must be done via the fuchsia_product_configuration Bazel rule as demonstrated in this codelab.

Next steps

This codelab covered the basics of defining and building a new product bundle.

To continue learning, you can: