feat(idlc): --cyclone defaults un-annotated aggregates to @final - #29
Merged
Conversation
--cyclone was accepted but a codegen no-op, so ZeroDDS still generated @appendable for un-annotated types while CycloneDDS defaults them to @Final. The two then disagree on XCDR2 framing (DHEADER present vs not), and a forced-XCDR2 reader fails to decode the peer's samples (#27). --cyclone now resolves the default extensibility to `final` for un-annotated struct/union/enum, matching CycloneDDS' generator default. Precedence (highest first): explicit IDL annotation > explicit --default-extensibility > --cyclone (final) > global default (appendable). Resolved once after argument parsing, so order is irrelevant; --opendds stays a pure intent no-op. Help text and a precedence test added.
A gated, live DCPS-over-UDP interop harness (domain 100, topic `robot`) pinning down the #27 finding and the --cyclone fix. Five cases, each reporting match / decoded-sample / decode-error counts SEPARATELY: final+XCDR1 -> ZeroDDS @appendable match, samples, 0 errors appendable+XCDR2 -> ZeroDDS @appendable match, samples, 0 errors final+XCDR2 -> ZeroDDS @appendable match, 0 samples, WireError (#27) reverse: ZeroDDS @appendable/XCDR2 -> Cyclone reader samples final+XCDR2 -> ZeroDDS @Final (--cyclone) match, samples, 0 errors Case 3 shows the reporter's symptom is a SURFACED decode error (take() -> WireError), not a silent drop; case 5 shows generating the reader with `--cyclone` (default-final) fixes it. Opt-in / gated: loud-skips (exit 0) without a Python that can import cyclonedds. Reference vendor CycloneDDS 11.0.1. Verified end-to-end on the Linux test host (all five PASS). The reader lives in a standalone crate (own [workspace]); its src/robot.rs is regenerated per case by run_matrix.sh (git-ignored).
The comments on `reader_accept_repr` and its regression test claimed the XTypes 1.3 §7.6.2 default (empty) DataRepresentation policy "accepts both XCDR1 and XCDR2". The spec default is XCDR1 only. Advertising both is ZeroDDS' own deliberate interop choice (so an XCDR1-defaulting CycloneDDS/ RTI/OpenDDS writer still matches), not the meaning of the spec default. Comment-only; the advertise-both behaviour is unchanged.
) A decode failure at take() now reports the received encapsulation (representation + byte order) and this reader type's own extensibility, and names an extensibility/framing mismatch as a *plausible* cause: in XCDR2 @appendable/@mutable carry a DHEADER length prefix and @Final does not, so a peer with a different extensibility fails to decode. Flagged as plausible, not asserted — the remote type is not available at the decode site to confirm it. Applied at the five representation-dispatched decode_for_encap call sites via a shared decode_wire_error helper. Test added; the advertise/decode behaviour is otherwise unchanged.
SandraK82
added a commit
that referenced
this pull request
Aug 3, 2026
…#28) The all-core Fast DDS source build (XTypes/TypeObject TUs are memory-heavy) peaked past the hosted-runner limit and the runner was SIGTERM'd twice at ~54% (exit 143) on the unchanged SHA — a resource limit, not transient infra. Cap the build to 2 jobs (CMAKE_BUILD_PARALLEL_LEVEL + gradle --max-workers) so the peak fits, add actions/cache keyed on the pinned versions so the stack builds once, and set a 60-minute job timeout for a clean failure mode. Cyclone cell is already green (both directions + the #29 XTypes cases).
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.
Related to #27.
CycloneDDS treats an un-annotated aggregate as
@final; ZeroDDS follows the XTypes 1.3 default of@appendable. Under XCDR1 this is invisible — an@appendabletype emits no DHEADER, so the bytes match@finaland everything interoperates. Under XCDR2 the@appendablereader expects a DHEADER the@finalwriter never wrote, so every sample fails to decode. The endpoints still match (cross-vendor matching falls back to the type name; complete standard TypeObject validation is not reached here), so it presents as "matched but no data".Changes
--cyclonenow defaults un-annotated aggregates to@final(matching CycloneDDS' own default). Previously the flag was accepted but a codegen no-op. A reader generated with--cyclonenow interoperates with a CycloneDDS@final/XCDR2 writer out of the box.take()surfaces the framing mismatch as aWireErrorwith decode diagnostics instead of a generic error, so the failure is visible rather than dropped on the floor.interop/cyclone-xtypes-27/) covering the default XCDR1 path, the forced-XCDR2 failure, and the--cyclonefix, against CycloneDDS 11.0.1.Validation (5-case matrix, CycloneDDS 11.0.1)
--cyclone)Workaround without this PR: annotate the type
@final, or generate with--default-extensibility final.This does not close #27: the reporter's default (XCDR1) path failure is not reproducible here and needs more data — leaving the issue open for that.