Builds

Overview

cikit provides reusable build support for CMake and PlatformIO projects.

The build macros execute the native build tool, collect common metrics, persist command logs, and expose the results through cikit data and artifacts.

The generic build report then combines these outputs into one RST page.

Conceptually:

build tool
   |
   +-- build metrics ---------> cikit data
   |
   +-- command logs ----------> LOG artifacts
   |
   +-- project outputs -------> BUILD artifacts
   |
   +-- release archives ------> BUNDLE artifacts
   |
   +-- optional timelines ----> TIMELINE artifacts
   |
   v
write_build_report()
   |
   v
RST build report

The current reusable build implementations are:

  • CMake

  • PlatformIO

Both use the same generic build report format.

Build Environment

Build macros operate on a CiRunEnvironment.

A typical project command starts by creating a logical build run:

from cikit.macros import common

env = common.init_env(run_name="build")

All metrics and artifacts created afterwards belong to that logical run.

This is important because build reports only consume data and artifacts from their active run.

Common Build Data

The build report understands a common set of run data keys.

Key

Meaning

build_files

Number of detected compiled source files.

warnings

Number of detected compiler warnings.

errors

Number of detected compiler errors.

configure_time

CMake configure duration in seconds.

build_time

Build duration in seconds.

total_build_time

Combined configure and build duration when available.

memory_<region>_used

Used bytes for one detected memory region.

memory_<region>_total

Total bytes for one detected memory region.

memory_<region>_percent

Percentage used for one detected memory region.

compiler

Compiler executable used by the build.

compiler_version

Detected compiler version.

compiler_type

Compiler type or tool-specific compiler identifier.

cmake_version

Version of the CMake executable queried for the build.

optimization

Detected compiler optimization setting, for example O2 or Os. mixed indicates multiple recognized settings.

lto

Detected link-time optimization setting. The value may describe an enabled, disabled, compiler-specific, or mixed setting.

Not every build tool provides every metric.

The generic report therefore treats most metrics as optional.

CMake Builds

The CMake macro separates configuration and build execution.

Typical usage:

from pathlib import Path

from cikit.macros import cmake, common

env = common.init_env(run_name="build")
build_dir = cmake.get_build_dir(env)

cmake.gather_cmake_meta(env)

cmake.cmake_configure(
    env,
    source_dir=Path("."),
    build_dir=build_dir,
    generator="Ninja",
    clean=True,
    compile_commands=True,
)

cmake.gather_compiler_meta(env, build_dir, "CXX")
cmake.cmake_build(env, build_dir)
cmake.write_rst(env)

Build Directory

cmake.get_build_dir() returns a directory below:

PROJECT_ROOT/build/

The default name is:

rel

For example:

build_dir = cmake.get_build_dir(env)

resolves conceptually to:

<project>/build/rel

A custom name can be supplied:

build_dir = cmake.get_build_dir(env, "debug")

CMake Configuration

cmake_configure() executes the CMake configure step:

cmake.cmake_configure(
    env,
    source_dir=Path("."),
    build_dir=build_dir,
    generator="Ninja",
    clean=True,
)

The generated command is conceptually:

cmake -G Ninja -S . -B <build-dir>

Additional CMake definitions can be supplied:

cmake.cmake_configure(
    env,
    source_dir=Path("."),
    build_dir=build_dir,
    defines=[
        "-DCMAKE_BUILD_TYPE=Release",
        "-DENABLE_TESTS=ON",
    ],
)

When clean=True is used, the complete build directory is removed before configuration.

compile_commands=True requests generation of compile_commands.json by passing CMAKE_EXPORT_COMPILE_COMMANDS=ON to CMake.

When CMake generates the compilation database, cikit registers it as a METADATA artifact. Some generators do not provide compile_commands.json; in that case configuration still succeeds and the related compiler-option metrics remain unavailable.

Configure Metrics

The configure duration is stored as:

configure_time

The value is measured in seconds and rounded to one decimal place.

Configure Log

The complete command result is persisted as:

cmake-configure.log

and automatically registered as a LOG artifact.

The log is written even when configuration fails. The configure return code is checked only after the log and configure_time have been persisted.

CMake Metadata

The installed CMake version can be stored in the current run data:

cmake.gather_cmake_meta(env)

The command queries the same cmake executable used by the build workflow and stores:

cmake_version

Compiler Metadata

After configuration, compiler metadata can be collected for a CMake language:

cmake.gather_compiler_meta(env, build_dir, "CXX")

The language must match a language enabled by the configured CMake project, for example C or CXX.

