From aa60d34e13b88a9d4d608e7b4ed45ff9a6ed32eb Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Tue, 14 Apr 2026 11:07:58 -0700 Subject: [PATCH] oven: persistent /kernel-build volume + skip no-op .config rewrites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously every native build copied the whole linux-$KVER tree from /cache into an ephemeral $BUILD dir, so ccache helped per-file but every link, modpost, kallsyms, and bzImage assembly pass ran fresh. Now the kernel tree lives on a named Docker volume (ac-os-kbuild) mounted at /kernel-build; docker-build.sh seeds it from /cache on first run and keeps the .o files between oven runs. A .kver sentinel wipes the tree when the kernel version bumps. Also: cmp .config against the repo copy before overwriting — the blind `cp` was touching .config's mtime every run, invalidating kbuild's own dependency graph even when the config hadn't changed. Expected win: code-only changes drop from ~10 min kernel rebuild to ~1-2 min (only changed .c files recompile + bzImage re-links with new initramfs). Config changes still do the full rebuild. Co-Authored-By: Claude Opus 4.6 (1M context) --- fedac/native/docker-build.sh | 41 ++++++++++++++++++++++++++++++------ oven/native-builder.mjs | 2 +- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/fedac/native/docker-build.sh b/fedac/native/docker-build.sh index 81458c68c6..dcf9b911db 100644 --- a/fedac/native/docker-build.sh +++ b/fedac/native/docker-build.sh @@ -627,22 +627,51 @@ log " Initramfs: $((INITRAMFS_SIZE / 1048576))MB compressed" # Step 4: Build kernel with embedded initramfs # ══════════════════════════════════════════════ log "Step 4/4: Building kernel..." -LINUX_DIR="$BUILD/linux-$KVER" + +# Persistent kernel build tree lives on a Docker volume mounted at /kernel-build. +# This preserves object files between oven runs so ccache + kbuild's own +# dependency tracker produce true incremental rebuilds. Fall back to ephemeral +# $BUILD when the volume isn't present (e.g. running docker-build.sh locally +# without the oven wrapper). +if [ -d /kernel-build ] && [ -w /kernel-build ]; then + KBUILD_ROOT="/kernel-build" + log " Using persistent kernel build tree at $KBUILD_ROOT" +else + KBUILD_ROOT="$BUILD" + log " No persistent volume — kernel tree at $KBUILD_ROOT (ephemeral)" +fi +LINUX_DIR="$KBUILD_ROOT/linux-$KVER" + +# Version sentinel: if the persisted tree is for a different kernel version, +# wipe it before reinitializing from the Docker image's cached source. +SENTINEL="$KBUILD_ROOT/.kver" +if [ -f "$SENTINEL" ] && [ "$(cat "$SENTINEL")" != "$KVER" ]; then + log " Kernel version changed ($(cat "$SENTINEL") → $KVER), wiping build tree" + rm -rf "$KBUILD_ROOT/linux-"* + rm -f "$SENTINEL" +fi # Use cached kernel source or download if [ ! -f "$LINUX_DIR/Makefile" ]; then if [ -d "/cache/linux-$KVER" ]; then - log " Using cached Linux $KVER..." - cp -a "/cache/linux-$KVER" "$BUILD/" + log " Initializing $KBUILD_ROOT from cached Linux $KVER..." + cp -a "/cache/linux-$KVER" "$KBUILD_ROOT/" else log " Downloading Linux $KVER..." - cd "$BUILD" + cd "$KBUILD_ROOT" curl -sL "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KVER}.tar.xz" | tar xJ fi + echo "$KVER" > "$SENTINEL" fi -# Copy config -cp "$NATIVE/kernel/config-minimal" "$LINUX_DIR/.config" +# Copy config only if it differs — overwriting touches mtime and invalidates +# large swaths of kbuild's dependency graph, forcing full rebuilds. +if ! cmp -s "$NATIVE/kernel/config-minimal" "$LINUX_DIR/.config"; then + log " Config changed, updating .config" + cp "$NATIVE/kernel/config-minimal" "$LINUX_DIR/.config" +else + log " Config unchanged, preserving existing build tree" +fi # Stage built-in firmware blobs referenced by CONFIG_EXTRA_FIRMWARE # into a container-local directory and rewrite CONFIG_EXTRA_FIRMWARE_DIR. diff --git a/oven/native-builder.mjs b/oven/native-builder.mjs index f77d735f59..84b0924ba4 100644 --- a/oven/native-builder.mjs +++ b/oven/native-builder.mjs @@ -471,7 +471,7 @@ async function runBuildJob(job) { addLogLine(job, "stdout", "Phase 2: Compiling C kernel in Docker..."); const cidFile = `/tmp/oven-cid-${job.id}`; await runPhase(job, "build", "bash", ["-c", [ - `CID=$(docker create -e AC_BUILD_NAME=${buildName} -e AC_GIT_HASH=${buildGitHash} -e AC_BUILD_TS=${buildTs} -v ac-os-ccache:/ccache ac-os-builder)`, + `CID=$(docker create -e AC_BUILD_NAME=${buildName} -e AC_GIT_HASH=${buildGitHash} -e AC_BUILD_TS=${buildTs} -v ac-os-ccache:/ccache -v ac-os-kbuild:/kernel-build ac-os-builder)`, `echo $CID > ${cidFile}`, `docker start -a $CID`, ].join(" && ")], repoDir); -- 2.51.2