Skip to content

Add Wrynose (Yocto 6.0) support and IoT Edge 1.6.0-rc.1 recipes#211

Closed
jlian wants to merge 9 commits into
Azure:mainfrom
jlian:feat/wrynose-iotedge-16-rc1
Closed

Add Wrynose (Yocto 6.0) support and IoT Edge 1.6.0-rc.1 recipes#211
jlian wants to merge 9 commits into
Azure:mainfrom
jlian:feat/wrynose-iotedge-16-rc1

Conversation

@jlian

@jlian jlian commented Jun 12, 2026

Copy link
Copy Markdown
Member

What this does

This adds Wrynose (Yocto 6.0) support to the layer and a parallel IoT Edge 1.6.0-rc.1 recipe set, without disturbing the existing Scarthgap/1.5 and Kirkstone builds. Wrynose selects 1.6 through PREFERRED_VERSION; Scarthgap and Kirkstone keep 1.5. Version-specific bits live in the versioned .inc/.bb, and the shared .inc files stay common to both versions.

The full Wrynose build is green (iotedge + aziot-edged RPMs produced, libaziot_keys.so installs), and both the wrynose and scarthgap templates parse with 0 errors.

The commits

The branch is organized so each commit stands on its own and bisects cleanly:

  1. Wrynose layer scaffolding (conf templates, fetch.sh, README). LAYERSERIES_COMPAT now lists kirkstone scarthgap wrynose.
  2. The 1.6.0-rc.1 recipes, exactly as the upstream RC ships them (new per-package recipes + auto-generated crates.inc, panic-patches regenerated against the restructured 1.6 Cargo.toml, plus the 1.5 .inc coexistence tweaks). No build fixes here on purpose, so it reads as "here are the recipes."
  3. The Yocto 6.0 build fixes layered on top (see below).
  4. CI wiring so the workflows build and release Wrynose/1.6 alongside Scarthgap/1.5.

The Yocto 6.0 build fixes (commit 3)

Each fix is scoped so it can't regress the Scarthgap/1.5 build, since several of these files are shared:

  • iot-identity-service.inc: 1.6 splits IIS into workspace crates that use { workspace = true } deps/lints, so do_compile needs a synthesized workspace root and the cargo paths pointed at the unpack dir. Yocto 6.0 introduced UNPACKDIR (${WORKDIR}/sources); Scarthgap doesn't have it. So the paths go through a UNPACK_ROOT fallback that resolves to UNPACKDIR on Wrynose and WORKDIR on Scarthgap. The debug-prefix-map for the sibling crate build paths is guarded the same way (it's a no-op on Scarthgap).
  • aziot-keys: set CARGO_INSTALL_LIBRARIES = "1" so the libaziot_keys.so cdylib actually installs. Yocto 6.0 gates .so/.rlib install behind this knob, and aziot-keys has no binary, so without it cargo install finds nothing.
  • aziotd: add -Wno-unused-command-line-argument to CFLAGS. aziotd builds with TOOLCHAIN="clang", and clang errors on the OE linker flags that the rust-cc wrapper bakes into the C compiler during compile-only cc-rs steps (gcc, used by aziot-keys, just ignores them). This keeps -Werror=format-security intact. Service-install paths also go through UNPACK_ROOT.
  • edgelet (iotedge + aziot-edged): pin sysinfo to 0.38. The RC pulls sysinfo 0.39, which needs rustc 1.95, but Wrynose poky ships rustc 1.94.1. 0.38 is the last release before that toolchain bump and is API-compatible with the edgelet call sites, so no source changes. The patch rewrites the edgelet Cargo.toml + Cargo.lock to match the --frozen build.

Also cleans up the lack-of-whitespace-around-assignment QA warnings across the shared .inc files.

Notes

  • The 1.6.0-rc.1 SRCREVs are pinned to the 1.6.0-rc.1 tags, from the prerelease channel.
  • Scarthgap/Kirkstone behavior is unchanged: the version pins keep 1.6 out of those builds, and the shared-inc changes fall back to the original ${WORKDIR} paths when UNPACKDIR isn't defined.

