Skip to content

Build System Integration

The build-config command bridges the Syscribe feature model and your build system. It reads a named Configuration, resolves which features are selected, and emits a set of named build variables in the format your toolchain expects.

1. Annotating FeatureDefs with buildExports

Add a buildExports: list to any FeatureDef to declare what the build system should see when that feature is selected or deselected:

# model/Features/ABS.md
---
type: FeatureDef
id: FEAT-ABS
name: ABS
buildExports:
  - var: ENABLE_ABS        # build variable name
    whenSelected: 1        # value when the feature is selected (default: 1)
    whenDeselected: 0      # value when deselected; omit to suppress the var entirely
---

Key semantics:

  • var (required) — the variable name emitted into the build artifact.
  • whenSelected (optional, default 1) — the value assigned when the feature is selected in the Configuration.
  • whenDeselected (optional, absent by default) — the value assigned when the feature is deselected. If absent the variable is omitted from the output entirely when the feature is not selected.

A single FeatureDef may declare multiple entries, each with a different var:

buildExports:
  - var: ENABLE_CAMERA
    whenSelected: 1
    whenDeselected: 0
  - var: CAM_RESOLUTION
    whenSelected: 1080

2. Adding buildVar to parameters

A FeatureDef parameter with buildVar: maps its bound value directly into a build variable when the feature is selected:

# model/Features/Radar.md
---
type: FeatureDef
id: FEAT-RADAR
name: Radar
parameters:
  - name: maxRange
    type: ScalarValues::Real
    range: "0..200"
    default: 100
    buildVar: RADAR_MAX_RANGE   # emitted when the feature is selected
---

When the Configuration has a parameterBindings: entry for this parameter, the bound value is used; otherwise the parameter default: is the fallback.

3. Configuration buildOverrides

Use buildOverrides: on a Configuration to set a variable unconditionally. buildOverrides wins over everything — it is applied last and always replaces any value contributed by buildExports or buildVar.

# model/Configs/CONF-PREMIUM-001.md
---
type: Configuration
id: CONF-PREMIUM-001
name: Premium
status: approved
featureModel: Features
features:
  Features::ABS: true
  Features::Radar: true
parameterBindings:
  Features::Radar.maxRange: 150
buildOverrides:
  DEBUG_BUILD: 0      # suppress debug regardless of any feature
---

buildOverrides also resolves E050 conflicts: when two selected features both declare the same variable with different whenSelected values, adding a buildOverrides entry silences the error and emits the override value.

4. Running build-config

# CMake cache file
syscribe -m model/ build-config --config CONF-PREMIUM-001 --format cmake

# C/C++ header
syscribe -m model/ build-config --config CONF-PREMIUM-001 --format c-header

# GNU Makefile include
syscribe -m model/ build-config --config CONF-PREMIUM-001 --format makefile

# Shell environment (source in CI)
syscribe -m model/ build-config --config CONF-PREMIUM-001 --format env

# JSON (usable by any build system or script)
syscribe -m model/ build-config --config CONF-PREMIUM-001 --format json

# Kconfig (Zephyr, Linux kernel)
syscribe -m model/ build-config --config CONF-PREMIUM-001 --format kconfig

Example cmake output

# Generated by: syscribe build-config --config CONF-PREMIUM-001 --format cmake
# Configuration: CONF-PREMIUM-001 — "Premium"
set(ENABLE_ABS 1)
set(RADAR_MAX_RANGE 150)

Example c-header output

/* Generated by: syscribe build-config --config CONF-PREMIUM-001 --format c-header */
/* Configuration: CONF-PREMIUM-001 — "Premium" */
#ifndef SYSCRIBE_CONFIG_H
#define SYSCRIBE_CONFIG_H
#define ENABLE_ABS 1
#define RADAR_MAX_RANGE 150
#endif /* SYSCRIBE_CONFIG_H */

Example kconfig output

# Generated by: syscribe build-config --config CONF-PREMIUM-001 --format kconfig
# Configuration: CONF-PREMIUM-001 — "Premium"
CONFIG_ENABLE_ABS=y
CONFIG_RADAR_MAX_RANGE=150

Variables are always emitted in alphabetical order for reproducible diffs.

5. The --all-configs CI matrix pattern

Pass --all-configs to generate build variables for every Configuration in the model, emitted as a JSON array. This is useful for driving a CI matrix:

syscribe -m model/ build-config --all-configs --format json

Output:

[
  {
    "config": "CONF-BASE-001",
    "name": "Base",
    "vars": { "ENABLE_ABS": 0 }
  },
  {
    "config": "CONF-PREMIUM-001",
    "name": "Premium",
    "vars": { "ENABLE_ABS": 1, "RADAR_MAX_RANGE": 150 }
  }
]

When a configuration has an E050 conflict the entry gets an "error" field instead of an empty "vars".

GitHub Actions matrix example

jobs:
  generate-matrix:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.gen.outputs.matrix }}
    steps:
      - uses: actions/checkout@v4
      - name: Generate config matrix
        id: gen
        run: |
          MATRIX=$(syscribe -m model/ build-config --all-configs | jq -c '[.[] | {config: .config}]')
          echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"

  build:
    needs: generate-matrix
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
    steps:
      - uses: actions/checkout@v4
      - name: Build for ${{ matrix.config }}
        run: |
          syscribe -m model/ build-config --config ${{ matrix.config }} --format cmake \
            > build/${{ matrix.config }}.cmake
          cmake -C build/${{ matrix.config }}.cmake .

6. The --prefix option

Prepend a string to every emitted variable name to avoid namespace collisions:

syscribe -m model/ build-config --config CONF-PREMIUM-001 --format c-header --prefix MY_APP_

Output:

#define MY_APP_ENABLE_ABS 1
#define MY_APP_RADAR_MAX_RANGE 150

7. Validation: E050 and W050

E050 — conflicting variable from two selected features

E050 is part of the default validate pass. It fires when two or more selected features both declare the same buildExports variable name with different whenSelected values and the conflict is not resolved by a buildOverrides: entry on the Configuration.

To resolve: add buildOverrides: on the Configuration with the authoritative value, or rename one of the variables.

W050 — selected feature with no build mapping (not currently emitted)

W050 is catalogued as an opt-in warning for a selected feature that declares neither buildExports: nor any parameter with buildVar: (no build-system representation). In the current release, however, neither validate nor build-config prints it: the check runs inside the build-variable resolver but its findings are not surfaced, so validate --deny W050 has no effect. To find features that contribute nothing to the build, inspect the build-config output for missing variables, or add buildExports: to every feature that should drive the build.