pub const NROS_CODEGEN_VERSION_MIN: u32 = 18;Expand description
The oldest codegen version this runtime still accepts.
Equal to NROS_CODEGEN_VERSION at introduction: no window, because
generated/ is never committed and regeneration is therefore always
available. Raise the floor only when a real migration needs one, and lower
it never.
Held at 1 while NROS_CODEGEN_VERSION moved to 2 (phase-417, rebased onto
phase-429’s gate). That surface move is ADDITIVE plus one relocation, so a
tree generated against version 1 still runs: the C additions are new entry
points, the two C++ changes add size()/empty() and std::string interop
to FixedString/HeapString, and the four rcl_ret_t declarations that
left action.h / client.h / parameter.h / service.h were CONSOLIDATED
into nros_generated.h, which all four still reach through rclc/types.h
— the headers say so in a comment, and keeping that include is what makes
#include <rclc/action.h> continue to compile. Nothing a version-1 tree
names was withdrawn, so this is the window the doc above describes rather
than a migration.
Still 2 while NROS_CODEGEN_VERSION moved to 3 (issues 1148/1149). That
move is one ADDED runtime entry point, rclc_cdr_align, which the
header-only rclc_cdr_borrow_le_slice_* now call so an 8-byte borrowed
sequence starts on its stream boundary. A version-2 tree names nothing that
was withdrawn: it compiles against the corrected rclc/detail/view.h and picks the
fix up for free, because the view helpers are static inline in the header
rather than symbols the generated code defines.
Still 2 while NROS_CODEGEN_VERSION moved to 4 (phase-427 W7). That move
is a SPELLING change and nothing else: nros::bind_subscription_sized — the
one demanded C++ signature that names the node type — now writes its
parameter ::rclcpp::Node& where it wrote Node&, because the class moved
to rclcpp:: and nros::Node became a deprecated alias for it. The two
spellings are ONE TYPE (std::is_same<rclcpp::Node, nros::Node> is asserted
by tests/compile/one_node_type.cpp), so a version-3 tree — which declares
its node static ::nros::Node __nros_node_0; — still compiles and still
links, with a deprecation warning naming its replacement. Nothing it names
was withdrawn, which is the window the doc above describes.
The version moves anyway because the gate is fail-closed on the TEXT of a demanded declaration and should stay that way: it cannot know that two spellings are one type, and a gate that tried to would be one that misses a real rename.
Still 2 while NROS_CODEGEN_VERSION moved to 5 (phase-417 W5.b/c/d). That
move is ADDITIVE and, in most of its entries, is not a change to the runtime
at all: the rclc preset constructors landed as static inline forwarders in
<nros/{publisher,subscription,service,client,action}.h>, and naming a type
inside one of those headers is what puts a c|type|<header>|<name> row in
the extracted surface — rclc_message_type_t, rcl_node_t,
rclc_service_type_t and rclc_action_type_t were already declared in
nros_generated.h and still are, reached the same way. The one genuinely
new declaration a demanded name resolves to is
rclc_service_init_with_qos, which is an entry point that already existed
and is now also visible from <rclc/service.h>. Nothing a version-4 tree
names was withdrawn, so this is the window the doc above describes rather
than a migration.
The version moves anyway for the reason the version-4 paragraph gives: the gate is fail-closed on the TEXT of a demanded declaration and should stay that way. A gate that tried to decide which additions are “really” runtime changes is one that misses a real withdrawal.
Still 2 while NROS_CODEGEN_VERSION moved to 6 (phase-417 W4.b). That
move is a WITHDRAWAL, and it is the first one — nros_core::action stopped
re-exporting ActionServer and ActionClient, two “type-level marker”
structs. A version-5 tree still runs, because no generated tree has ever
named them: measured before the deletion, they had ZERO references anywhere
in this repository, and they were UNREACHABLE through the nros facade at
all, which re-exports nros_node’s live ActionServer / ActionClient
under exactly those spellings. So the floor stays where it is. The gate is
fail-closed on the extracted surface and cannot know which names nothing
reached, which is the property that makes it worth keeping.
Still 2 while NROS_CODEGEN_VERSION moved to 7 (issue 1437, phase-444).
ADDITIVE on the surface generated code names, plus one APPEND to a struct
nothing generated declares. The additions are six new C entry points
(nros_{publisher,subscription}_get_actual_qos and the four service /
client halves) and two enumerators on each of the four nros_qos_*_t
enums (UNKNOWN, and SYSTEM_DEFAULT on three of them), appended so every
existing discriminant keeps its value. The append is _executor on
rcl_subscription_t, placed after _opaque so every existing field
offset is unchanged — it grows the struct, which is why this is a surface
move at all, and a version-6 tree that only names the struct by pointer is
unaffected. Nothing was withdrawn, so the floor stays where it is.
The gate reports the move as two CHANGED types rather than as the four
added prototypes, because a const struct rcl_service_t * parameter is a
tagged reference and keys on the same c|type|<header>|<name> row as the
struct itself. That is the gate being fail-closed on text, which is the
property the version-4 paragraph above argues for keeping.
Still 2 while NROS_CODEGEN_VERSION moved to 8 (phase-467 Q3). ADDITIVE,
and the first move that adds a TRAIT generated code implements — which is
the trigger the doc above names first. nros_core::SecNanosecMsg is the
Rust stand-in for the C++ template <typename TimeMsgT> Time::to_msg: Rust
has no structural bound for “has sec and nanosec”, so
rosidl-codegen emits impl nros_core::SecNanosecMsg for <Msg> for every
generated message with that exact field set, and
nros_core::Time::to_ros_msg() returns through it. The surface moved by two
rows, both additions: the trait, and the pub use time:: re-export that
publishes it.
A version-7 tree still runs: it names nothing that was withdrawn, and the
impl is something a NEWER tree emits rather than something an older one is
missing. What an older tree loses is only the conversion itself —
t.to_ros_msg() into one of ITS types will not resolve until it is
regenerated, which is a compile error at the call site on the day someone
writes the call, not a silent mismatch. So the floor stays where it is.
Still 2 while NROS_CODEGEN_VERSION moved to 9 (issue 1293, phase-480
W4). ADDITIVE: nros_serdes::schema::EMPTY_STRUCT_MEMBER, the name of
rosidl’s uint8 structure_needs_at_least_one_member, which a version-9
tree names as the schema of every empty message and whose byte it writes.
A version-8 tree still compiles and runs against this runtime: it names
nothing that was withdrawn. What it keeps is the defect 1293 fixes for a
regenerated tree, and only for ITS OWN empty messages: they still go out
with no byte (which every stock RMW refuses) and still have no Cyclone
descriptor (BuildError::EmptySchema, which says to regenerate). That is
the behaviour the tree already had, not a new mismatch the runtime
introduces, so the floor stays where it is.
RAISED to 10 when NROS_CODEGEN_VERSION moved to 10 (phase-483 W1), and
this is the first move that needs it. The C++ user API left nros:: for
rclcpp::, and nros:: was deleted rather than kept as an alias, so every
name a version-9 C++ tree spells — nros::FixedString<N>,
nros::FixedSequence<T, N>, nros::HeapString, nros::HeapSequence<T>,
nros::Span<T> / LeSpan<T> / StringView, nros::bind_subscription_sized
and nros::detail::size_bound_dependent_false — is WITHDRAWN. A version-9
C++ tree does not compile against this runtime, so accepting it would
replace the version mismatch this constant reports with a wall of
unresolved-name errors. Rust and C trees are unaffected by the move, but the
version is one number for all three, so they regenerate too.
Still 10 while NROS_CODEGEN_VERSION moved to 11 (phase-487 W1). The
surface the gate saw move is subscription.h’s static inline
rclc_subscription_init_best_effort, whose body forwards to
rclc_subscription_init_with_qos and lost the two NULLs that function no
longer takes. Generated C does not call either: a message header’s typed
registration is rclc_executor_add_subscription_typed_sized, whose
signature is unchanged, and it passes struct rcl_subscription_t *, whose
layout shrank but which generated code never dereferences. A version-10
tree therefore still compiles and runs.
RAISED to 12 when NROS_CODEGEN_VERSION moved to 12 (phase-487 W2). A
generated C service header’s <Srv>_service_init called
rclc_service_init_with_qos with the trampoline and handler in two
positions that function no longer has, because the executor arena owns a
dispatched server’s callback and the struct keeps none. The binding moved
to a new generated <Srv>_executor_add_service over
rclc_executor_add_service_raw. A version-10 or -11 C tree with a service
therefore does not compile against this runtime, so it is refused by
version rather than by a wall of argument-count errors.
RAISED to 13 with NROS_CODEGEN_VERSION (phase-492 W3, RFC-0089
amended 2026-10-12). The C API dropped nros_: every function, type and
constant generated C names took its upstream spelling or rclc_
(docs/reference/c-api-rename-map.tsv), with no forwarders. A version-12 C
tree calls names this runtime no longer defines, so it is refused by
version rather than by a wall of undeclared identifiers. Rust and C++ trees
are unaffected, but the version is one number for all three.
RAISED to 14 with NROS_CODEGEN_VERSION (phase-492 W7/W8, RFC-0089
“Messages take rosidl’s C names AND its member shape” and
“ROSIDL_GET_*_TYPE_SUPPORT adopted as a pass-through”). Generated C
messages take rosidl’s names (std_msgs__msg__String in
<std_msgs/msg/string.h>) and member shape (rosidl_runtime_c__String,
__Sequence, wired by a generated __Storage + __init_in); the type
descriptors rclc_message_type_t / rclc_service_type_t /
rclc_action_type_t gained codec slots and the generated
<pkg>__<sub>__<Name>__nros_type fills them; the typed helpers
(<Msg>_publish, <Msg>_executor_add_subscription*, _subscribe*) and
rclc_executor_add_subscription_typed{,_sized} retired for rcl_publish
and rclc’s six-argument rclc_executor_add_subscription_with_context. A
version-13 C tree names a descriptor layout and entry points this runtime
no longer has, so it is refused by version. Rust and C++ trees are
unaffected, but the version is one number for all three.
Still 14 while NROS_CODEGEN_VERSION moved to 15 (issue 1179, phase-498
W3). Generated RosMessage impls now state MAX_SERIALIZED_BOUND, the
type’s size bound, so a typed subscription is sized from its type on every
backend. The trait const is DEFAULTED (None), so a version-14 tree still
compiles against this runtime and only keeps the image-wide receive size it
had. Nothing it names is gone, so it is accepted.
Was 14 while NROS_CODEGEN_VERSION moved to 16 (issue 1913). Generated
actions now carry the hash each of their five endpoints is keyed on: Rust
impls state RosAction::{CANCEL_GOAL_SERVICE,STATUS_MESSAGE}_HASH, the C
descriptor rclc_action_type_t gained a trailing endpoint_hashes, and C++
action types gained five <Endpoint>_HASH members. All three are ADDITIVE
on the runtime side: the Rust consts default to ACTION_HASH, an older C
designated initializer leaves the trailing member NULL (read as “use
type_hash”), and rclcpp::detail::action_endpoint_hashes<A>() detects the
C++ members. So an older tree still compiles and runs with the single hash
it always had, and is accepted; a NEW tree names a member an older runtime
lacks, which is what the bump refuses.
17 with NROS_CODEGEN_VERSION (phase-501 W1): the C API’s headers moved
from nros/ into rclc/ (<rclc/types.h>, <rclc/detail/cdr.h>,
<rclc/detail/view.h>, …), with no forwarding headers left behind. A
version-16 C tree includes <nros/types.h>, which this runtime no longer
ships, so it cannot compile and is refused by version instead. Rust and C++
trees are unaffected, but the version is one number for all three.
18 with NROS_CODEGEN_VERSION (phase-503 W1): the same move for C++. The
nros-cpp headers left nros/ for rclcpp/, rclcpp/detail/,
rclcpp_action/ and rclcpp_lifecycle/, again with no forwarding headers.
A version-17 C++ tree includes <nros/heap_string.hpp>, which this runtime
no longer ships, so it is refused by version rather than failing to compile.
The range [NROS_CODEGEN_VERSION_MIN, NROS_CODEGEN_VERSION] is expressed to
C and C++ as a SET OF DEFINED SYMBOLS rather than as a comparison — see
nros-build-helpers’ codegen-version anchor — so there is no range check on
that side that could itself be wrong.