jlian added 9 commits June 12, 2026 12:45
Introduce build infrastructure for the Wrynose (Yocto 6.0) release so the
layer can target it in parallel with the existing Scarthgap (5.0) and
Kirkstone setups, using a per-template model rather than separate branches.

- conf/layer.conf: extend LAYERSERIES_COMPAT to include wrynose.
- conf/templates/wrynose/: new bblayers.conf.sample + local.conf.sample.
  Wrynose splits poky into separate oe-core/bitbake/meta-yocto repos, so the
  layer set and PREFERRED_VERSION pins differ from the combo-poky templates.
- conf/templates/{scarthgap,kirkstone}/local.conf.sample: pin
  PREFERRED_VERSION for the IoT Edge packages to the 1.5.x recipes so the
  existing releases keep selecting 1.5 while wrynose selects 1.6.
- scripts/fetch.sh: support the wrynose split-repo layout (oe-core + bitbake
  + meta-yocto under poky/, meta-poky/meta-yocto-bsp symlinked), retaining the
  combo-poky path for scarthgap/kirkstone.
- README.md: document the supported Yocto/IoT Edge version matrix.
Add a parallel recipe set for IoT Edge 1.6.0-rc.1 (aziot-edge daemon +
aziot-identity-service 1.6.0-rc.1) that coexists with the existing 1.5.x
recipes. Wrynose selects 1.6 via PREFERRED_VERSION; Scarthgap/Kirkstone keep
1.5. Version-specific bits live in the versioned .inc/.bb; truly-common bits
stay in the shared .inc.

- New per-package recipes for iotedge, aziot-edged, aziot-keys, aziotctl,
  aziotd: versioned .bb + .inc + auto-generated -crates.inc (1.6.0-rc.1 from
  the prerelease channel; SRCREVs pinned to the 1.6.0-rc.1 tags).
- Regenerated the panic=abort -> unwind patches against the restructured 1.6
  Cargo.toml ([workspace.dependencies]/[workspace.lints]); dropped the obsolete
  Rust-1.78 dead_code workaround that no longer applies to 1.6.
- aziotd: pre-generated keys.generated.rs (bindgen 0.72.1) for the 1.6 C
  headers, placed in the version-specific FILESPATH dir.
- 1.5 .inc files: move version-divergent patch SRC_URI into the versioned
  includes so 1.5 and 1.6 recipes can coexist cleanly.
Resolve the build breakages that surface when building the 1.6.0-rc.1 recipes
against the Wrynose (Yocto 6.0) toolchain. Each fix is scoped to avoid
regressing the Scarthgap/1.5 build.

iot-identity-service.inc (1.6 IIS split-crate layout):
- Synthesize a minimal IIS workspace root and point EXTRA_OECARGO_PATHS at
  ${UNPACKDIR} (Yocto 6.0 sets UNPACKDIR=${WORKDIR}/sources for fetched
  crates). 1.6 uses { workspace = true } deps/lints across detached-fetched
  crates, which otherwise fail do_compile with "failed to find a workspace
  root" / "failed to read directory".
- Extend DEBUG_PREFIX_MAP to remap the ${UNPACKDIR} prefix so sibling-crate C
  build-script debug paths (openssl-sys2, aziot-key-openssl-engine) don't leak
  into the -dbg package and fail do_package_qa [buildpaths]. Longest-prefix
  match keeps the existing ${S} mapping intact.

aziot-keys: set CARGO_INSTALL_LIBRARIES=1 so the libaziot_keys.so cdylib is
installed to ${rustlibdir} (Yocto 6.0 gates .so/.rlib install behind this);
otherwise cargo_do_install finds nothing and fails.

