ci: emit per-board Kconfig dep graph as a release sidecar#2171
Merged
Conversation
Adds general/scripts/kconfig_graph.py — a kconfiglib-based emitter that walks
the merged Buildroot tree, filters to currently-set + user-prompted
BR2_PACKAGE_* symbols, and writes two schema-versioned JSONs into IMAGES_DIR
alongside sizes.json:
kconfig-graph.<board>-<variant>.json - per-symbol type/prompt/depends/
selects/selected_by (direct only)
kconfig-help.<board>-<variant>.json - per-symbol help text, split out
This feeds firmware-explorer v0.3's interactive configurator (separate PR)
which lets users toggle packages off, computes the cascade closure with
real Kconfig semantics, and downloads a defconfig fragment.
Why filter currently-set + user-prompted?
* Buildroot's full Config.in defines ~7100 BR2_PACKAGE_* symbols across
all upstream packages. Shipping every symbol per board produces ~3 MB
per platform with no UX benefit — the configurator can't usefully help
you enable a package whose dependencies aren't already on this board.
* Non-prompted symbols (*_ARCH_SUPPORTS, *_SUPPORTS, HAS_*) are feature
detection Buildroot derives automatically; they aren't user-toggleable
and would just be noise in the UI.
* The currently-set ∧ prompted intersection is closed under Kconfig's
own dep tracking: every symbol referenced from an emitted symbol's
depends_on / select chain is also set, so cascade resolution in the
explorer doesn't lose references.
Empirical on a fully-configured cv200lite board: emits 48 symbols, 12
cascade-required, **14 KB raw / 1.8 KB gzipped** per platform.
selected_by precision
Kconfiglib's `rev_dep` expression collects symbols from both the select
source AND its condition (`select X if Y` puts Y in the rev_dep). For
the cascade UX we want direct selectors only — otherwise depended-on
symbols look like spurious "blockers". The emitter pre-builds a
target_symbol → [direct selectors] index by iterating
`sym.selects` across the whole symbol set, then filters to the
currently-set ones for the emitted `selected_by` list.
Kconfig env contract
Mirrors size_report.py: TARGET_DIR / BR2_OUTPUT_DIR / IMAGES_DIR /
OPENIPC_SOC_MODEL / OPENIPC_VARIANT. Sets defaults for the env vars
Buildroot's Kconfig top-level expects (BR2_BASE_DIR, BR2_EXTERNAL,
BR2_EXTERNAL_GENERAL_PATH, srctree). Two non-obvious ones:
CONFIG_ = "" Kconfiglib defaults to stripping a CONFIG_ prefix
from .config lines; Buildroot's symbols carry the
BR2_ prefix INCLUDED in the symbol table, so any
non-empty prefix here makes load_config silently
skip every line as "malformed".
BR2_SKIP_LEGACY=YES Skip Config.in.legacy parsing. It only generates
renamed/removed warnings that we don't surface
in the configurator UI.
Makefile + CI
New `kconfig-graph` target mirrors `size-report`. `make deps` gains
`pip install --break-system-packages kconfiglib`. CI's Build kconfig
graph step is best-effort: a kconfiglib install or graph emit failure
emits a ::warning:: and continues, so an upstream kconfiglib regression
cannot sink a working build. The KCONFIG + KHELP env vars are added
to all three upload steps with the same pattern that already promotes
SIZES.
Tests
general/scripts/tests/test_kconfig_graph.py drives build_report()
against a synthetic Config.in fixture, asserting: type / prompt /
currently_set filtering; depends_on / selects / selected_by extraction;
direct-selector precision; help-text split. Run via
`python3 general/scripts/tests/test_kconfig_graph.py`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds `general/scripts/kconfig_graph.py` — a kconfiglib-based emitter that
walks the merged Buildroot tree, filters to currently-set + user-prompted
`BR2_PACKAGE_*` symbols, and writes two schema-versioned JSONs into
`IMAGES_DIR` alongside `sizes.json`:
depends / selects / selected_by (direct only)
This feeds firmware-explorer v0.3's interactive configurator (separate PR)
which lets users toggle packages off, computes the cascade closure with
real Kconfig semantics, and downloads a defconfig fragment.
Why filter currently-set + user-prompted
all upstream packages. Shipping all of them per board produces ~3 MB per
platform with no UX benefit — the configurator can't usefully help you
enable a package whose dependencies aren't already on this board.
feature-detection Buildroot derives automatically; they aren't
user-toggleable.
dep tracking: every symbol referenced from an emitted symbol's
`depends_on` / `select` chain is also set, so cascade resolution in the
explorer doesn't lose references.
Empirical on a fully-configured cv200lite tree:
`selected_by` precision (subtle)
Kconfiglib's `rev_dep` expression includes symbols from both the select
source AND its condition (`select X if Y` puts Y in the rev_dep). For the
cascade UX we want direct selectors only — otherwise depended-on symbols
look like spurious "blockers". The emitter pre-builds a
`target_symbol → [direct selectors]` index by iterating `sym.selects`
across the whole symbol set, then filters to the currently-set ones.
Kconfig env contract
Mirrors `size_report.py`: `TARGET_DIR` / `BR2_OUTPUT_DIR` / `IMAGES_DIR` /
`OPENIPC_SOC_MODEL` / `OPENIPC_VARIANT`. Sets defaults for the env vars
Buildroot's Kconfig top-level expects (`BR2_BASE_DIR`, `BR2_EXTERNAL`,
`BR2_EXTERNAL_GENERAL_PATH`, `srctree`). Two non-obvious ones:
from `.config` lines; Buildroot's symbols carry the `BR2_` prefix
included in the symbol table, so any non-empty prefix here makes
`load_config` silently skip every line as "malformed".
generates renamed/removed warnings that we don't surface.
Makefile + CI
(kconfiglib is a single-file pure-Python module, ~10k LoC, MIT).
after the existing "Build size report". Best-effort: kconfiglib
install or graph emit failure emits a `::warning::` and continues, so
an upstream kconfiglib regression cannot sink a working build.
steps with the same pattern that already promotes `SIZES`.
Tests
`general/scripts/tests/test_kconfig_graph.py` drives `build_report()`
against a synthetic Config.in fixture, asserting: type / prompt /
currently_set filtering; depends_on / selects / selected_by extraction;
direct-selector precision; help-text split. Run via
`python3 general/scripts/tests/test_kconfig_graph.py` — 2 unittest
cases, both green locally.
Cross-repo dependency
Once this lands and one nightly cycle attaches the JSON to release tags,
`OpenIPC/firmware-explorer` PR (separate) lights up the configurator tab.
Until then, that PR's prebuild logs "no kconfig assets found in any
retained tag" and the tab stays disabled with a tooltip — by design.
Test plan
🤖 Generated with Claude Code