nros_core/codegen_version.rs
1// RFC-0090 / phase-429 — the codegen version, the one token that says whether
2// generated code and this runtime can work together.
3//
4// NO `//!` INNER DOC COMMENTS IN THIS FILE, ON PURPOSE. It is `include!`d
5// verbatim by `nros-build-helpers`, and an inner doc comment inside an
6// `include!` expansion is a hard error (E0753). The module docs live on the
7// `pub mod` in `lib.rs`. Keep this file a dependency-free set of `const`s and
8// `const fn`s: anything else breaks the include.
9//
10// THREE READERS, THREE REASONS — do not "unify" them:
11//
12// * `nros-core` itself, directly.
13// * `nros-build-helpers` (defines the runtime anchors) and `rosidl-codegen`
14// (stamps the emitted artifacts), both by `include!`. Neither can afford a
15// `nros-core` dependency edge for a pair of `const`s: the first is host-only
16// and appears in every tracked leaf lockfile, and the second lives in the
17// separate `packages/cli` workspace, so the edge would add rows to its
18// lockfile too. `include!` costs nothing in the graph, and rustc records the
19// path in the depfile, so editing the constants rebuilds both.
20// * the CLI's guard, by parsing this file as TEXT. It inspects a CONSUMER's
21// tree at run time, where compiling is not available. This is the only
22// parser, and it exists because the other two options do not apply.
23
24/// The codegen version this runtime emits and accepts.
25///
26/// **Bump this deliberately** when the interface between generated code and the
27/// runtime changes: a trait signature generated code implements, a symbol it
28/// defines, a layout rule it obeys. Do NOT bump it for a cosmetic template
29/// edit — that moves the fingerprint, which is a different question (see the
30/// module docs).
31///
32/// Bumping invalidates every generated tree. That is affordable here because
33/// `generated/` is never committed (CLAUDE.md), so regeneration is always
34/// available — and it is exactly why the value must not move on cosmetics.
35///
36/// Gated by `check-codegen-version-surface`, which fails when the surface
37/// generated code names changes and this constant does not.
38pub const NROS_CODEGEN_VERSION: u32 = 1;
39
40/// The oldest codegen version this runtime still accepts.
41///
42/// Equal to [`NROS_CODEGEN_VERSION`] at introduction: no window, because
43/// `generated/` is never committed and regeneration is therefore always
44/// available. Raise the floor only when a real migration needs one, and lower
45/// it never.
46///
47/// The range `[NROS_CODEGEN_VERSION_MIN, NROS_CODEGEN_VERSION]` is expressed to
48/// C and C++ as a SET OF DEFINED SYMBOLS rather than as a comparison — see
49/// `nros-build-helpers`' codegen-version anchor — so there is no range check on
50/// that side that could itself be wrong.
51pub const NROS_CODEGEN_VERSION_MIN: u32 = 1;
52
53/// Does `emitted` fall in the range this runtime accepts?
54///
55/// The ONE comparison. Rust call sites reach it through
56/// `nros_node::codegen_version_check`, the CLI through `abi_guard`; neither
57/// re-spells the bounds.
58#[must_use]
59pub const fn accepts(emitted: u32) -> bool {
60 emitted >= NROS_CODEGEN_VERSION_MIN && emitted <= NROS_CODEGEN_VERSION
61}
62
63// The accepted range must be non-empty, checked at COMPILE time because it is a
64// property of two constants. A runtime test would be the wrong tool — and
65// clippy says so (`assertions_on_constants`) in every crate that `include!`s
66// this file, which is how it was found.
67const _: () = assert!(
68 NROS_CODEGEN_VERSION_MIN <= NROS_CODEGEN_VERSION,
69 "the accepted codegen range is empty: nothing could ever be compatible"
70);