Hey all,
I wanted to share a personal experiment I've been running: an exploratory migration of the LibreELEC build pipeline to a Yocto/BitBake-based system.
Repository: https://github.com/adam-aph/LibreELEC.new-30-March-2026.git
The branch is based on the master libreelec.tv cut from March 30. This started as a private experiment - my excuse to work with Claude Code on a larger, real-world codebase - so temper expectations accordingly.
🔧 WHY BOTHER?
For those curious whether there's any genuine technical merit here (beyond "Yocto is what the cool kids use"), here's what actually changes for a codebase like LibreELEC:
- Cryptographic task signatures replace fragile .stamp_* files
Changing an ffmpeg configure option, patch, or dependency in package.mk triggers precisely the affected downstream packages (Kodi, gstreamer plugins, etc.) - no more volunteers reaching for rm -rf build.* to restore correctness, and no more PKG_NEED_UNPACK hacks. - sstate-cache gives reliable task-level binary reuse across machines and CI
Tweaking one kernel config or driver only rebuilds the delta, instead of re-executing large chunks of the 5000+ recipe tree that the current linear stamp logic often invalidates unnecessarily. - Task-level DAG dependency modeling eliminates build races
PKG_DEPENDS_TARGET + make -jN has inherent race conditions when multiple packages populate sysroot headers/libraries concurrently. BitBake replaces shell sequencing with mathematically enforced ordering, down to do_populate_sysroot vs do_configure. - Isolated native (-native) sysroots hermetically seal the toolchain and host tools
cmake, pkg-config, sed, gawk, Python - all of it. This removes the occasional host contamination that leaks through the current packages/toolchain/ wrapper across varied volunteer distros (Arch, Ubuntu, NixOS, etc.). - Layer + bbappend architecture cleanly separates BSP/vendor specifics
Device trees, kernel patches (RPi, Rockchip, Amlogic) and conditional logic move out of core packages/linux/ and packages/mesa/ into isolated vendor layers. Dropping an old SoC becomes a layer deletion rather than untangling shell if [ "$PROJECT" = ... ] blocks scattered across hundreds of package.mk files. - PACKAGECONFIG provides declarative, graph-aware feature toggles
Global flags like pulseaudio, lirc, various codecs are automatically wired with their dependencies and configure flags across recipes - replacing the current manual threading of SUPPORT_*=yes through per-package shell conditionals in make_target() and options files. - module.bbclass and kernel-devsrc formalize out-of-tree driver builds
Correct kernel tree preparation, KDIR, headers, and module packaging/stripping for WiFi dongles and proprietary drivers - instead of fragile sequencing scripts in packages/linux-drivers/ that depend on exact prior kernel unpack/configure timing. - Reproducible builds become an auditable property, not volunteer discipline
sstate + the dependency graph make it easier to verify bit-identical images across machines without relying on stamp + shell environment assumptions.
These are codebase-specific wins. That said - for a small volunteer team where the shell system is already well-understood, highly optimized for Kodi-centric embedded use, and operationally successful, the migration cost is massive. The strongest drivers are incremental correctness, host isolation, and long-term BSP maintainability as hardware targets grow.
⚠️ CURRENT STATE & CAVEATS
• Converted 42 arch-variants across 28 PROJECT/DEVICE combinations.
• Clean build achieved: libreelec-generic-x86-64 only
• Not tested on any physical device (it will very likely fail)
• Getting to a clean x86_64 build took roughly 100 hours working with Claude Code - this is not a weekend project
🤔 IS THERE INTEREST?
Honestly, I'm not sure this brings value for you - maybe it does. I'm sharing it in case someone sees potential here, or wants to pick it up.
Realistically, continuing this properly would mean sponsored API tokens, access to physical hardware for testing, and significant time investment. That's essentially a part-time (or full-time) job, which isn't where I am right now.
That said, I'm happy to discuss it. If you see value in this direction, think it's worth pursuing, or have thoughts on what "continued" would even look like in practice - feel free to DM me. No promises, but I'm not closing the door either.
Cheers!
Build:
$ time kas build kas/machines/libreelec-generic-x86-64.yml --target libreelec-image 2>&1 | tee /tmp/libreelec-image-build.log
2026-05-22 10:58:11 - INFO - kas 5.2 started on Ubuntu 24.04
2026-05-22 10:58:11 - INFO - Cloning repository poky
2026-05-22 10:58:11 - INFO - Cloning repository meta-openembedded
2026-05-22 10:58:33 - INFO - Repository meta-openembedded already contains 1ad0d777d1de1769e5995eb806f7ae5c15d0be54 as commit
2026-05-22 10:59:11 - INFO - Repository poky already contains 8643f911602949518c5c5474726b49f943e36b83 as commit
2026-05-22 10:59:11 - INFO - Repository poky checked out to 8643f911602949518c5c5474726b49f943e36b83
2026-05-22 10:59:12 - INFO - Repository meta-openembedded checked out to 1ad0d777d1de1769e5995eb806f7ae5c15d0be54
2026-05-22 10:59:14 - INFO - Loading cache...done.
...
2026-05-22 10:59:55 - INFO - done.
2026-05-22 10:59:56 - INFO - Parsing of 3338 .bb files complete (0 cached, 3338 parsed). 5229 targets, 599 skipped, 214 masked, 0 errors.
2026-05-22 10:59:56 - INFO - NOTE: Resolving any missing task queue dependencies
2026-05-22 10:59:56 - INFO - NOTE: Multiple providers are available for runtime busybox (busybox, busybox-init)
2026-05-22 10:59:56 - INFO - Consider defining a PREFERRED_RPROVIDER entry to match busybox
2026-05-22 10:59:58 - INFO -
2026-05-22 10:59:58 - INFO - Build Configuration:
2026-05-22 10:59:58 - INFO - BB_VERSION = "2.8.1"
2026-05-22 10:59:58 - INFO - BUILD_SYS = "x86_64-linux"
2026-05-22 10:59:58 - INFO - NATIVELSBSTRING = "ubuntu-24.04"
2026-05-22 10:59:58 - INFO - TARGET_SYS = "x86_64-oe-linux"
2026-05-22 10:59:58 - INFO - MACHINE = "libreelec-generic-x86-64"
2026-05-22 10:59:58 - INFO - DISTRO = "libreelec"
2026-05-22 10:59:58 - INFO - DISTRO_VERSION = "12.0"
2026-05-22 10:59:58 - INFO - TUNE_FEATURES = "m64"
2026-05-22 10:59:58 - INFO - TARGET_FPU = ""
2026-05-22 10:59:58 - INFO - meta-libreelec-bsp
2026-05-22 10:59:58 - INFO - meta-libreelec-distro
2026-05-22 10:59:58 - INFO - meta-libreelec-kodi = "<unknown>:<unknown>"
2026-05-22 10:59:58 - INFO - meta-filesystems
2026-05-22 10:59:58 - INFO - meta-multimedia
2026-05-22 10:59:58 - INFO - meta-networking
2026-05-22 10:59:58 - INFO - meta-oe
2026-05-22 10:59:58 - INFO - meta-python = "scarthgap:1ad0d777d1de1769e5995eb806f7ae5c15d0be54"
2026-05-22 10:59:58 - INFO - meta
2026-05-22 10:59:58 - INFO - meta-poky = "scarthgap:8643f911602949518c5c5474726b49f943e36b83"
2026-05-22 10:59:58 - INFO -
2026-05-22 10:59:59 - INFO - Initialising tasks...Sstate summary: Wanted 2037 Local 0 Mirrors 0 Missed 2037 Current 0 (0% match, 0% complete)
2026-05-22 11:00:00 - INFO - done.
2026-05-22 11:00:00 - INFO - NOTE: Executing Tasks
2026-05-22 11:00:00 - INFO - NOTE: Running task 1 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-devtools/quilt/quilt-native_0.67.bb:do_recipe_qa)
2026-05-22 11:00:00 - INFO - NOTE: Running task 2 of 4469 (virtual:native:/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-devtools/gnu-config/gnu-config_git.bb:do_recipe_qa)
2026-05-22 11:00:00 - INFO - NOTE: Running task 3 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-extended/texinfo-dummy-native/texinfo-dummy-native.bb:do_recipe_qa)
2026-05-22 11:00:00 - INFO - NOTE: Running task 4 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-core/gettext/gettext-minimal-native_0.22.5.bb:do_recipe_qa)
2026-05-22 11:00:00 - INFO - NOTE: Running task 5 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-devtools/m4/m4-native_1.4.19.bb:do_recipe_qa)
2026-05-22 11:00:00 - INFO - NOTE: Running task 6 of 4469 (virtual:native:/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-devtools/autoconf/autoconf_2.72e.bb:do_recipe_qa)
2026-05-22 11:00:00 - INFO - NOTE: Running task 7 of 4469 (virtual:native:/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-devtools/automake/automake_1.16.5.bb:do_recipe_qa)
2026-05-22 11:00:00 - INFO - NOTE: Running task 8 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../poky/meta/recipes-devtools/libtool/libtool-native_2.4.7.bb:do_recipe_qa)
...
2026-05-22 14:30:22 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_image_squashfs: Started
2026-05-22 14:30:22 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_rootfs_wicenv: Started
2026-05-22 14:30:22 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_rootfs_wicenv: Succeeded
2026-05-22 14:30:39 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_image_squashfs: Succeeded
2026-05-22 14:30:39 - INFO - NOTE: Running task 4466 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../meta-libreelec-distro/recipes-images/libreelec-image.bb:do_image_wic)
2026-05-22 14:30:40 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_image_wic: Started
2026-05-22 14:30:54 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_image_wic: Succeeded
2026-05-22 14:30:54 - INFO - NOTE: Running task 4467 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../meta-libreelec-distro/recipes-images/libreelec-image.bb:do_image_complete)
2026-05-22 14:30:55 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_image_complete: Started
2026-05-22 14:30:56 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_image_complete: Succeeded
2026-05-22 14:30:56 - INFO - NOTE: Running task 4468 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../meta-libreelec-distro/recipes-images/libreelec-image.bb:do_populate_lic_deploy)
2026-05-22 14:30:57 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_populate_lic_deploy: Started
2026-05-22 14:30:57 - INFO - NOTE: recipe libreelec-image-1.0-r0: task do_populate_lic_deploy: Succeeded
2026-05-22 14:30:57 - INFO - NOTE: Running noexec task 4469 of 4469 (/home/adamh/cc-le-conv/LibreELEC.new/build/../meta-libreelec-distro/recipes-images/libreelec-image.bb:do_build)
2026-05-22 14:30:58 - INFO - NOTE: Tasks Summary: Attempted 4469 tasks of which 0 didn't need to be rerun and all succeeded.
2026-05-22 14:30:58 - INFO -
2026-05-22 14:30:58 - INFO - Summary: There were 483 WARNING messages.
real 212m47.967s
user 1m5.970s
sys 0m13.694s
Display More
Result:
303M May 22 14:30 LibreELEC-Generic.x86_64-12.0.img.gz
1M May 22 14:30 LibreELEC-Generic.x86_64-12.0.img.gz.sha256
1M May 22 14:30 LibreELEC-Generic.x86_64-12.0.manifest
275M May 22 14:30 LibreELEC-Generic.x86_64-12.0.squashfs
1M May 22 14:30 LibreELEC-Generic.x86_64-12.0.testdata.json
549M May 22 14:30 LibreELEC-Generic.x86_64-12.0.wic
303M May 22 14:30 LibreELEC-Generic.x86_64-12.0.wic.gz
1M May 22 14:30 libreelec-image.env
Complete Target Matrix - 42 arch-variants across 28 PROJECT/DEVICE combinations:
| PROJECT | DEVICE | ARCH | KERNEL | BOOTLOADER | DISPLAYSERVER | GRAPHIC_DRIVERS | patch_arch | Notes |
|---|---|---|---|---|---|---|---|---|
| Generic | Generic | x86_64 | default | syslinux | no | crocus i915 iris r300 r600 radeonsi vmware virtio | x86 | GBM/DRM; OpenGL=mesa |
| Generic | Generic-legacy | x86_64 | default | syslinux | x11 | crocus i915 iris radeonsi nvidia vmware virtio | x86 | X11/Fluxbox; OpenGL=mesa |
| Generic | gbm | x86_64 | default | syslinux | no | crocus i915 iris r300 r600 radeonsi vmware virtio | x86 | GBM/DRM (same as Generic) |
| Generic | wayland | x86_64 | default | syslinux | wl | crocus i915 iris nvidia-ng r300 r600 radeonsi vmware virtio | x86 | Wayland/Sway; OpenGLES=mesa |
| Generic | x11 | x86_64 | default | syslinux | x11 | crocus i915 iris radeonsi nvidia vmware virtio | x86 | X11/Fluxbox; OpenGL=mesa |
| RPi | RPi | arm | raspberrypi | bcm2835-bootloader | no | vc4 | arm | RPi 1/Zero; arm1176jzf-s; KERNEL_TARGET=zImage |
| RPi | RPi2 | arm | raspberrypi | bcm2835-bootloader | no | vc4 | arm | RPi 2/3; cortex-a7; KERNEL_TARGET=zImage |
| RPi | RPi4 | aarch64 | raspberrypi | bcm2835-bootloader | no | vc4 | aarch64 | cortex-a72; KERNEL_TARGET=Image |
| RPi | RPi4 | arm | raspberrypi | bcm2835-bootloader | no | vc4 | aarch64 | arm32 on arm64 kernel; cortex-a53; TARGET_KERNEL_PATCH_ARCH=aarch64 |
| RPi | RPi5 | aarch64 | raspberrypi | bcm2835-bootloader | no | vc4 | aarch64 | cortex-a76; KERNEL_TARGET=Image |
| RPi | RPi5 | arm | raspberrypi | bcm2835-bootloader | no | vc4 | aarch64 | arm32 on arm64 kernel; cortex-a53; TARGET_KERNEL_PATCH_ARCH=aarch64 |
| Amlogic | AMLGX | aarch64 | amlogic | u-boot | no | lima panfrost | aarch64 | cortex-a53; OPENGLES=mesa |
| Amlogic | AMLGX | arm | amlogic | u-boot | no | lima panfrost | aarch64 | arm32 on arm64 kernel; cortex-a53 |
| Rockchip | RK3288 | arm | default | u-boot | no | panfrost | arm | cortex-a17; arm-only SoC |
| Rockchip | RK3328 | aarch64 | default | u-boot | no | lima | aarch64 | cortex-a53; ATF |
| Rockchip | RK3328 | arm | default | u-boot | no | lima | aarch64 | arm32 on arm64 kernel; cortex-a53 |
| Rockchip | RK3399 | aarch64 | default | u-boot | no | panfrost | aarch64 | cortex-a72.cortex-a53; ATF |
| Rockchip | RK3399 | arm | default | u-boot | no | panfrost | aarch64 | arm32 on arm64 kernel; cortex-a72.cortex-a53 |
| Rockchip | RK356X | aarch64 | rockchip | u-boot | no | panfrost | aarch64 | cortex-a55; LINUX=rockchip |
| Rockchip | RK356X | arm | rockchip | u-boot | no | panfrost | aarch64 | arm32 on arm64 kernel; cortex-a55 |
| Rockchip | RK3576 | aarch64 | rockchip | u-boot | no | panfrost | aarch64 | cortex-a72.cortex-a53; LINUX=rockchip |
| Rockchip | RK3576 | arm | rockchip | u-boot | no | panfrost | aarch64 | arm32 on arm64 kernel |
| Rockchip | RK3588 | aarch64 | rockchip | u-boot | no | panfrost | aarch64 | cortex-a76.cortex-a55; LINUX=rockchip |
| Rockchip | RK3588 | arm | rockchip | u-boot | no | panfrost | aarch64 | arm32 on arm64 kernel |
| Allwinner | A64 | aarch64 | default | u-boot | no | (mesa via OPENGLES) | aarch64 | cortex-a53; ATF+crust |
| Allwinner | A64 | arm | default | u-boot | no | (mesa via OPENGLES) | aarch64 | arm32 on arm64 kernel; cortex-a53 |
| Allwinner | H2-plus | arm | default | u-boot | no | (mesa via OPENGLES) | arm | cortex-a7; arm-only; UBOOT_FIRMWARE=crust |
| Allwinner | H3 | arm | default | u-boot | no | (mesa via OPENGLES) | arm | cortex-a7; arm-only; UBOOT_FIRMWARE=crust |
| Allwinner | H5 | aarch64 | default | u-boot | no | (mesa via OPENGLES) | aarch64 | cortex-a53; ATF+crust |
| Allwinner | H5 | arm | default | u-boot | no | (mesa via OPENGLES) | aarch64 | arm32 on arm64 kernel; cortex-a53 |
| Allwinner | H6 | aarch64 | default | u-boot | no | (mesa via OPENGLES) | aarch64 | cortex-a53; ATF+crust (sun50i_h6) |
| Allwinner | H6 | arm | default | u-boot | no | (mesa via OPENGLES) | aarch64 | arm32 on arm64 kernel; cortex-a53 |
| Allwinner | R40 | arm | default | u-boot | no | (mesa via OPENGLES) | arm | cortex-a7; arm-only; no ATF |
| NXP | iMX6 | arm | default | u-boot | no | etnaviv | arm | cortex-a9; arm-only |
| NXP | iMX8 | aarch64 | default | u-boot | no | etnaviv | aarch64 | cortex-a53; ATF+firmware-imx |
| NXP | iMX8 | arm | default | u-boot | no | etnaviv | aarch64 | arm32 on arm64 kernel; cortex-a53 |
| Qualcomm | Dragonboard | aarch64 | default | u-boot | no | freedreno | aarch64 | cortex-a53; mkbootimg host tool |
| Qualcomm | Dragonboard | arm | default | u-boot | no | freedreno | aarch64 | arm32 on arm64 kernel; cortex-a53 |
| ARM | ARMv7 | arm | default | u-boot | no | lima | arm | Generic ARMv7 reference; cortex-a8 |
| ARM | ARMv8 | aarch64 | default | u-boot | no | lima | aarch64 | Generic ARMv8 reference; cortex-a53 |
| ARM | ARMv8 | arm | default | u-boot | no | lima | aarch64 | Generic ARMv8 reference; arm32 on arm64 kernel |
| Samsung | Exynos | arm | samsung | u-boot | no | panfrost | arm | cortex-a15.cortex-a7; LINUX=samsung |