build: bring packages inline with LE where possible - #3201
Draft
sunshineinabox wants to merge 119 commits into
Draft
build: bring packages inline with LE where possible#3201sunshineinabox wants to merge 119 commits into
sunshineinabox wants to merge 119 commits into
Conversation
sunshineinabox
force-pushed
the
ng-macbuild
branch
7 times, most recently
from
August 17, 2026 04:42
091ab2b to
9422b6a
Compare
sunshineinabox
marked this pull request as ready for review
August 17, 2026 05:47
sunshineinabox
force-pushed
the
ng-macbuild
branch
4 times, most recently
from
August 17, 2026 17:37
55f2b4f to
e322cc9
Compare
HOST_CFLAGS hardcoded -march=x86-64 -mtune=generic, so every host package configure on an aarch64 build machine (native arm64 CI, Apple Silicon Docker) failed with: cc1: error: unknown value 'x86-64' for '-march' Pick the baseline from uname -m instead: x86-64/generic on x86_64, the equivalent armv8-a baseline on aarch64, and no -march elsewhere. Host builds stay portable across CPUs on both architectures.
Re-derive the project override from the LE 2.44 recipe. ROCKNIX deltas kept: pigz:host dep, +bfd -gold, -O3, per-device --enable-kernel (RK3588 6.1 BSP), all glibc programs shipped, writable ld.so.cache config. Vendor LE's five 2.44 build-fix patches (shadow dir replaces core, so they no longer applied). Drop OPEN_TREE guard patch (upstream d12b017cddfe in 2.44). Rebase dns-with-broken-routers onto the in6addr refactor and regenerate the aarch64 vector-types guard as a minimal 3-condition diff (same __ARM_VEC_MATH_DISABLED semantics).
Slim the override to the real ROCKNIX deltas: libatomic enabled and shipped on aarch64 (core only does arm/riscv64) and libgomp added to the target runtime libs. Everything else now inherits from the core recipe, including SANITIZER_SUPPORT gating: release images no longer ship asan/ubsan runtimes (previously copied unconditionally). Vendored patches are byte-identical to core's 16.2 set.
cbindgen, mtdev and swaybg overrides were byte-identical to the core recipes apart from an older version pin; remove them. wlr-randr keeps its profile.d file but sources the core recipe instead of pinning.
The scc.h removal (Jakub Jelinek, gcc r16 cherry-pick of LLVM 3dc4fd6dd411) is included in the 16.2.0 release; with the project override now following core 16.2.0 the patch detects as previously applied and aborts unpack.
gcc 16.2 -Werror=stringop-overflow false positive in locale/programs/ld-ctype.c (last_charcode, offset 23 into size 16) breaks the 2.44 build at -O3 on armv8a. glibc's -Werror is a maintainer QA gate for their own compiler matrix; disable it for distro builds so compiler-upgrade warning churn cannot fail us.
Enable MOLD_SUPPORT and make mold the default target linker. Proven across full SM8250 arm+aarch64 builds on ng: ~700 packages link with mold; the only genuine incompatibility is -Ttext-segment (box86/ box64, opted out via -mold) and glibc pins +bfd by recipe. gcc grows a mold:host toolchain dep automatically.
Both link with -Wl,-Ttext-segment to place themselves outside the emulated guest address space; mold parses that as a linker-script path (mold: fatal: cannot open text-segment) and has no equivalent of the option. Opt out of the mold default via PKG_BUILD_FLAGS.
Port of the Autoremove-branch fix: the unconditional tail copied system/box86.box86rc from the arm sub-build's build directory, which AUTOREMOVE deletes once the arm pass finishes, failing the aarch64 image install. Install it into install_pkg during the arm pass and let the aarch64 pass consume install_pkg only.
No LTO: mesa's meson.build hard-errors on b_lto builds (bug reports from LTO builds are rejected upstream); injecting -flto via CFLAGS merely bypasses that guard, producing an unsupported configuration.
Upstream supports LTO builds: loader/CMakeLists.txt compiles its asm_offset generator with -fno-lto specifically so the remaining targets can be built with LTO enabled.
Fixes the build against glibc 2.44, which declares call_once and once_flag in stdlib.h (C23) and collided with waffle's bundled C11-threads emulation (fixed upstream in 6b3c4648 and faa301a9, both contained in this snapshot). Validated: package builds and installs against the aarch64 glibc 2.44 sysroot.
The placeholder-disable patch commented out the hidden-notification rendering block but left hidden_count/total_notifications and their increments behind; gcc 16 fails the -Werror build with set-but-unused. Rewrite the patch to delete the block and the orphaned counters (the max_visible continue path is preserved).
gcc 16 rejects the mixed designated/positional initializers in lightrec_map (mednafen/psx/cpu.cpp); backport upstream 2b8fbd60a3 which designates the three memory-mirror entries via the PSX_MAP_MIRROR* enums (present in our vendored lightrec). Verified: core builds and links against the ng toolchain.
libstdc++ 16 exposes the C++20 std::numbers namespace via <string>; combined with the core's using namespace std, the file-static numbers array in statusline.cpp becomes ambiguous. Backport upstream 4c7e3a86e2 which renames it to td_numbers. Verified: core builds and links.
The new LE base ships SDL3; wx 3.3 autodetects it for the wxSound backend and bakes SDL3::SDL3-shared into its exported cmake targets. Any SDL2 consumer that also links wx (cemu-sa) then fails configure with SDL's own guard: INTERFACE_SDL_VERSION "SDL3" vs "SDL2" on the same binary. Nothing in the tree uses wxSound; disable wx's SDL usage so its targets stay SDL-free. Verified: rebuilt wx exports contain no SDL3 reference.
Port of aa587bf815c1 (Mac/aarch64 host support): the aarch64-host nix closure ships the x86_64 guest-thunk clang under its cross-prefixed name, but the LibraryForwarding descriptor references bare clang, so the guest-libs configure fails with CMAKE_C_COMPILER not found. Rewrite the descriptor before cmake_enable_libfwd.sh generates the toolchain files; x86 hosts are unaffected (uname guard).
gstvideodmabufpool.h (new in 1.28, included from the video.h aggregate) needs the complete GstVideoBufferPool declarations, but gstvideopool.h included the aggregate before making them: any TU including gstvideopool.h directly (vlc's gstreamer module does) re-enters through the aggregate, hits the include guard, and fails with unknown/incomplete type errors. Declare the bufferpool types before the aggregate include (prelude only, for GST_VIDEO_API) and move the GstVideoAlignment helpers after it. Unfixed upstream; to be submitted as a GStreamer MR. Verified: gst-plugins-base and the vlc gstreamer plugin both build.
The shadow pinned a 2023 git hash, 1.13.1, three releases behind the 1.16.0 core carries in addon-depends. Take core's release tarball and checksum, verified by download, and keep the host build the shadow exists for. This does not explain the H700 configure failure. libvpx's nasm section alignment probe is byte identical in 1.13.1, 1.15.0 and 1.16.0, and nasm 3.02 passes it in the CI image itself, so that failure lies elsewhere.
The shadow was core's package minus libnl, libxml2 and readline, a trim that was valid at 2.6.4. 2.9.2 checks libnl unconditionally at configure time, flips junction support (libxml2) and nfsdctl (readline) to default-on, and the shadow neither declared nor disabled any of them - so a clean sysroot fails configure outright and a populated one only works if unrelated packages happen to build first. Core's copy declares all three and also removes the libtool rpath. Nothing else in the shadow differed from core, so delete it rather than patch it.
The shadow replaces core's package dir wholesale, and core's patches/ never apply to a shadowed package - so the shadow silently dropped the patch core added for wlroots 0.20+, which includes the generated enum headers directly via <wayland-protocols/...-enum.h> and needs Cflags in the dataonly .pc. Builds pass today only because the cross toolchain falls back to the sysroot include path; any consumer resolving includes strictly through pkg-config regresses. Copy the patch into the shadow.
The bump left libvpx.so.6 pointing at libvpx.so.8.0.1, the concrete filename of 1.13.x; the soname moved on at 1.14, so 1.16.0 installs no such file and the image shipped a dangling symlink on the one path Steam loads it from. Resolve the installed library at install time instead of hardcoding a filename, and die loudly if there is nothing to point at.
The shadow pinned strace per device family (6.17/6.18/6.19, whichever was current when each device was brought up) with patches regenerated per pin - bring-up cruft, not policy. Core is at 7.1 and its two autoreconf patches apply cleanly to the 7.1 tarball (verified with git apply against the release, whose checksum matches core's). Delete the shadow so every device tracks core.
Every device except the SM and AMD64 families was pinned to fmt 9.1.0 with an empty PKG_SHA256, so the fetch was unverified and the hold predates the rebase - bring-up residue from the device-init era, with no recorded rationale. Core carries 12.2.0; let every device track it. The in-tree consumers (cemu, daedalusx64, dolphin, easyrpg) build on the devices that already used core's version, and CI proves the rest.
avahi, mesa-demos, pulseaudio, libglvnd and libpthread-stubs fetch the identical version and URL core fetches, but carried no PKG_SHA256, so the downloads went unverified for no reason. Copy core's values back.
The shadow existed to override PKG_SHA256 while codeberg's regenerated archive bytes disagreed with core's record; core has since recorded the same value the shadow carries, so the whole directory is inert.
The knob existed for fex-emu's 0010 patch, which was dropped once the thunk failures were root-caused to foreign-arch includes in the guest parse. Nothing in the tree defines __ARM_VEC_MATH_DISABLED anymore, so the patch guarded a define with no definer.
Adding freedreno to the host vulkan-drivers made mesa's meson require glslangValidator at configure time, which nothing put in the toolchain: every CI leg that builds mesa:host on a clean tree fails with "Program 'glslangValidator' not found". Local builds passed only because an incremental toolchain already carried the binary - the same stale-sysroot class the ledger documents. glslang:host installs the binary into the toolchain, and imagination alone never needed it, which is why core's dep list didn't.
The nasm shadow deletion audited every line it removed but not the line it did not have: core pins PKG_ARCH="x86_64", and scripts/build exit-0s any package whose PKG_ARCH excludes the target arch, so on every aarch64 device nasm:host silently never built. libvpx:host assembles x86_64 host code on all target arches and died at configure with 'nasm: not found' - the failure the config.log archiving finally surfaced after weeks of a misleading 'nasm too old' message. Every emu-standalone leg on run 31774244853 failed on it. Restore the shadow as a one-line PKG_ARCH unlock over core's package.
First x86_64-host CI build of the rewritten thunk stack exposed the
gap the ledger predicted: THUNKGEN_EXTRA_FLAGS hands the host parse
the aarch64 sysroot and libstdc++, but never a target, so on an
x86_64 build host clang parses those headers as x86_64 - SVE types in
bits/math-vector.h come back unknown and every generated host_layout
carries the wrong ABI, which the .inl compiles reject ('no viable
overloaded ='). On aarch64 build hosts the default target happened to
match, which is why every Mac build passed. Pass --target=TARGET_NAME
explicitly; the guest parse appends its own target and sysroot after
the CLI flags, so it is unaffected, same as the --sysroot design.
The image shipped /etc/hostname as a symlink to /proc/sys/kernel/ hostname. hostnamed reads that file as the STATIC hostname, and systemd 261 enforces static over transient - so network-base-setup's transient device name (system.hostname, e.g. RK3566) was refused with 'static hostname is already set' and the box stayed ROCKNIX. The symlink made it self-referential: whatever fallback PID1 applied at early boot read back as static policy. Verified live on an RG-DS. Downstream, samba's netbios name and avahi's mDNS name both default to the hostname, so network browsing lost the device name too - the regression that surfaced this. Drop /etc/hostname entirely (hostnamed: no file = no static hostname, transient accepted), read $HOSTNAME from the kernel in the profile, and keep ROCKNIX as the pre-configuration identity the sanctioned way: -Dfallback-hostname=ROCKNIX, which names the box at early boot without counting as static.
pipewire-pulse runs as root on the system bus, and no policy granted the org.pulseaudio.Server name, so every boot logged 'Failed to acquire org.pulseaudio.Server ... AccessDenied' (seen on RG-DS) and name-based pulse clients could not discover the server; socket-path clients were unaffected, which is why audio still worked. Ship a dbus policy allowing root to own the name and everyone to talk to it - the system-bus counterpart of what a system-wide pulseaudio install ships for its pulse user.
systemd 258+ sets up StateDirectory= with symlink-refusing semantics, and ROCKNIX's tmpfiles symlinks /var/lib/systemd/rfkill onto /storage for persistence across the tmpfs /var - so under 261.2 the unit dies at step STATE_DIRECTORY with 'Too many levels of symbolic links' (observed on SM8250 and SM8550; every device is affected, the RK3566 check just raced the socket trigger). rfkill.c hardcodes the /var/lib/systemd/rfkill/ path in 261.2 exactly as in 255.8 and follows the symlink happily, so a drop-in clearing StateDirectory= restores the 255 behavior without giving up /storage persistence. The tmpfiles audit found no other symlinked state directory.
Dolphin's bundled static SDL2 probes the sysroot at configure time and enables its pipewire audio driver whenever the headers happen to be installed - and that driver does not compile against pipewire 1.6's retyped method macros (pw_node_enum_params now wants a pw_node*, and gcc >= 14 makes the mismatch a hard error). Whether this fails is a build-order lottery: CI won its race, and the incremental local tree built dolphin before the pipewire bump so the stamp never forced a rebuild. A clean tree loses reliably. Dolphin never uses SDL audio - it ships its own openal/pulse/alsa backends and SDL is there for controller input - so pass -DSDL_PIPEWIRE=OFF through to the vendored build and make the configure deterministic.
Rockchip's rkbin tools (loaderimage, boot_merger - executed by u-boot-legacy's rkhelper on RK3326) are x86-only prebuilts. The aarch64 container branch installs libc6-amd64-cross, but that lays the x86 loader out at /usr/x86_64-linux-gnu/lib/ - the qemu interp-prefix convention. macOS/OrbStack runs x86 binaries through Rosetta, which resolves the binary's literal interpreter path /lib64/ld-linux-x86-64.so.2 in the container namespace and dies with 'Dynamic loader not found'. Symlink the cross loader at the canonical path; the cross libc's compiled-in search path then finds the rest from its own prefix. Harmless on amd64 hosts (branch never runs) and on Linux arm64 runners (binfmt qemu keeps using the interp-prefix).
The loader symlink got Rosetta past the interpreter, but unlike qemu - whose interp-prefix rewrites every library lookup into /usr/x86_64-linux-gnu - Rosetta leaves the loader to its compiled-in search paths, so dynamically linked rkbin tools (loaderimage, trust_merger; boot_merger only survived by being static) still died on libc.so.6. Symlink the multiarch directory the loader actually searches at the cross package's library dir.
qtbase fatally rejects build directories whose path contains symlinks; build trees living behind a symlink (e.g. docker-volume mounts) are legitimate. QT_ALLOW_SYMLINK_IN_PATHS is Qt's supported escape hatch (added for Homebrew) and downgrades the check to a warning. No effect where paths contain no symlinks.
Both are LibreELEC-upstream candidates rather than anything ROCKNIX specific; carried here so the tree builds, to be offered upstream. meson: a configure killed partway - an interrupted run, a parallel job failing beside it - leaves a build directory holding coredata.dat but no build.dat or build.ninja. meson then refuses it as "already configured" while being unable to read it, so the package can never configure again: Directory already configured. ERROR: No such build data file as '.../meson-private/build.dat' The state is regenerable, so discard it and configure fresh. Only fires on that combination, so healthy build directories are reused as before. Hit on wayland:host and systemd:target. sysroot: the 32-bit leg needs usr/lib32 to be a symlink to usr/lib, so packages configured with --libdir=/usr/lib32 resolve to the tree everything else installs into. That link was created only in the per-package staged sysroot and reached the shared one as a side effect of whichever package happened to merge first, so on a fresh tree a consumer could be built before anything created it - fluidsynth failed to find glibconfig.h, which was present under lib the whole time. Create it for the shared sysroot too. Verified by deleting the link and rebuilding: it comes back.
get_env dumps every exported variable, so two kinds of host state cross into the build and break it in ways that look like something else. Underscore-namespace variables are the build system's own derived state and are only recomputed when unset: init_package_cache regenerates the package caches when _CACHE_PACKAGE_* are empty, and set_debug_depends runs when _DEBUG_DEPENDS_LIST is unset - tested with +x, so the empty value already in .env suppresses it. Carrying them in pins a stale package cache for the whole run, and adding or removing a package then silently does nothing. That cost two builds to find while deleting the opus and nasm shadows: the first still resolved a package.mk that was already gone, the second could not find the package at all. TMPDIR is the second: macOS sets one by default and the path does not exist in the container, so mktemp fails and init_package_cache dies before it can build the caches. This is why builds have needed to run as "env -u TMPDIR make docker-...". Dropping it here removes the need for that prefix. Checked with a plain docker run, no TMPDIR prefix and no cache overrides: opus resolves to packages/audio/opus at 1.6.1, iwd to the ROCKNIX shadow at 3.12, nasm to packages/lang/nasm at 3.02 x86_64.
pre_configure_target ran a bare ./autogen.sh, which only works on the first attempt: scripts/build stays in the source dir when no configure script exists yet. Once autogen has generated one and the build is interrupted - a parallel-job failure elsewhere is enough - the retry takes the configure-toolchain path, whose cwd is the fresh build subdir, and ./autogen.sh can never be found again. Run it from PKG_BUILD explicitly; autogen re-runs idempotently. Core carries the identical latent hook, so this is an upstream candidate alongside the scripts/build meson partial-configure repair.
SM8550 aarch64 could not install box86:
cp: cannot stat '.../distribution-ng/build.ROCKNIX-SM8550.arm/
install_pkg/box86-*/usr/share/box86/lib/*': No such file or directory
Seven packages reach into the other architecture's build tree and all
of them spelled it ${ROOT}/build.${DISTRO}-${DEVICE}.<arch>. That is
only correct when the build trees sit inside the checkout. config/path
sets BUILD_ROOT=${BUILD_DIR:-$ROOT} and derives BUILD from it, so with
BUILD_DIR pointing at the docker volume the trees are elsewhere and
every one of these lookups misses.
It has been papered over with hand-made symlinks in the checkout -
build.ROCKNIX-RK3566.arm and the two SM8250 ones point into /build.
That is why RK3566 built: its symlink exists. SM8550 is new, has none,
and failed on the first package that needed the other leg.
Worse than the visible failure, retroarch guards its copy with
if [ -f ... ], so a missing path silently skips installing the 32-bit
retroarch rather than failing. Any device without a symlink would ship
an incomplete image with nothing in the log.
12 sites across box86, lib32, daedalusx64-sa, gpsp-lr, pcsx_rearmed-lr,
desmume-lr and retroarch. BUILD_ROOT falls back to ROOT when BUILD_DIR
is unset, so behaviour is unchanged for in-tree builds and the symlinks
can go.
box86 now installs for SM8550 aarch64: binary, five 32-bit libs,
box86rc and the binfmt config all copied from the arm leg.
A failing package tells you to read config.log, but the failure artifact only carries output.log and the thread logs, so a configure abort cannot be diagnosed from it. libvpx on H700 is the current example: its probe swallows the real error into config.log and dies with a misleading message about nasm being too old. Collect config.log and meson-log.txt from the build tree alongside the existing logs. Failure only, so nothing is uploaded on a green build.
The shadow already fetches the github archive of e486f20; give it the checksum so a mirror can never swap the bytes. The pin stays ahead of core deliberately: e486f20 is 30 commits past the 1.2.2 release core carries, with nothing behind.
The shadow already fetches the armbian firmware bundle as a github archive; pin its checksum so a mirror can never swap the bytes.
fex-emu's install copies the x86_64 libvulkan_freedreno.so that the mesa:host pass builds - the same unconditional copy upstream carries - so trimming mesa:host to a compiler-only build breaks every fex device leg at install. Restore upstream's host dependency set and pre_configure_host verbatim. The compiler-only trim belongs with the fex guest-vulkan rework that compensates for it, which lives on the follow-up branch.
Pointing DISTRO_MIRROR at the fork release replaced the org mirror rather than adding to it, which broke configtools: savannah stopped generating the cgit snapshot its PKG_URL fetches, so the org mirror was the only place left that had it. PKG_URLS is already a list the download loop walks, so treat DISTRO_MIRROR as one too and append each entry. The org mirror comes first, so dropping the fork entry later degrades cleanly. Between them every package that currently fails is covered: configtools only exists on the org mirror, ncurses and libevdev only on the fork, libgpg-error on both.
sunshineinabox
force-pushed
the
ng-macbuild
branch
from
August 18, 2026 00:37
e322cc9 to
8707680
Compare
sunshineinabox
marked this pull request as draft
August 20, 2026 15:16
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.
Summary
Testing
Additional Context
AI Usage
While ROCKNIX doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? YES, very much so. Have reviewed most of the AI slop.