cikit uses the CMake File API toolchains-v1 reply generated during cmake_configure() and stores:

compiler
compiler_version
compiler_type

compiler_type contains the compiler ID reported by CMake, for example GNU or Clang.

Missing, invalid, or unavailable toolchain metadata is reported as a CiToolsError.

CMake Build

After configuration:

cmake.cmake_build(env, build_dir)

The build command is based on:

cmake --build <build-dir>

Parallel Execution

cmake_build() accepts a jobs argument.

cmake.cmake_build(env, build_dir, jobs=8)

The behavior is:

jobs = 0
    -> cmake --build ... --parallel

jobs = 1
    -> no explicit parallel argument

jobs > 1
    -> cmake --build ... --parallel <jobs>

The same parallel-argument handling is used by the CTest helper.

CMake Build Metrics

cmake_build() collects the following build metrics:

  • compiled files

  • warnings

  • errors

  • linker memory usage

  • build duration

Compiled files, diagnostics, and memory usage are extracted from the build output. Build duration is measured around the CMake build command.

When configure_time is already available, cikit also stores:

total_build_time

When compile_commands.json is available, cmake_build() additionally inspects the compiler command lines for recognized optimization and LTO options.

The detected settings are stored as:

optimization
lto

If multiple recognized settings are found, the corresponding value is stored as mixed.

These metrics are optional. They are omitted when the compilation database is not available or the relevant compiler options cannot be recognized.

Compiled Files

Compiled files are detected from CMake/Ninja output containing lines matching the form:

Building ... object

If no such lines are found, build_files is omitted.

This can happen when an incremental build has nothing to compile.

Diagnostics

Compiler diagnostics are detected through GCC/Clang-style output containing:

warning:
error:

The resulting counts are stored as:

warnings
errors

Memory Usage

CMake memory metrics are parsed from linker output compatible with GNU linker --print-memory-usage.

For example, a region named FLASH becomes:

memory_flash_used
memory_flash_total
memory_flash_percent

Memory metrics are only available when the linker actually emits compatible memory usage information.

Build Log

The complete CMake build command result is stored as:

cmake-build.log

and registered automatically as a LOG artifact.

Metrics and the log are persisted before the build return code is checked.

CTest

The CMake macro also provides a CTest helper:

cmake.ctest(
    env,
    build_dir,
    test_alias="ctest",
)

CTest creates JUnit-compatible XML output and feeds it into the same test-result processing used by the pytest macro.

PlatformIO Builds

PlatformIO builds are configured by the project’s platformio.ini.

cikit does not duplicate PlatformIO board, framework, environment, library, or build settings.

Typical usage:

from pathlib import Path

from cikit.macros import common, platformio

env = common.init_env(run_name="build")

platformio.build(
    env,
    project_dir=Path("."),
    clean=True,
)

platformio.write_rst(env)

Project Directory

When project_dir is omitted, PlatformIO runs in PROJECT_ROOT.

Clean Builds

With:

platformio.build(env, clean=True)

cikit removes the project’s local:

.pio/

directory before invoking PlatformIO.

Build Execution

The macro runs:

pio run

through the configured cikit execution tool.

Compiler Metadata

Compiler metadata can be collected independently from the build:

platformio.gather_compiler_meta(env)

The resolved PlatformIO environment is queried for its configured C compiler. The compiler itself is then queried for its version.

When available, cikit stores:

compiler
compiler_version
compiler_type

These values are also included in the generated build report summary.

Compiler metadata is only stored when exactly one PlatformIO environment can be resolved unambiguously. Missing or unavailable metadata is ignored.

PlatformIO Metrics

PlatformIO output is parsed for:

  • compiled files

  • compiler warnings

  • compiler errors

  • memory usage

  • build duration

build_files, warnings, and errors are always stored, even when they are zero.

Compiled files are detected from lines beginning with:

Compiling

Warnings and errors are counted from compiler-style diagnostics on stderr.

Memory Usage

When PlatformIO emits its standard memory summary, cikit converts detected regions into the same flat key structure used by the CMake macro:

memory_<region>_used
memory_<region>_total
memory_<region>_percent

Memory metrics are omitted when the PlatformIO output does not contain a recognized memory table.

Build Log

The PlatformIO command result is persisted as:

platformio-build.log

and automatically registered as a LOG artifact.

Registering Build Outputs

The CMake and PlatformIO macros do not automatically decide which generated files are project deliverables.

The project command should explicitly register outputs that should appear in the build report.

For example:

from cikit.artifacts import ArtifactType