aziotd: add -Wno-unused-command-line-argument to CFLAGS. aziotd builds with
TOOLCHAIN="clang"; the OE rust-cc wrapper bakes linker flags (-Wl,...) into the
C compiler, and clang errors on them under -Werror during cc-rs compile-only
steps (gcc, used by aziot-keys, ignores them). Keeps -Werror=format-security.
Also point the service-install paths at ${UNPACKDIR} for Yocto 6.0.

edgelet (iotedge + aziot-edged): pin sysinfo to 0.38 (MSRV 1.88). The RC pulls
sysinfo 0.39, which requires rustc 1.95, but Wrynose poky ships rustc 1.94.1.
0.38 is the latest release before the 1.95 bump and is API-compatible with the
edgelet call sites (no source changes). 0002 patch rewrites edgelet
Cargo.toml + Cargo.lock to match the --frozen cargo build.

Also fixes the lack-of-whitespace-around-assignment QA warnings (export VAR=...
-> export VAR = "...") across the shared .inc files.
Wire the GitHub Actions workflows to cover the new Wrynose/1.6 target in
addition to the existing Scarthgap/1.5 build.

- ci-build.yml: add the wrynose template to the build matrix.
- release.yml: produce 1.6.0-rc.1 artifacts for wrynose alongside the 1.5
  release artifacts.
- build-devcontainer.yml: publish the (Yocto-release-agnostic) devcontainer
  image under both scarthgap and wrynose tags so the build workflow can
  reference a per-template tag.
The "Recipe consistency check" CI regenerates the 1.5 recipes with
scripts/update-recipes.sh and fails on any drift. The coexistence change
hand-edited the generated 1.5 incs to add edgelet patch SRC_URI and an
aziotd keys.generated.rs do_compile:prepend, which the generator does not
emit, so the check failed.

Make the generator the source of truth instead of hand-editing its output:

- update-recipes.sh now wires the edgelet source-compatibility patches into
  each generated aziot-edged/iotedge version inc. It does not hardcode the
  list. It wires whatever .patch files exist in the recipe's patch dir for
  that version, mirroring bitbake FILESPATH precedence: the version dir
  recipes-core/<pkg>/<pkg>-<ver>/ first, then the shared files/ dir. That is
  the same order the build uses to resolve file://, so the generated wiring
  matches what the build picks up. Today 1.5.x patches live in files/ and
  1.6.x patches live in the version dir, so each series gets its own set
  (1.5 keeps the Rust 1.78 dead_code workaround, 1.6 does not).

- The aziotd keys.generated.rs prepend moves into the shared aziotd.inc using
  ${UNPACK_ROOT}, which already resolves to ${WORKDIR} on Scarthgap and
  ${UNPACKDIR} on Wrynose. It is the same install for every aziotd version, so
  it belongs in the shared inc and the generated version inc stays at just the
  export VERSION line. This also removes the duplicate prepend the 1.5 and 1.6
  incs each carried.

Patch and asset files stay on disk in their version dirs by hand. The
generator only writes the SRC_URI wiring, so committed recipes match the
generator output and the consistency check stays green. The 1.6.0-rc.1
recipes remain hand-authored because the generator only fetches released
versions; CI regenerates 1.5.35 only, so they do not trip the check.

After running update-recipes.sh --iotedge-version 1.5.35 --skip-validate,
git diff --exit-code recipes-core/ is clean.
The "Recipe consistency check" enforces that recipes are generated, never
hand-edited. The 1.5 (Scarthgap) recipes already come from
scripts/update-recipes.sh, but the 1.6.0-rc.1 (Wrynose) recipes were
hand-authored because the generator only knew the old single-version
(Scarthgap) recipe shape. 1.6 is headed for the LTS channel, so it must be
generator-managed too: then the consistency check covers it and the daily
watch-upstream bot keeps working when LTS flips to 1.6.

Teach the generator the Wrynose shape via a template-driven switch:

- Add 'wrynose' to the allowed --template set. The recipe shape is decided by
  series: scarthgap/kirkstone keep the legacy shape (shared <pkg>-crates.inc
  name, explicit S = "${WORKDIR}/git"); wrynose uses the new shape (per-version
  <pkg>-<ver>-crates.inc so 1.5 and 1.6 coexist in one recipe dir, and no S
  line because Yocto 6.0 sets S = ${UNPACKDIR}/${BP} by default and the old
  explicit value hard-errors). Default template stays scarthgap, so the CI
  consistency step (no --template) is unchanged.

- Resolve the release channel by version instead of hardcoding lts. At the
  1.6.0-rc.1 tag the 1.6 daemon lives in the prerelease channel (lts still
  points at 1.5.21), so the generator now picks the channel whose aziot-edge
  product version matches the requested release, preferring lts and falling
  back to lts when nothing matches. 1.5.35 still resolves to lts -> 1.5.21 /
  1.5.6 exactly as before.

- Compute LIC_FILES_CHKSUM md5s for LICENSE and THIRDPARTYNOTICES from the
  fetched source instead of hardcoding the 1.5 values. THIRDPARTYNOTICES is
  regenerated each release, so its md5 differs by version (1.6 is c613cff9...,
  1.5 stays 11604c61...). Computing it makes the recipe correct for any release.

- Apply each version's source-compatibility patches to the throwaway source
  checkout before reading Cargo.lock for crates.inc. The cargo build runs with
  --frozen, so the fetched crate set is the one in the patched lock. The 1.6
  sysinfo-0.38 pin rewrites edgelet/Cargo.lock (drops the macOS-only objc2
  stack), so the generated crates.inc must reflect that. The 1.5 patches only
  touch Cargo.toml / source, so 1.5 crates.inc is byte-identical.

- Emit the aziot-keys CARGO_INSTALL_LIBRARIES wiring for the wrynose shape
  only. aziot-keys builds a cdylib, and the Yocto 6.0 cargo bbclass needs that
  flag to install the .so. Scarthgap installs it without the flag, so its
  recipe is unchanged.

The 1.6 patch FILES (0001-Remove-panic-abort, 0002-Pin-sysinfo-0.38) and the
keys.generated.rs asset stay hand-crafted in their version dirs. The generator
only wires and applies them, so committed recipes match generator output and
the consistency check stays green.

Normalizations applied to the previously hand-authored 1.6 recipes so they
match generator output:

- iotedge-1.6.0-rc.1.inc / aziot-edged-1.6.0-rc.1.inc: replace the bespoke
  per-patch comment + two separate SRC_URI lines with the generator's standard
  combined patch-wiring block (same two patches, same order, same form 1.5
  uses).

- aziotd-1.6.0-rc.1.inc: drop the informational keys.generated.rs comment; the
  install logic already lives in the shared aziotd.inc, so the version inc is
  just the export VERSION line the generator emits.

- iotedge-1.6.0-rc.1-crates.inc / aziot-edged-1.6.0-rc.1-crates.inc: remove the
  five stale objc2 crates (dispatch2, objc2, objc2-encode, objc2-foundation,
  objc2-open-directory). The committed crates.inc had sysinfo pinned to 0.38.4
  but still listed the objc2 stack that the 0002 patch removes from the lock,
  so it was internally inconsistent with its own patch. The generated file is
  derived from the actual patched lock, which references only
  objc2-core-foundation and objc2-io-kit.

Verified: update-recipes.sh --iotedge-version 1.6.0-rc.1 --template wrynose and
update-recipes.sh --iotedge-version 1.5.35 (default scarthgap) both reproduce
recipes-core/ byte-identically (git diff --exit-code clean).
The build job built only one template per run, chosen from the branch or
tag. On a Wrynose pull request it still built Scarthgap, so a green "Build
and validate" check did not prove that the 1.6 line fetches, builds, and
boots. With 1.5 and 1.6 now shipping as overlapping LTS lines, the check
must cover both.

Make the build job a matrix over Yocto templates:

