[meta-virtualization][PATCH 0/2] image-oci: two do_image_oci bugfixes (missing pm.update(), libgcc/libgcc-initial sysroot collision)
Koen Kooi <[email protected]> Tue, 28 Jul 2026 13:55:14 +0200
| Newsgroups | org.yoctoproject.lists.meta-virtualization |
|---|---|
| Message-ID | <[email protected]> |
I've been working on a prototype for building a rootfs with multiple multi-layer OCI images images, all built from scratch, in one bitbake command. I ran into some issues along the way:
* image-oci never runs "opkg update" or its rpm equivalents, so nothing gets installed
* both libgcc-initial and libgcc want to be in the sysroot
The first issue is straightforward, for the second I leaned heavily on Claude to write a proper python version and explain it better. I'm not fond of the wall of text that ends up with, but this is already the 3rd iteration of trimming it down.
Anyway, This is the first wave, I'm working on other fixes, like mtime influencing base-layer sharing, but that needs more testing and review. I do need to mention how surprisingly small the resulting image recipes are, a big thanks to everyone who has been working on this!
Below is what Claude suggested as coverletter, it is still verbose, but doesn't suffer from the "Is Koen being sarcastic or not?" issue :)
1/2 - image-oci: call pm.update() before pm.install() for multi-layer
package layers
Multi-layer OCI images (OCI_LAYER_MODE = "multi") with a "packages:"
layer silently produce an empty layer: do_image_oci reports success,
but the layer's opkg/rpm install never actually installs anything,
because the per-layer package manager's local index/cache was never
refreshed after write_index() (do_rootfs itself always calls
pm.update() between the two; image-oci's per-layer installer
skipped it). The failure is only visible as a bb.warn(), so the
build looks green while shipping an empty (or partial) layer.
Reproduced with a minimal recipe (base-files+coreutils in a
"packages:" layer): before the fix, the resulting OCI layer blob
contains 0 regular files; after the fix, the same layer contains
272 files and the compressed blob grows from 299 bytes to 7.5 MB.
Full before/after evidence in the patch commit message and in
TEST-CASES.md if you'd like to reproduce it independently.
2/2 - image-oci: drop *-initial sysroot deps in do_image_oci to avoid
libgcc collision
do_image_oci can abort with:
ERROR: <recipe> do_image_oci: The file .../crtbegin.o is installed
by both libgcc and libgcc-initial, aborting
Root cause is in extend_recipe_sysroot's dependency walk: the
SSTATE_EXCLUDEDEPS_SYSROOT exclusion that normally keeps libgcc and
libgcc-initial from landing in the same sysroot only applies when
the *consuming* task is itself do_populate_sysroot. do_image_oci
reaches extend_recipe_sysroot through the generic "any task whose
[depends] contains populate_sysroot gets this prefunc" path
(staging_taskhandler), so that exclusion never fires for it.
This one is sstate-state-dependent -- whether it fires depends on
whether extend_recipe_sysroot's walk actually needs to populate (vs.
skip via sstate/setscene shortcuts) both libgcc and libgcc-initial
for a given recipe/build. A minimal single-package OCI recipe did
not reach libgcc in our testing, but a multi-layer "packages:" recipe
installing a from-source-compiled library with a moderately large
DEPENDS (boost) did: instrumenting the fix confirms it stripping two
"*-initial" do_populate_sysroot nodes out of BB_TASKDEPDATA for that
recipe, i.e. libgcc-initial is a real, reachable node in
do_image_oci's dependency walk for realistic recipes. We were not
able to force the raw abort deterministically within our test
window (a downstream build did hit it deterministically across 6+
clean-sstate attempts on a wider dependency set) -- see the patch
commit message and TEST-CASES.md for exactly what was and wasn't
reproduced, and why the fix is justified regardless.
Signed-off-by: Koen Kooi <[email protected]>