Supply chain: SBOM and the LLVM version lock

Two related release-mechanics concerns: what a release artifact is made of (the SBOM), and why the LLVM/MLIR major version it links against is pinned (the version lock).

SBOM

Every build emits a CycloneDX 1.5 SBOM describing the release artifacts.

  • Generated by: cmake/GenerateSBOM.cmake, driven by the sbom target, which is part of ALL.
  • Build tree: <build>/llvm-dsdl-sbom.cdx.json
  • Installed to: <prefix>/<datadir>/llvm-dsdl/llvm-dsdl-sbom.cdx.json (component bin)
  • Regenerate on demand: cmake --build <build> --target sbom

Contents

Component Scope Why
llvm-dsdl (metadata component) Tool version (from VERSION), MIT, the shipped tool names, and the exact source commit under pedigree.commits
llvm, mlir required The LLVM/MLIR found by find_package and linked into the tools
zstd required (when found) Pulled in transitively by LLVM
llvm-dsdl-runtime required First-party serialisation runtime shipped as headers/sources alongside generated code
public_regulated_data_types, libudpard excluded Submodules pinned to exact commits. They are build/test inputs (the DSDL corpus; libudpard for the examples) and are not linked into shipped artifacts — recorded for provenance, scoped out of the shipped dependency set

Determinism

The generator emits no wall-clock timestamp and no random serial number, so regenerating from the same commit produces a byte-identical document. For a timestamped document, pass -DSOURCE_DATE_EPOCH_VALUE=<epoch> to the generator.

If the source tree is not a git checkout (e.g. an extracted tarball), the SBOM is still valid — the commit is recorded as unknown and submodule entries are omitted.

LLVM version lock

Policy: the LLVM/MLIR major version is locked to 22.

Rationale

The C backend is routed through MLIR/EmitC, whose printed output can legitimately vary across MLIR majors. The same DSDL compiled against two different MLIR majors may produce C that differs textually while remaining semantically equivalent, which makes the LLVM major a semantic input to the output rather than a build detail.

Enforcement

The lock is enforced at configure time by the build system and again by CI.

  • Build system: after find_package(LLVM/MLIR), CMake asserts LLVM_VERSION_MAJOR equals LLVMDSDL_REQUIRED_LLVM_MAJOR (22) and fails configuration otherwise. -DLLVMDSDL_ALLOW_LLVM_MAJOR_MISMATCH=ON downgrades the failure to a warning. MLIR ships with LLVM at the same version, so asserting the LLVM major covers MLIR too.
  • Everywhere: CI and the release link the toolchain this project builds for itself, pinned by revision in packaging/toolchain/llvm.pin rather than by major alone. Every lane therefore links one exact LLVM, not merely one major of it.
  • Hashed-container iteration is excluded at the source rather than detected afterwards. std::unordered_* iteration order is a property of the standard library, so a loop over one could emit different bytes when built against a different one. tools/determinism/check_unordered_iteration.py (ctest llvmdsdl-determinism-unordered-iteration) rejects any such iteration in the generated-output path unless it carries a stated reason why the order cannot reach emitted text.

  • Cross-architecture determinism gate: the corpus jobs generate the full UAVCAN corpus for all six backends on x86-64 and on arm64, from the same source and the same toolchain, and cross-arch-determinism compares the manifests byte-for-byte with --require-c. Identical input and identical toolchain means a difference could only come from the architecture — word size, alignment, floating-point formatting reaching emitted text.

Comparing two standard libraries instead — libstdc++ against libc++ — is the obvious-looking alternative and does not work here: a project shipping one toolchain has no such divergence to measure. The architecture pair covers what is released. tools/determinism/corpus_determinism.py serves the gate, and is equally the tool for comparing any two builds' corpora by hand.

Consequences

  • Build against the locked major to reproduce released artifacts byte-for-byte.
  • The SBOM records the LLVM/MLIR version linked.
  • To raise the lock: bump LLVMDSDL_REQUIRED_LLVM_MAJOR and packaging/toolchain/llvm.pin together, let the Toolchain workflow republish, then re-baseline the determinism corpus hashes.