env.copy_artifact(
    build_dir / "firmware.bin",
    ArtifactType.BUILD,
)

Registered BUILD artifacts are exposed by the generic build report as a table containing download links, file sizes, and SHA-256 hashes. File size and SHA-256 metadata are populated automatically by copy_artifact().

For details about artifact storage and naming, see Artifacts.

Bundles

Release archives can be created with the bundle macro.

Example:

from cikit.macros import bundle

bundle.create(
    env,
    "firmware-package.zip",
    build_dir / "release",
)

Supported formats are:

  • .zip

  • .tar

  • .tar.gz

The archive is registered automatically as a BUNDLE artifact.

Bundles registered for the active run are included automatically in the build report using the same table format with download links, file sizes, and SHA-256 hashes as direct build artifacts.

Timelines

A build report can also include timeline charts registered before report generation.

For example:

from cikit.macros.common import chart_timeline
from cikit.timeline import TimelinePreset

timeline = chart_timeline(
    env,
    keys=["build_time", "warnings"],
    title="Build History",
    limit=20,
)

timeline.set_preset(TimelinePreset.LINE_REPORT)
timeline.create(env, "build")

The resulting TIMELINE artifact is discovered by the build report through the artifact registry.

For the complete timeline workflow, see Timeline Charts.

Build Reports

CMake and PlatformIO provide convenience wrappers around the generic build report.

For CMake:

cmake.write_rst(env)

For PlatformIO:

platformio.write_rst(env)

Both delegate to:

reports.write_build_report(env)

The generic build report includes all supported information available for the active run.

It can contain:

  • compiled file, warning, and error counts

  • CMake and compiler metadata

  • optimization and LTO settings when available

  • configure, build, and total build times

  • memory usage

  • timeline charts

  • BUILD and BUNDLE artifact tables with download links, file sizes, and SHA-256 hashes

  • LOG artifacts

The generated report is itself registered as an RST artifact.

Artifacts must be registered before write_rst() if they should appear in that report.

Complete CMake Example

A project-specific CMake command can combine the complete workflow:

from pathlib import Path

from cikit.artifacts import ArtifactType
from cikit.macros import bundle, cmake, common

env = common.init_env(run_name="build")
build_dir = cmake.get_build_dir(env)

cmake.gather_cmake_meta(env)

cmake.cmake_configure(
    env,
    source_dir=Path("."),
    build_dir=build_dir,
    generator="Ninja",
    clean=True,
    compile_commands=True,
)

cmake.gather_compiler_meta(env, build_dir, "CXX")
cmake.cmake_build(env, build_dir)

env.copy_artifact(
    build_dir / "firmware.bin",
    ArtifactType.BUILD,
)

bundle.create(
    env,
    "firmware-package.zip",
    build_dir / "release",
)

cmake.write_rst(env)

The important order is:

gather CMake metadata
   |
configure
   |
gather compiler metadata
   |
build
   |
register outputs
   |
create bundles
   |
create optional timelines
   |
write build report

Complete PlatformIO Example

A PlatformIO project follows the same pattern:

from pathlib import Path

from cikit.artifacts import ArtifactType
from cikit.macros import common, platformio

env = common.init_env(run_name="build")

platformio.build(
    env,
    project_dir=Path("."),
    clean=True,
)

env.copy_artifact(
    Path(".pio/build/myenv/firmware.bin"),
    ArtifactType.BUILD,
)

platformio.write_rst(env)

The exact PlatformIO output path remains project-specific because it depends on the configured PlatformIO environments.

Failed Builds

Both build macros persist available metrics and command logs before checking the tool return code.

As a result, failed builds can still leave useful diagnostics in cikit data and registered LOG artifacts.

Whether a report is generated after a failed build is controlled by the project’s command orchestration.

Sphinx Integration

Build reports are registered as RST artifacts.

During Sphinx preparation they are copied into:

docs/_generated/

Build outputs, bundles, logs, and timeline charts are copied into the static artifact area according to the common artifact mapping.

See Sphinx Documentation Build for the documentation build workflow.

Responsibilities

Component

Responsibility

Project build configuration

Compiler, linker, board, framework, source files, and tool-specific settings.

CMake / PlatformIO macro

Execute the build, collect common metrics, and persist command logs.

Project command

Select deliverables and register BUILD artifacts.

Bundle macro

Package selected files or directories as BUNDLE artifacts.

Timeline support

Render historical cikit data as TIMELINE artifacts.

Generic build report

Combine run data and supported artifacts into an RST report.

Sphinx macro

Collect generated reports and artifacts into the documentation build.

See Also