- Add a set-matrix step in check-changes that outputs the templates to
  build. Pull requests and pushes build both LTS lines (scarthgap for 1.5,
  wrynose for 1.6). Release tags, which release.yml passes as inputs.ref,
  collapse to the single template that matches the tag, so a release does
  not double-build or build the wrong line.
- The build job uses strategy.matrix.template with fail-fast: false and
  passes matrix.template to fetch.sh, build.sh, and validate-qemu.sh, and
  to the per-template artifact names.
- status-check keeps the needs.build.result == 'success' check. A matrix
  job aggregates to success only when every leg succeeds, so a single
  failed line fails the overall check.

Keep kirkstone out of the pull request matrix. It builds the same 1.5
recipe as scarthgap, so a third leg adds build time without testing a
distinct recipe shape. The single template output still maps kirkstone for
any branch or release that needs it.

Name the devcontainer image with a release-neutral tag. The image only
provides host build tools, and OE-core fetches the per-template uninative
and cross toolchain at build time, so the image content does not depend on
the Yocto release. Publish it as :latest and reference :latest for every
matrix leg, which also removes the :wrynose chicken-and-egg (that tag never
existed). build-devcontainer.yml still pushes :scarthgap as a transitional
alias for one publish cycle so existing references keep working during the
rename.

Note: :latest must be published once before the matrix build can pull it.
The .devcontainer files are unchanged here, so build-devcontainer does not
run on this change. A maintainer should run build-devcontainer once via
workflow_dispatch (or merge any later .devcontainer change) to publish
:latest. The :scarthgap alias keeps current builds working until then.
The daily upstream watcher assumed a single recipe line. With 1.5 and 1.6
recipes coexisting in recipes-core/ during LTS overlap, it had three
failures and pulled the wrong devcontainer.

Fix the version comparison crash. compare_versions split versions on "."
and ran arithmetic on each field. A prerelease like 1.6.0-rc.1 produced a
patch field of "0-rc.1", which raised a bash arithmetic syntax error. Under
set -e that killed the whole job. Replace the arithmetic with a small
dependency-free Python comparator that orders by the numeric release first,
then applies the semver rule that a prerelease sorts below its final release
(1.6.0-rc.1 < 1.6.0) and rc.1 < rc.2. Pure 1.5.x numeric comparisons are
unchanged. GNU sort -V is not used for the decision because it orders a
prerelease above its final release, which would make the bot miss an rc to
final upgrade.

Scope the recipe lookup to the tracked line. get_recipe_version and
get_recipe_release picked the highest recipe on disk, which is now
1.6.0-rc.1 even while the lts channel still ships 1.5.x. Derive the
major.minor from the lts channel daemon version and compare against that
line only, so the bot tracks 1.5 today and 1.6 once the channel advances,
without the other line interfering.

Scope update-recipes.sh --clean to the line being regenerated. The clean
deleted every *_*.bb and *-[0-9]*.inc across all versions, which would wipe
the other LTS line on a single-line update. Restrict the delete to the
major.minor derived from the daemon and IIS versions, so regenerating 1.5
never removes 1.6 recipes and vice versa. The recipe consistency check does
not use --clean, and a normal regeneration overwrites the same files, so
generated output stays byte-identical for both lines.

Make the watcher template-aware. check-upstream now emits a template output
(1.6.* maps to wrynose, otherwise scarthgap), and watch-upstream.yml passes
it as --template to update-recipes.sh. The container moves to the
release-neutral :latest tag, since the image provides host tools only and
the Yocto line comes from --template.

Verified with the live product-versions.json (exits 0, tracks the 1.5
line), a unit harness over compare_versions across rc and numeric inputs,
and a simulation of the 1.5 to 1.6 channel transition that confirms the
rc to final upgrade is detected and the correct template is chosen.
@jlian

jlian commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

Superseded by #212. Re-opened as a same-repo branch (Azure/meta-iotedge:feat/wrynose-iotedge-16-rc1) so the self-hosted build runner and the devcontainer image publish work without fork-PR restrictions. Same commits, same content.

@jlian jlian closed this Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant