4. Step 4 — Get built and tested on the target platforms#

What it unlocks

Target-platform builds & tests — Your module is actually compiled and tested by the pipelines — built for and deployed to every target platform (Linux x86_64, QNX, AutoSD, EBcLfSA aarch64), offered in the interactive CLI, and exercised by CI. This is the rung that turns a library into something that runs inside the integration images.

The integration provides build definitions for four target images, each built and tested by its own gating CI job — it does not distribute ready-to-flash images; you build them yourself from these definitions. The table lists the platforms, the image target, the runner used to execute it, and the per-platform CI check.

Table 4.1 Supported target platforms#

Platform

Arch

Image

Runner

Build & test workflow

Linux

x86_64

images/linux_x86_64

Docker (runners/docker_x86_64)

build_and_test_linux.yml

QNX 8

x86_64

images/qnx_x86_64

QEMU (runners/qemu_x86_64)

build_and_test_qnx.yml

Red Hat AutoSD

x86_64

images/autosd

Docker (runners/docker_x86_64)

build_and_test_autosd.yml

EB corbos Linux (Safety Apps)

aarch64

images/ebclfsa_aarch64

QEMU (runners/qemu_aarch64)

build_and_test_ebclfsa.yml

Note

The EB corbos Safety Applications image is special: it runs a high-integrity application demo under additional constraints. Read images/ebclfsa_aarch64/README.md before targeting it.

4.1. Run any image locally in QEMU#

Every image exposes a :run target that boots it in an emulator, so you can try your module on the real target without hardware:

bazel run //images/linux_x86_64:run
bazel run //images/qnx_x86_64:run
bazel run //images/autosd:run
bazel run //images/ebclfsa_aarch64:run

The QNX and aarch64 images boot under QEMU, which needs a one-time host setup (TUN device, bridge helper, libvirt network). Follow the QEMU runner how-to in runners/qemu_x86_64/README.md before the first run.

4.2. Two ways to get your module onto the images#

There are two ways to make your binary part of the images:

  • As a showcase (recommended). Bundle your binary once into the aggregate //showcases package; it is then deployed into every image automatically — one change covers all four platforms, and the binary is also offered in the interactive CLI. See Add a runtime showcase below.

  • Directly into each image. Wire the binary into each image’s build description by hand. This is per-image work that you repeat four times (once per image) and is only needed for components that are not run as a CLI showcase — typically a daemon. The datarouter is integrated this way; see how the QNX image does it in images/qnx_x86_64/build/BUILD (the datarouter source and the DATAROUTER_PATH mapping). See Deploy a component as part of the platform below. Prefer the showcase route unless you have a reason not to.

4.3. Add a runtime showcase#

A showcase is the vehicle that gets your module onto the platforms: you bundle a runnable binary, and the aggregate //showcases package is deployed into every image automatically. You do this once — there is no per-platform work.

There are two patterns under showcases/:

  • Standalone (showcases/standalone) — bundle a binary that already exists in your module’s repo.

  • Composed — a small binary defined here that combines several modules.

1. Define the bundle. Add a score_pkg_bundle target. To re-use a binary that lives in your module:

load("//bazel_common:bundlers.bzl", "score_pkg_bundle")

score_pkg_bundle(
    name = "my_module",
    bins = ["@score_my_module//examples:my_example"],
    config_data = ["//showcases/standalone:my_module.score.json"],
    package_dir = "standalone",
)

2. Add a CLI descriptor. Create a *.score.json next to the bundle. The CLI auto-discovers every *.score.json deployed under /showcases and offers it in the interactive menu, so no code change to the CLI is required:

{
    "name": "My Module Example",
    "description": "Demonstrates my_module at runtime",
    "apps": [
        {
            "path": "/showcases/bin/my_example",
            "args": [],
            "env": {},
            "dir": "/showcases/data/my_module"
        }
    ]
}

3. Register the bundle in the top-level showcase package. Add your bundle to the aggregate score_pkg_bundle in showcases/BUILD so it is packaged into the images:

score_pkg_bundle(
    name = "showcases",
    bins = ["//showcases/cli"],
    other_package_files = [
        "//showcases/standalone:comm_pkg_files",
        # ...
        "//showcases/standalone:my_module_pkg_files",
    ],
    package_dir = "showcases",
)

Once your bundle is part of //showcases, it is reachable from //images/<platform>:image and is therefore built by every platform CI job automatically.

4.4. Deploy a component as part of the platform#

If your component is not a showcase — for example a background daemon that must always be present — it belongs to the deployed platform rather than to the demonstration layer. The image content is split into three layers:

  • Image (images/<variant>) — boot, kernel, drivers, rootfs skeleton.

  • Platform (deploy/) — the S-CORE modules as deployed. This is what a conformance check treats as the system under test.

  • Showcases (showcases/) — applications that demonstrate and verify the platform, removable without touching the two layers below.

Keep the two apart: a showcase must never ship the component it exercises, otherwise it verifies the binary it brought along instead of the one the distribution deployed. The lifecycle module is the illustrative case — the launch_manager daemon is deployed from deploy/lifecycle, while the applications it supervises stay in showcases/simple_lifecycle.

1. Package the component. Declare destination path and permissions as a pkg_files target in deploy/<module>/BUILD. Because the binary is resolved per platform (not per architecture), one definition serves every image, and score_rules_imagefs generates the matching mkifs directives so nothing has to be added to system.build:

# deploy/logging/BUILD
pkg_files(
    name = "datarouter_bin",
    srcs = ["@score_logging//score/datarouter"],
    attributes = pkg_attributes(mode = "0777"),
    prefix = "usr/bin/datarouter",
    strip_prefix = strip_prefix.files_only(),
)

2. Register it in the platform bundle. Unlike the showcase route this is not per-image work: every image already consumes the aggregate, so adding your target to it is enough.

# deploy/BUILD
pkg_filegroup(
    name = "platform_bundle",
    srcs = [
        # ...
        "//deploy/logging:datarouter_bin",
    ],
    visibility = ["//visibility:public"],
)

# images/qnx_x86_64/build/BUILD  (already present, shown for context)
qnx_ifs(
    name = "init",
    srcs = [
        # ...
        "//deploy:platform_bundle",
    ],
    build_file = "init.build",
    extra_build_files = ["system.build"],
)

Note that qnx_ifs rejects anything in srcs that does not provide a rules_pkg provider, so a plain filegroup or a bare label will not work - wrap it in pkg_files.

See deploy/README.md for the contract between the platform and the showcase layer, and images/qnx_x86_64/build/BUILD for how an image consumes it.

If your module simply cannot run on a given platform, exclude its targets via metadata.exclude_test_targets / metadata.extra_test_config in known_good.json (Step 1) rather than editing the workflow.