Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
# Copyright (c) 2026 TiaC Systems
# SPDX-License-Identifier: Apache-2.0
"""context.cmake -- the build-glue handoff, not a rig artifact: a cmake
fragment the rig build module (dts.cmake) include()s to learn what the
rig instantiated. Kept out of `emit()` so rig artifacts and build glue
stay separate concerns: this is a pure value function over `(rig,
deps)`; cli.py calls it alongside, never inside, `emitter.emit()`.
rigc is the sole authority on what pass 1 actually read: `deps` is the
UNION of every real source-tree file this run's loader/registry/board
calls touched (rig.yml, its content file, `.shield` templates and their
cpp includes, connector bindings, index headers, the board .dts) --
cmake/modules/dts.cmake appends RIG_DEPENDS (sorted, absolute) to
CMAKE_CONFIGURE_DEPENDS on top of its own static registrations.
"""
from __future__ import annotations
import logging
from ..deps import Deps
from ..model import Rig
log = logging.getLogger(__name__)
def _cmake_list_escape(value: str) -> str:
"""Escape one string for embedding as ONE element of a ;-joined CMake
list literal inside a double-quoted set(... "a;b;c") -- CMake unescapes
\\;/\\"/\\\\ when the string is later read back (e.g. by
set_property(... APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ...) in
dts.cmake), so a path containing a literal ;, ", or \\ survives the
round-trip as a single element instead of being mis-split or corrupting
the quoting. Real-world paths essentially never contain these, but
RIG_DEPENDS is provenance data cmake.cmake trusts verbatim -- worth doing
correctly once here rather than assuming."""
return value.replace("\\", "\\\\").replace('"', '\\"').replace(";", "\\;")
def render(rig: Rig, deps: Deps) -> bytes:
"""context.cmake's full bytes (UTF-8; the content is plain cmake
syntax, but every artifact in this package encodes explicitly rather
than depending on a locale). `rig`/`deps` are read-only; returns a
fresh bytes value the caller owns.
RIG_NAME/RIG_BOARD/RIG_SHIELDS always appear. RIG_BOARD is `rig.board`
verbatim -- the board this build actually used, which can only ever
be the CLI's own `--board` (no rig file declares one); this function
has no injection logic of its own, it only ever echoes what the
loader already resolved.
RIG_SHIELD_REVISIONS/RIG_REVISION/RIG_VARIANT appear only when the rig
actually declares the corresponding axis/shield-revision -- the "no
declaration, no artifact" rule that keeps an axis-less rig's
context.cmake free of the field entirely. RIG_REVISION is always the
RESOLVED value (hwmv2's nearest-lower match already applied);
RIG_REVISION_REQUESTED appears alongside it only when a request was
made AND it differs from what it resolved to -- dts.cmake's own
configure-log line reads this to print "requested -> resolved" only
in that case, never when a rig simply took its declared default.
RIG_DEPENDS is always present, sorted, each element escaped for a
CMake list literal (`_cmake_list_escape`)."""
log.info("context.render(): rig '%s'", rig.name)
shields: list[str] = []
shield_revisions: list[str] = []
for inst in rig.instances:
if inst.shield.name not in shields:
shields.append(inst.shield.name)
# The RESOLVED revision of every shield that declares a
# revisions: axis, default selections included -- symmetric with
# RIG_REVISION/RIG_VARIANT below. Silence would mean both
# "revision 1" and "no revisions at all"; a value with a raw and
# a resolved form is always recorded in its RESOLVED form.
shield = inst.shield
if shield.revision is not None and shield.revisions is not None:
pair = f"{shield.name}@{shield.revision}"
if pair not in shield_revisions:
shield_revisions.append(pair)
lines = [
"# generated by rigc — consumed by cmake/modules/dts.cmake",
f'set(RIG_NAME "{rig.name}")',
f'set(RIG_BOARD "{rig.board}")',
f'set(RIG_SHIELDS "{";".join(shields)}")',
]
if shield_revisions:
lines.append(f'set(RIG_SHIELD_REVISIONS "{";".join(shield_revisions)}")')
# RIG_REVISION/RIG_VARIANT: written only when this rig actually declares
# the corresponding axis (rig.revision/rig.variant is None otherwise) --
# same "no declaration, no artifact, zero churn" precedent as
# rig-gen-includes.dtsi follows for its own header list, so an
# axis-less corpus rig's context.cmake stays byte-identical.
if rig.revision is not None:
lines.append(f'set(RIG_REVISION "{rig.revision}")')
if rig.revision_requested is not None and rig.revision_requested != rig.revision:
lines.append(f'set(RIG_REVISION_REQUESTED "{rig.revision_requested}")')
if rig.variant is not None:
lines.append(f'set(RIG_VARIANT "{rig.variant}")')
deps_list = ";".join(_cmake_list_escape(p) for p in sorted(deps))
lines.append(f'set(RIG_DEPENDS "{deps_list}")')
log.debug("context.render(): %d shield(s), %d dep(s)", len(shields), len(deps))
return ("\n".join(lines) + "\n").encode("utf-8")
efd4b04f2This file is pinned to the selected release. Download the original to open it in your design tool.