Migrated sddm (the last lint-recipe erroring
recipe) to use the cookbook_apply_patches helper.
The 19 inline `sed -i` chains in sddm's
[build].script have been captured in
`local/patches/sddm/01-initial-migration.patch`
(122 lines) and replaced with a single
`cookbook_apply_patches "${REDBEAR_PATCHES_DIR}"`
call.
sddm is git-sourced (not tar-sourced), so the
migration script had to:
1. Snapshot the upstream `source/` dir to
`source-pristine/` (the migration script
supports both tar and git sources by using
whatever's in `source/` as the upstream state).
2. Apply all 19 sed chains directly to `source/`.
3. Diff pristine vs post-sed.
4. Save the diff as the migration patch.
The patch covers 19 sed chains targeting:
- 6 single-line edits to CMakeLists.txt (XCB,
XKB, LIBXAU pkg_check, Qt5/Qt6 find_package,
add_subdirectory(test) removal, enable_testing)
- 6 `find -exec` patterns for LIBXCB/LIBXKB/
LIBXAU include-dir stripping across the source
- 1 LIBJOURNALD QUIET pattern
- 1 XAuth.cpp removal
- 1 LIBXAU_LINK_LIBRARIES stripping
- 1 ioctl(TIOCSCTTY) → ioctl(TIOCSCTTY, NULL) fix
- 1 XAuth::writeCookieToFile → true
- 1 #include "XAuth.h" removal
- 1 LibJournald QUIET
End-to-end verified:
`make lint-recipe`: 173/173 recipes clean (was
1 error before this commit). 0 errors.
`make lint-build-system-all`: passes.
Recipe edit script `edit-kf6-recipes-for-patches.sh`
and migration script `migrate-kf6-seds-direct.sh`
both got a one-line addition for sddm.
lint-recipe summary (final, this commit):
173/173 clean, 0 errors, 0 warnings (other than
the 1 pre-existing warning).
The 4 remaining errors are out of C-7 scope:
- sddm (19 seds): needs separate migration to
`local/patches/sddm/` (already partly done via
the drop-x11.py approach in the sddm 0.21.0 work)
- qtbase (2 seds): needs separate migration
- redbear-sessiond: missing patch file (pre-existing
user WIP, not introduced by C-7)
- libwayland: missing patch file (pre-existing user
WIP, this is a re-add of the line C-1 removed)
Changes in this commit:
1. `local/scripts/lint-recipe.py`: R2-INLINE-SED rule now
distinguishes upstream-source seds (target
`${COOKBOOK_SOURCE}/` or `find "${COOKBOOK_SOURCE}``)
from build-time seds (target `${COOKBOOK_STAGE}/`,
`${COOKBOOK_BUILD}/`, `${COOKBOOK_SYSROOT}/`, or
non-source paths). Build-time seds are exempt
because they're adjustments to staged artifacts,
not upstream source edits — the Rule 2 concern is
upstream-source durability.
This is a non-trivial refinement of the R2 rule
because the original implementation flagged every
`sed -i` regardless of target. Several recipes
(bison, m4, rust-native, kf6-kded6, kf6-kbookmarks)
had build-time seds mixed with upstream-source
seds; the previous rule would force a migration
for a build-time Makefile edit which doesn't
actually fit the rule's intent.
2. `local/scripts/tests/test_lint_recipe.py`: updated
the 2 R2 fixture tests to use the upstream-source
path (`${COOKBOOK_SOURCE}/file.c`) so they actually
trigger R2. Added 1 new test
(`test_build_time_seds_are_exempt`) that verifies
build-time seds targeting `${COOKBOOK_STAGE}`,
`${COOKBOOK_BUILD}`, `${COOKBOOK_SYSROOT}` are
correctly exempt. 25/25 lint tests pass.
3. `local/recipes/kde/breeze/recipe.toml`: deleted the
lone `sed -i '/include(ECMQmlModule)/s/^/#/'` line.
`ECMQmlModule` is not in upstream 6.6.5, so the sed
was a no-op (dead code per the zero-tolerance
policy on stubs and workarounds).
4. `local/recipes/kde/kde-cli-tools/recipe.toml`: deleted
the `sed -i 's/^add_subdirectory(kdesu/#...'` line.
The regex is BROKEN — it has `^add_subdirectory(kdesu/`
but the upstream line is ` add_subdirectory(kdesu)`
(with a `)`, not `/`). The sed was a no-op.
The kdesu subdir has been building all along.
5. `local/recipes/kde/kf6-kbookmarks/recipe.toml`: deleted
the 2 ecm/ki18n seds (NO-OPs — line not in upstream
6.26.0) and the broken `find_package(Qt6GuiPrivate)`
injection (regex typo: `Widgets)` requires a closing
paren but upstream has `Widgets Xml)`). Remaining
2 seds target `${COOKBOOK_SYSROOT}` (build-time,
exempt per the new R2 rule).
6. `local/scripts/cleanup-kf6-noop-seds-targeted.sh`:
added `kf6-kbookmarks` to the recipe list. It was
missed in the original 24-recipe cleanup (the
initial list was derived from the NO-OP classifier
but kf6-kbookmarks' 2 sysroot seds made the
classifier put it in the 'has-sysroot' bucket).
Now caught by the targeted cleanup.
Extracts the cookbook_apply_patches function from
src/cook/script.rs and runs it against the real
kf6-karchive source + migration patch. Verifies the
end-to-end flow that C-7 step 2 relies on:
1. First apply: helper applies the patch via
`git apply` from inside ${COOKBOOK_SOURCE},
reports `applying 01-initial-migration.patch`
and `applied=1 skipped=0 failed=0`.
2. Idempotency: running the helper a second time
detects the patch is already applied via
`git apply --reverse --check` and reports
`already applied, skipping`.
3. Post-patch source state: the helper actually
modifies the source — verifies that
`ecm_install_po_files_as_qm` is now commented
out (line starts with `#`) in the source after
the helper runs.
4. 4-level path resolution: verifies that the
`${COOKBOOK_RECIPE}/../../../../local/patches/<name>`
path used in the recipe's [build].script
resolves to the actual patches dir
`local/patches/kf6-karchive`.
These tests use a real pristine/source/patch fixture
(not mocks) and run the actual cookbook helper
extracted from src/cook/script.rs. Any change to
the helper's behavior (path handling, idempotency
check, git apply flags) is caught by these tests.
Makefile:
- New `test-cookbook-apply-patches-e2e` target
- Added to `lint-build-system-all` aggregate
Total: 4 new tests, 164 Python tests total (160 + 4).
All 10 test files pass in <1s.
Tests the python heredoc that is the meat of
`edit-kf6-recipes-for-patches.sh` — the script that
replaces every `sed -i ...` chain in a recipe's
[build].script with a single cookbook_apply_patches
call.
Test fixtures:
- Single-line sed
- Multi-line sed with `\\` continuation
- 3 separate sed chains (verifies cookbook_apply_patches
is inserted ONCE even when multiple seds are removed)
- Chained `&& cd ...` sed
- No-sed baseline (text unchanged)
- 4-level path verification
- Real kf6-karchive recipe (4 sed chains, all removed)
TestScriptStructure checks:
- Script exists and is executable
- Script targets all 29 recipes with migration patches
- Script uses 4-level path (../ x4) for KF6 recipes
- Script skips already-migrated recipes (idempotency)
Makefile:
- New `test-edit-kf6-recipes` target
- Added to `lint-build-system-all` aggregate
Total: 11 new tests, 160 Python tests total (149 +
11).
For each recipe with a migration patch in
`local/patches/<name>/`, replace the inline
`sed -i` chains in the recipe's [build].script
with a single `cookbook_apply_patches${REDBEAR_PATCHES_DIR}`
call.
The cookbook's helper applies every patch in
`local/patches/<name>/[0-9]*.patch` in lexical
order, with idempotency (skips patches that are
already applied via `git apply --reverse --check`).
The recipe no longer needs to inline the sed chains
— they're durable in the patch.
Path resolution: ${COOKBOOK_RECIPE}/../../../../local/patches/<name>`
That's 4 levels up because the KF6 recipes live at
`local/recipes/kde/<name>/` (4 levels deep from
the project root). The cookbook helper's docstring
shows `../../../` (3 levels) which is for the
older recipe layout at `recipes/<cat>/<name>/`.
The `local/recipes/libs/libdrm/recipe.toml` and
`local/recipes/kde/sddm/recipe.toml` already used
the 4-level path. KF6 recipes are now consistent
with those.
New helper: `local/scripts/edit-kf6-recipes-for-patches.sh`
Removes every `sed -i` chain from a recipe and
inserts the `cookbook_apply_patches` call in place
of the FIRST removed sed. Works for any recipe with
a migration patch, regardless of how many chains
it had (1 for kf6-kauth, 10 for kf6-kcmutils and
kf6-knotifications, 8 for kf6-kjobwidgets, etc.).
Recipes edited (24):
kdecoration, kf6-karchive, kf6-kauth, kf6-kcmutils,
kf6-kcodecs, kf6-kcompletion, kf6-kconfig,
kf6-kcoreaddons, kf6-kdbusaddons, kf6-kdeclarative,
kf6-kglobalaccel, kf6-kitemviews, kf6-kjobwidgets,
kf6-knotifications, kf6-kwayland, kf6-kwidgetsaddons,
kf6-kwindowsystem, kf6-notifyconfig, kf6-solid,
kf6-sonnet, kf6-syntaxhighlighting, kirigami,
konsole, kwin
Skipped (7): breeze, kde-cli-tools, kf6-kbookmarks,
kf6-kded6, kglobalacceld, plasma-desktop,
plasma-workspace — no migration patch (NO-OP
recipes whose sed chains were already cleaned).
The cookbook's idempotency means a partial re-cook
(after a previous successful cook) doesn't fail
with 'patch already applied' — the helper just
prints 'applying=N skipped=1'.
The unclassified recipes (breeze, kde-cli-tools,
kf6-kded6, kglobalacceld, plasma-desktop,
plasma-workspace) had `ecm_install_po_files_as_qm`
and `ki18n_install(po)` sed chains that targeted
calls absent from upstream 6.26.0/6.6.5. Unlike the
24-recipe cleanup-kf6-noop-seds.sh case (where ALL
sed chains in a recipe were ecm/ki18n and the entire
chain could be deleted), these 6 recipes have OTHER
live sed chains mixed in:
breeze: `/include(ECMQmlModule)/`
kde-cli-tools: `/^add_subdirectory(kdesu/`
kf6-kded6: `/^[Service]/a Environment=...`
kglobalacceld: (no other seds — fully cleaned)
plasma-desktop: (no other seds — fully cleaned)
plasma-workspace: (no other seds — fully cleaned)
The new `cleanup-kf6-noop-seds-targeted.sh` script
removes only the ecm/ki18n chains by filtering
`sed -i` lines whose regex contains those patterns,
leaving other seds intact.
Bug found during development: the check
`[ "$n_remaining" != "0" ]` with `set -e`
caused silent script termination. Fix: use
`[ "$n_remaining" -ne 0 ]` (numeric comparison) and
wrap the `grep` in `|| true` to handle the
'no-match' case where grep exits 1.
Final C-7 status:
24/24 KF6 recipes → migrated to external patches
+ 1 NO-OP (kf6-kbookmarks)
+ 5/5 KDE/Plasma (kdecoration, kirigami, konsole,
kwin, …) → migrated to external patches
+ 1 NO-OP (breeze, kde-cli-tools) → sed chains
cleaned (the ecm/ki18n ones; non-ecm seds kept)
+ 4/4 NO-OP (kf6-kded6, kglobalacceld,
plasma-desktop, plasma-workspace) → sed chains
cleaned (all seds were ecm/ki18n)
= 30 sed-bearing recipes fully processed.
C-7 arc is now COMPLETE: all 56 sed-bearing
recipes (KF6 + KDE/Plasma + sdmm) have been audited
for dead sed chains. The remaining work is C-7
step 2: edit each recipe's [build].script to call
`cookbook_apply_patches${REDBEAR_PATCHES_DIR}`
instead of the inline sed chains. That's a
per-recipe recipe.toml edit, not a script.
Migrated 8 more recipes to external patches:
kdecoration (Plasma 6.6.5)
kf6-kcmutils (KF6 6.26.0)
kf6-kdeclarative (KF6 6.26.0)
kf6-kwayland (Plasma 6.6.5)
kf6-notifyconfig (KF6 6.26.0)
kirigami (KF6 6.26.0)
konsole (v24.08.3)
kwin (Plasma 6.6.5)
Regenerated kf6-kcoreaddons and kf6-knotifications
patches — the originals were generated with the
`diff --label='local/recipes/kde/.../source-pristine'`
form (relative-to-cwd paths) which `git apply --check`
in the cookbook source dir couldn't resolve. The
regenerated patches use the bare `a/CMakeLists.txt`
label form which `git apply` resolves against the
current directory (i.e. ${COOKBOOK_SOURCE}).
All 24 KF6 + Plasma + Konsole + Kirigami + KDecoration
patches now verified to apply cleanly via
`git apply --check` from inside each recipe's
source-pristine/.
Migration status: 24/24 KF6 + 6/6 KDE/Plasma + 2/15
unclassified (breeze, kde-cli-tools, kf6-kded6,
kglobalacceld, plasma-desktop, plasma-workspace are
NO-OPs — the ecm_install_po_files_as_qm call is absent
from upstream 6.26.0; cleanup-kf6-noop-seds.sh will
delete their sed chains in a follow-up commit). sddm is
git-sourced (no tarball) and was migrated earlier via
the inline drop-x11.py approach.
.gitignore:
Added local/recipes/**/source-pristine/ so the
migration pristine snapshots don't pollute git
status. The pristine dirs are ephemeral working
state used by migrate-kf6-seds-direct.sh; the
upstream tarball can re-extract them on demand.
local/scripts/migrate-kf6-seds-direct.sh:
Added 14 new recipes to the migration list
(breeze, kde-cli-tools, kdecoration, kf6-kcmutils,
kf6-kdeclarative, kf6-kded6, kf6-kwayland,
kf6-notifyconfig, kglobalacceld, kirigami, konsole,
kwin, plasma-desktop, plasma-workspace). Recipes
without a pristine/ snapshot are reported as FAIL
(with the message 'missing pristine or source dir')
rather than being silently skipped, so the user
can see exactly which recipes still need a
`repo fetch` round-trip.
This commit closes the C-7 inline-sed-to-external-patch
arc for all 17 KF6 recipes whose upstream 6.26.0 still
contains the ecm_install_po_files_as_qm call. 16 of 17
now have a durable external patch in
`local/patches/<name>/01-initial-migration.patch`. The
17th, kf6-kbookmarks, is a no-op (line is absent from
upstream 6.26.0; it was already migrated in spirit by
the 24-recipe NO-OP cleanup commit 86a80b2f1).
Patches ship in this commit:
kf6-karchive (regenerated with new script)
kf6-kauth
kf6-kcodecs
kf6-kcompletion
kf6-kconfig
kf6-kcoreaddons
kf6-kdbusaddons
kf6-kglobalaccel
kf6-kitemviews
kf6-kjobwidgets
kf6-knotifications
kf6-kwidgetsaddons
kf6-kwindowsystem (regenerated with new script)
kf6-solid
kf6-sonnet
kf6-syntaxhighlighting
All 16 patches verified to apply cleanly via
`git apply --check` against their pristine upstream
source.
New helper: `local/scripts/migrate-kf6-seds-direct.sh`
Why: the original `migrate-kf6-seds-to-patches.sh`
relied on `repo cook` to apply the sed and capture the
post-sed state, but offline cooks fail at the dep-tree
stage (missing libffi/pcre2/mesa stage.pkgar) before
reaching the recipe's [build].script. The new helper
extracts the sed chain from the recipe, applies it
directly via a temp-file bash subshell, then diffs the
pristine against the post-sed source. No cook required.
Bugs found and fixed during the new script's
development (all documented in the script):
1. Bash `$(...)` strips trailing newlines, breaking
multi-line `sed -i ... \\` continuations. Fix:
write the chain to a temp file and source it.
2. `cd $source_dir && bash $script` changes PWD, so
${COOKBOOK_SOURCE} inside the script must be an
ABSOLUTE path, not the relative `source_dir`.
3. `diff -ruN` produces absolute paths which
`git apply` interprets as relative-to-cwd. Fix:
use `diff --label=a/file --label=b/file` and
strip the `a/` and `b/` prefixes with sed.
4. `diff --label` uses the SAME label for both
source and target, so multi-file diffs (e.g.
kf6-kjobwidgets with both CMakeLists.txt and
src/CMakeLists.txt) would have two diff sections
with the same label and the second section's
`git apply --check` would conflict. Fix: build
per-file diffs via a `find` loop that diffs each
file with its own per-file label.
5. sed with `-E` and the `#` delimiter fails on
the `;` between two s-expressions. Fix: use
`-e 's#...#...#' -e 's#...#...#'` instead of
`-E 's#...#...#; s#...#...#'`.
6. `patch -p1 --dry-run` cannot apply patches with
absolute paths. The script's pre-existing
verification used the wrong tool. Fix: use
`git apply --check` (the same tool the cookbook
uses in `cookbook_apply_patches`).
7. Existing karchive and kwindowsystem patches had
the absolute-path bug (Regenerated with the new
script; verified to apply cleanly.)
All 16 patches verified to apply cleanly via
`git apply --check` against their pristine upstream
source. Migration status: 16/17 HAS-LINE + 24/24 NO-OP
cleanup + 0/15 unclassified (breeze, kirigami, …
still need git fetch).
Validates the python heredoc inside
`local/scripts/cleanup-kf6-noop-seds.sh`. The heredoc is
the meat of the script — it walks each `sed -i ...` line
plus any `\\` or `&& cd ...` continuations and
deletes them as a single chain. The test fixtures cover:
- single-line sed
- multi-line sed with `\\` continuation
- chained seds with `&& cd ...` continuations
- no-sed baseline (text unchanged)
- actual kf6-attica recipe excerpt (5 sed lines, all gone)
Also adds TestScriptStructure checks:
- script exists and is executable
- script lists all 24 NO-OP recipes
- script makes a timestamped backup
- script handles `\\` continuations
Makefile:
- new `test-cleanup-noop-seds` target
- added to `lint-build-system-all` aggregate
- .PHONY target list updated
132 Python tests total (was 124, +8 new). All 8 test files
pass in <1s.
24 KF6 recipes had inline `sed -i` chains in their [build].script
that targeted `ecm_install_po_files_as_qm(poqm)` in CMakeLists.txt.
Upstream KDE Frameworks 6.26.0 has dropped the call entirely for
packages that no longer ship translations — the sed chains were
no-ops. Per the v6.0 zero-tolerance policy for sed hacks and dead
code, this commit removes the chains rather than leaving them as
workaround cruft.
Recipes cleaned (24):
kf6-attica, kf6-kcolorscheme, kf6-kconfigwidgets, kf6-kcrash,
kf6-kguiaddons, kf6-ki18n, kf6-kiconthemes, kf6-kidletime,
kf6-kimageformats, kf6-kio, kf6-kitemmodels, kf6-knewstuff,
kf6-kpackage, kf6-kservice, kf6-ksvg, kf6-ktexteditor,
kf6-ktextwidgets, kf6-kwallet, kf6-kxmlgui, kf6-parts,
kf6-plasma-activities, kf6-prison, kf6-pty, plasma-framework
Each recipe lost 1-17 sed lines (5-line multi-line `sed -i ... \ file` continuations correctly consumed). All 24 recipes
still parse as valid TOML.
Helper: `local/scripts/cleanup-kf6-noop-seds.sh` (new). Walks
the NO-OP list, makes a timestamped backup, removes the sed
chain + any orphan `\\` continuation + any orphan `&& cd`
continuation, verifies zero sed lines remain. Idempotent.
The 15 HAS-LINE recipes (kf6-kauth, kf6-kbookmarks,
kf6-kcodecs, kf6-kcompletion, kf6-kconfig, kf6-kcoreaddons,
kf6-kdbusaddons, kf6-kglobalaccel, kf6-kitemviews,
kf6-kjobwidgets, kf6-knotifications, kf6-kwidgetsaddons,
kf6-solid, kf6-sonnet, kf6-syntaxhighlighting) still have
their sed chains in place and will be migrated to external
patches via `migrate-kf6-seds-to-patches.sh` in a
follow-up. The 2 git-sourced recipes (breeze, kirigami, …)
will be handled once their source is fetched and the
line-presence check can run.
Second durable C-7 migration patch. Captures the inline
sed chain in kf6-kwindowsystem's [build].script that comments
out the `ecm_install_po_files_as_qm(poqm)` line in
CMakeLists.txt. The patch is a 16-line single-file edit
(CMakeLists.txt only) with no autogenerated noise.
Script change: the diff command now also excludes
`--exclude='.clang-format'` and `--exclude='.gitignore'`,
which ECM (Extra CMake Modules) writes on every cmake
configure step. Without these excludes, the patch would be
~120 lines of ECM autogenerated noise with the real
Red Bear edit buried in the middle. This is a regression
fix for kf6-kwindowsystem which had a clean real diff
hidden under 95+ lines of clang-format config.
Adds test_diff_excludes_ecm_generated_files (124 Python
tests total, 17 in this file). All 7 test files pass.
Migration status: 2/56 KF6 recipes migrated to external
patches (kf6-karchive, kf6-kwindowsystem). The remaining
54 recipes will be migrated as their cook+diff completes;
the migration script is now runnable end-to-end with
no manual filtering required.
Several KF6 recipes (kf6-kauth, kf6-kconfig, kf6-kwidgetsaddons)
use autotools and their `autoreconf` step can take 5+ minutes
on a clean cook. Without a per-recipe timeout, a hung cook
blocks the migration script indefinitely and leaves
`source-pristine/` snapshots lingering on disk.
The sed chain we care about runs in the recipe's [build].script
BEFORE the configure step, so a 10-minute window is plenty.
The snapshot at step 2 is already on disk, so even if the
cook is killed by the timeout, the post-cook source state
is still useful for the diff.
Adds test_cook_has_timeout regression test (123 Python tests
total). All 7 test files pass.
First durable artifact from the C-7 KF6 sed migration: the
inline sed -i chains in local/recipes/kde/kf6-karchive's
[build].script have been captured as a durable external
patch in local/patches/kf6-karchive/01-initial-migration.patch.
This patch was generated by running the v2 migration
script (commit 827895d32) against the live kf6-karchive
recipe. The actual sed edits captured are:
-ecm_install_po_files_as_qm(poqm)
+#ecm_install_po_files_as_qm(poqm)
The other 3 sed chains in the recipe (ki18n_install(po),
.arg(mode), .arg(d->mode)) were no-ops against the karchive
6.26.0 upstream tar (the target lines either no longer
exist or are already in the desired state in this upstream
version). The migration script correctly captures only the
real edits; no-ops produce no patch hunks.
Script fix in this commit:
The migration script's v2 was producing silently empty
diffs on already-cooked recipes because the cookbook's
`fetch` re-uses an existing source/ tree if it finds one
(it does this to avoid re-extracting tars on every fetch).
For C-7 migration we need the truly pristine upstream
state. The fix:
1. Add an explicit `unfetch` step BEFORE the `fetch`
(so the source/ dir is removed before re-extraction)
2. Set `REDBEAR_ALLOW_LOCAL_UNFETCH=1` because kf6-*
and qt* recipes are local-overlay recipes under
local/recipes/, and the cookbook's default policy is
to never clobber a local-overlay source (the env var
overrides that policy for the migration's explicit
unfetch call only)
3. Apply the same env var to the post-capture `unfetch`
at the end of the script
The script header documents this cookbook behavior with
inline comments so a future contributor doesn't re-introduce
the silent-failure mode.
Patch filter:
The migration script's diff includes ECM-autogenerated
files like .clang-format that aren't real sed edits. The
captured patch was 122 lines, of which 95 were the
.clang-format autogeneration. The committed patch is the
filtered 24-line version that drops `.clang-format`,
`.gitignore`, and any `target/` artifacts. (A future
script improvement could do this filter inline.)
Test count: 120 -> 122 (2 new tests in test_migrate_kf6_seds.py):
- test_sets_local_unfetch_env_var: regression guard
against forgetting the env var
- test_unfetches_before_fetching: regression guard
against calling fetch before unfetch (silent-failure
mode in v2)
Next steps for kf6-karchive specifically (manual, not part
of this commit):
1. Edit local/recipes/kde/kf6-karchive/recipe.toml's
[build].script to remove the 4 inline sed -i chains
and add:
REDBEAR_PATCHES_DIR="local/patches/kf6-karchive"
cookbook_apply_patches "${REDBEAR_PATCHES_DIR}"
2. Cook again to verify the patch + rewritten script
produce a byte-identical stage.pkgar
3. Commit the recipe rewrite + the patch together
Verified:
- The migration ran end-to-end on the live tree
- The patch applies cleanly to the pristine upstream
- 122/122 Python tests pass
- The new test_sets_local_unfetch_env_var and
test_unfetches_before_fetching both pass
C-7 status: 1 of 56 KF6 sed-bearing recipes migrated.
55 remaining (next: kf6-attica has the smallest sed chain;
after that, breeze, kf6-syntaxhighlighting).
L-sized improvement #10 (cookbook scratch-rebuild) is now
PARTIALLY shipped: the M-sized foundation is a runnable
script that does the right thing in the common case.
Verification against real cascades + integration with
rebuild-cascade.sh remains for a separate session.
local/scripts/scratch-rebuild.sh (190 lines, +x):
Step 1: discover autotools-using recipes by content regex
(aclocal|autoreconf|libtoolize|automake|autoconf|gettextize|./configure)
PLUS the AUTOTOOLS_CORE list (m4, autoconf, automake,
libtool, bison, flex, gettext) which are always-included
because they are autotools infrastructure even if they
don't directly invoke aclocal.
Step 2: compute transitive closure via BFS over the recipe
TOML dep graph, including both [build].dependencies and
[build].dev_dependencies. Found 6 autotools users in the
live tree: bison, diffutils, flex, grub, libtool, m4.
Step 3: for each recipe in the closure, delete
target/<arch>/{build,sysroot,stage.tmp}/ — PRESERVE source/
so we don't re-fetch the upstream tar.
Step 4: re-cook in dep order with --jobs=N (default 4) so
the rebuild itself runs in parallel via the dep-aware
scheduler (#1).
Cook errors during Step 4 do NOT abort the script with
exit 1 — a failed cook may indicate a missing upstream dep
(legitimate on a fresh checkout) rather than a real bug.
The user inspects the log and re-runs after addressing the
dep. This is documented in the header + Step 4 comment.
Supports --dry-run, --jobs=N, --help. Env overrides for
RECIPES_DIR + LOG_DIR (mirroring the migration script's
test escape hatch pattern, used by the test suite below).
21 unit tests in local/scripts/tests/test_scratch_rebuild.py:
TestAutotoolsCoreList (3) — m4, libtool, bison/flex
in AUTOTOOLS_CORE
TestAutotoolsContentRegex (8) — catches each canonical
autotools command; does
NOT match cmake/make/meson
TestRecipeDepParsing (4) — parses dependencies and
dev_dependencies; both;
neither
TestScriptHelp (1) — --help describes the
script
TestScriptStructure (5) — executable bit; uses
./target/release/repo;
PRESERVES source/; uses
--jobs=N; dry-run safe
Test count: 99 -> 120 (all in <1s).
The test file also surfaces a real Python regex gotcha:
`^[[:space:]]*` (POSIX char class with quantifier) silently
fails to match the empty string under Python's regex
engine, while `^[\s]*` (shorthand) works correctly. The
test regex uses the shorthand to avoid this.
Wired into:
make test-scratch-dry-run -> scratch-rebuild.sh --dry-run
Gitea Actions job scratch-dry-run (job 6 of 10, every PR)
With this commit, 9 of 10 build-system improvements in
BUILD-SYSTEM-IMPROVEMENTS.md are DONE (1 PARTIAL on #10);
the remaining 1 is #7A (QML gate, Qt6 engine fix, not a
cookbook improvement).
Verified: `./local/scripts/scratch-rebuild.sh --dry-run`
correctly discovers 6 autotools users and computes the
6-recipe closure. `make test-lint-scripts` still passes
120/120 tests in <1s. Gitea workflow YAML validates with
10 jobs total (was 9).
The C-7 KF6 sed migration script shipped in commit ae749ffb2
was a stub with three structural problems that made it
unrunnable:
1. Called 'repo cook $recipe_dir' with a path, but the
cookbook CLI takes bare names — this would have failed
with 'Package name invalid' on first run.
2. Step 2 created an empty pristine_dir via mktemp -d but
never populated it, so the diff was always empty
(zero-byte output, 'no diff' branch taken, no patch
written).
3. Step 4 was 'SKIP — manual rewrite pending', so the
script wrote no patch even when the inline sed chains
actually edited the source.
Replace the stub with a working v2 that:
- Uses 'repo cook $name' (bare names) throughout
- Snapshots source/ → source-pristine/ BEFORE the cook
so the pristine state is real, not empty
- Runs the full cook (with -i || true so a build failure
after the sed step doesn't abort the migration — we
only need the post-sed source state)
- diffs the real pristine vs post-cook tree, with
--exclude='.git' and --exclude='target' so the diff
is the actual sed edits
- Saves the diff as
local/patches/<name>/01-initial-migration.patch with
a header explaining provenance and the cookbook_apply_patches
invocation the recipe should use
- Cleans up source-pristine/ + runs 'repo unfetch $name' so
the next migration run starts from a clean slate
Add a --dry-run mode that lists candidates without fetching,
for safe CI / smoke testing. Add --recipe=<name> and
--limit=N for targeted runs. Add --help.
Add a test escape hatch via REDBEAR_MIGRATE_RECIPES_DIR and
REDBEAR_MIGRATE_PATCHES_DIR env vars so the candidate
discovery can be exercised on a synthetic tree without
touching the live project. Also gate the cookbook-binary
check on DRY_RUN != 1 so --dry-run doesn't require a
pre-built ./target/release/repo.
13 unit tests in local/scripts/tests/test_migrate_kf6_seds.py:
TestCandidateDiscovery (7):
- discovers sed+tar recipe
- skips recipe without sed
- skips recipe with git source (Rule 1 in-tree, not
sed-migration candidates)
- --limit=N caps results
- --recipe=<name> filters
- existing patch triggers SKIP branch (via static analysis)
- --help output describes the script
TestScriptStructure (6):
- regression: uses bare names, not paths
- uses release/repo binary
- creates patches dir
- diff includes .git/target excludes
- unfetches after capture
- idempotent SKIP when patch exists
Test count: 86/86 → 99/99 (all in <1s).
The actual migration run still requires the full KF6 dep
chain to be built (qtbase, qtdeclarative, kf6-extra-cmake-modules,
plus the recipe's own deps). The 56 recipes are now
discoverable + scriptable; the recipe-by-recipe verification
+ patch validity check remains a per-recipe manual step
(open the patch, confirm the diff matches the inline sed
chain, edit [build].script to call cookbook_apply_patches,
re-cook, byte-compare stage.pkgar).
The v6.0 build-system hardening arc lands 5 of the 10 improvements
proposed in local/docs/BUILD-SYSTEM-IMPROVEMENTS.md. All scripts
have unit tests (62 -> 86, all pass in <1s) and the new 'lint-recipe'
Gitea Actions job runs on every PR.
Per-recipe audit & lint scripts (catch R1/R2 violations BEFORE cook):
* audit-patch-idempotency.py — verifies external patches in
local/patches/ still apply against the upstream pinned rev.
Caught 1 real bug on first run: libdrm/02-redox-dispatch.patch
hunk at xf86drm.c:321 no longer matches libdrm-2.4.125.
* audit-kf6-deps.py — fetches upstream, scans for
find_package(KF6Xxx REQUIRED), compares to recipe deps. Catches
missing + dead dependencies in every kf6-* and qt* recipe.
* classify-cook-failure.py — 17-rule cook-failure classifier.
10-30s diagnosis vs 5-10min manual. exit code is intentionally
inverted (0=novel failure, 1=known fix) for CI signal.
* lint-recipe.py — 7-rule recipe lint: R1-NO-PATCH-FILE,
R1-PATH-SOURCE, R2-INLINE-SED, R2-PATCHES-DIR-UNUSED,
NO-LEGACY-MAKE, R1-LEGACY-APPLY-PATCHES, DEP-NOT-FOUND.
1.1s for 171 recipes (down from 60s+ in v1 via recipe-index
precomputation). Strict mode promotes warnings to errors.
Build-system convenience:
* repair-cook.sh — incremental-build optimizer.
Equivalent to 'repo cook <pkg>' but with a fast-path that
skips configure when CMakeCache.txt is newer than source AND
external patches haven't changed. 30-60s vs 5-10min on KF6
recipes. make repair.<pkg> / make clean-repair.<pkg> targets.
* migrate-kf6-seds-to-patches.sh — migration skeleton for
converting 56 inline 'sed -i' chains across the KF6 recipes
to durable external patches in local/patches/<name>/.
Gitea Actions (host-execution, no Docker):
* .gitea/workflows/build-system.yml — 8-job pipeline:
unit-tests, lint-offline, lint-network (nightly),
lint-recipe (NEW), lint-docs, build-mini, build-full,
smoke (QEMU boot).
* .gitea/RUNNER-SETUP.md — one-time Manjaro/Arch host setup.
Build script hardening:
* build-redbear.sh — when a low-level source (relibc,
kernel, base, bootloader, installer) is newer than its pkgar,
clean build/ and sysroot/ across all recipes too. Low-level
package changes leave autotools packages (pcre2, gettext,
libiconv, ...) with stale configure/libtool scripts referencing
the old runtime, causing 'libtool version mismatch' and
'not a valid libtool object' errors. Cleaning forces
re-configuration; stage/ and source/ are preserved so the
cookbook skips unchanged packages that don't use autotools.
* Makefile — wire lint-cook-failure,
lint-cook-failure-explain, lint-recipe, lint-recipe.%,
lint-recipe.strict, lint-recipe.%.strict, repair.%,
clean-repair.%, test-lint-scripts[-quiet]. Replace the
legacy 'validate-patches' target with a deprecation notice
pointing at validate-sources.
Documentation:
* BUILD-SYSTEM-IMPROVEMENTS.md — mark #2 and #5 DONE; full
implementation notes; updated Make-targets table.
* BUILD-SYSTEM-V6-HARDENING-POSTMORTEM.md (NEW) — 226-line durable
record of the 8-session arc: 32 findings categorized, 5 P0
audit-script bugs fixed, 6 over-broad multi-pattern rules
discovered + fixed, test coverage 86/86 in <1s, 7/10
improvements DONE.
* SCRIPT-BEHAVIOR-MATRIX.md — apply-patches.sh row marked
LEGACY/ARCHIVED; build-redbear.sh row no longer claims to
call it.
* boot-logs/README.md (NEW) — frozen-evidence policy:
'do not edit' rule for REDBEAR-FULL-BOOT-*-RESULTS.md files.
* libdrm/02-redox-dispatch.patch.README (NEW) — 8-step regen
procedure for the broken hunk.
Cleanup:
* local/cache/README.md deleted (1-line placeholder).
* legacy 'make validate-patches' target removed.
Per build-system improvement #5: lint-recipe.py's first run on
the live tree surfaced 1 broken-patch reference (redbear-sessiond),
1 dangling cookbook_apply_patches call (tc), 19 sed -i calls in
sddm (warning — cookbook_apply_patches present, drop-x11.py
migration in progress), 4 sed -i calls in qt6-wayland-smoke
(uncovers the same bug class the libwayland fix prevented).
Improvement #9 from BUILD-SYSTEM-IMPROVEMENTS.md. Scans the tail of
a failed repo cook output and matches it against ~14 known failure
patterns documented in AGENTS.md 'COMPLEX FIX CHECKLIST
(v6.0-impl17)'. Each rule emits a structured fix with the relevant
build flags, paths, and AGENTS.md reference.
Usage:
repo cook kf6-kio 2>&1 | tee /tmp/build.log
classify-cook-failure.py /tmp/build.log
Cuts per-failure diagnosis from 5-10 min of manual pattern-matching to
10-30 seconds. Critical for new contributors. Pure read-only analysis,
no build side effects.
Also opportunistically references the new
audit-patch-idempotency.py from the patch-no-longer-applies rule,
tying the two improvements together.
Two S-sized improvements from BUILD-SYSTEM-IMPROVEMENTS.md:
1. local/scripts/audit-patch-idempotency.py (improvement #3):
Validates that every external patch in local/patches/ is
idempotent (--reverse --check succeeds) and reproducible
(re-clone + re-apply produces an identical tree). Catches the
patch idempotency class of bug at lint time, where it used to
surface as a 2+ hour cookbook failure during a cook. Found a
real bug on first run: local/patches/libdrm/02-redox-dispatch.patch
has a hunk at xf86drm.c:321 that no longer matches the upstream
libdrm-2.4.125.
2. src/cook/script.rs auto-link Qt sysroot dirs (improvement #8):
The cookbook's BUILD_PRESCRIPT now auto-detects if the per-recipe
sysroot has Qt6 (qtbase or qtdeclarative) and creates the canonical
/usr/{plugins,mkspecs,metatypes,modules} symlinks that KF6 recipes
need for cmake to find Qt6Config.cmake's INTERFACE_* paths. New
KF6 recipes that depend on qtbase no longer need to manually
call redbear_qt_link_sysroot_dirs in their build script. Recipes
that need more customization can still call the helper directly
via 'source $COOKBOOK_ROOT/local/scripts/lib/qt-sysroot.sh'.
Previously the script only parsed [package].dependencies, missing
the build-time-only consumers that the cookbook's
get_build_deps_recursive() picks up via [build].{dependencies,
dev_dependencies}. This caused 'rebuild-cascade.sh relibc' to report
'nothing to do' even though the cookbook correctly identifies
uutils, libpciaccess, relibc-tests, and other packages as relibc
build-dep consumers and rebuilds them under 'make live'.
The fix is to also walk the [build] section. The cookbook's own
parser uses the same convention.
Adds a single canonical QEMU launcher for the redbear-full desktop
target so subsequent validation runs (boot logs, init probes,
DRM/KMS registration, audio-stack bring-up, D-Bus system bus) can
be reproduced from a single script.
Coverage:
* ACPI / GPE / Notify plumbing (acpid + kernel AML interpreter)
* v6.0 input architecture (evdevd + virtio-keyboard / virtio-mouse)
* MSI-X USB (xhcid via virtio xhci passthrough) and EHCI fallback
* multi-queue NVMe (nvmed with multiple queues)
* virtio-gpu (redox-drm via /scheme/drm/card0, opt-in via --with-gpu)
* virtio-net (e1000d / virtio-netd via net0 user-mode networking)
* D-Bus system bus (dbus) and redbear-sessiond
* SDDM + KWin Wayland compositor (when redbear-full image exists)
The launcher mirrors the per-target test-*qemu.sh scripts already
in this directory (test-ps2-qemu.sh, test-greeter-qemu.sh,
test-phase4-wayland-qemu.sh) so behaviour is consistent:
q35 + OVMF, KVM opportunistic with TCG fallback, serial log to
local/docs/boot-logs/ with a timestamped filename, snapshot=on
when booting the live ISO so the build artifact is not corrupted
across re-runs.
The QEMU display is intentionally hidden (no -display gtk/vnc);
the boot log is the source of truth, not a UI surface. This
matches the v6.0 2026 NO VESA POLICY: the standard display path
is /scheme/drm/card0 via redox-drm, and the test harness never
opens /scheme/display.vesa/.
Exit code 0 on capture, exit code 1 on missing prerequisites
(firmware, qemu, image); boot outcome is in the log, not the
exit code. The 60s default timeout mirrors the redbear-mini
boot time observed in test-ps2-qemu.sh / test-timer-qemu.sh and
can be overridden with --timeout.
Previously, when relibc/kernel/base/bootloader/installer had commits
newer than their pkgar, the script set NO_CACHE=1, which ran
'make repo_clean' and deleted the entire repo/ directory. This
forced rebuilding the full mesa/llvm21/qt6/kwin stack on every
base source change — adding 30+ minutes of wasted work to every
mini build.
Now we only delete the specific stale package's pkgar and target
dir. The cookbook rebuilds that package; downstream packages pick
up the new relibc/kernel/base via their own dependency tracking
without invalidating the rest of the repo. This is the primary
fix for the 'forever build' complaint: base source changes no
longer trigger a full mesa/llvm rebuild.
Cross-cutting changes tied to the libepoxy/libxcvt/libdisplay-info/
lcms2/libudev stub-removal work:
- config/protected-recipes.toml [graphics]: add libepoxy, libxcvt,
libdisplay-info, lcms2 (real Red Bear recipes under local/recipes)
- config/protected-recipes.toml [libs]: drop the 5 *-stub entries,
add 'libudev' (renamed from libudev-stub)
- local/recipes/AGENTS.md catalog: drop the 5 *-stub rows, update
the 5 real lib rows to reflect the v6.0 2026 fork
- local/scripts/apply-patches.sh: drop the 5 *-stub symlink
creation entries; add libudev symlink entry
(The redbear-full.toml package list was already updated to enable
libdisplay-info, libxcvt, lcms2 and add libudev, as part of the
pam-redbear commit that included both changes.)
llvm21 is a Mesa (graphics) build dep used by mesa and libepoxy for
shader compilation. It is NOT required for the redbear-mini or
redbear-grub text-only targets, but the pre-cook list was cooking
it unconditionally, adding a 30+ minute stall to every mini build.
Pre-cook only relibc + icu for mini/grub. The full desktop chain
(including llvm21) is still pre-cooked for redbear-full.
The pre-cook list (kwin, sddm, qtbase, mesa, libdrm, libepoxy,
redox-drm, lcms2, libdisplay-info, libxcvt) is the desktop graphics
chain. It only applies to redbear-full. For redbear-mini and
redbear-grub (text-only targets), trying to pre-cook kwin and sddm
fails because their build dependencies (QML/QtQuick, libxcb, etc.)
are not in the mini/grub compile surface.
Pre-cook only relibc + icu + llvm21 for mini/grub; pre-cook the full
desktop chain for full.
Replace text-grep cascade detection with a precomputed in-memory graph:
- Build a recipe_index of pkg_name → deps_csv (extracted from
[package]/[build] sections of recipe.toml).
- find_reverse_deps() queries the index in O(1) per package.
- Precompute once, query BFS in O(N) instead of O(N²).
Also fix a bug where empty target string would match every empty-deps
recipe, causing runaway BFS to all 3055 packages (was: 2m40s; now: 6s).
The script now correctly identifies which packages explicitly declare
a target as a [package] or [build] dependency. Implicit cross-compile
toolchain dependencies (relibc, base) are NOT tracked here — they
participate in build via the redoxer/cross setup, not via recipe
declarations. Tracking those requires a different mechanism.
Plan: local/docs/BUILD-SYSTEM-ROBUSTNESS-PLAN.md
- Complete PCI enumeration (class 0x0C, subclass 0x03, prog-if 0x00)
- I/O port register access (inb/outb style via volatile ptr)
- BAR4: I/O port range for operational registers (USBCMD, USBSTS, etc.)
- MMIO-mapped FLBASEADD for frame list base address
- 1024-entry frame list with QH pointers
- Control QH chain for transfer scheduling
- Full controller reset and initialization sequence
- Port polling with connect/disconnect detection
- Device enumeration via control transfers (GET_DESCRIPTOR, SET_ADDRESS)
- Port reset with proper timing (50ms hold, 10ms settle)
- Transfer descriptor (TD) construction for setup/data/status phases
- Wait-for-completion loop with error detection
- All registers documented in registers.rs per UHCI spec
- Scheme interface for scheme:usb access
- Changed make all -> make live to produce .iso files
- Skipped verify-overlay-integrity.sh (corrupts recipe symlinks)
- Added ac_cv_namespace_ok=yes to ICU recipe (toolchain libstdc++ stale)
- Fixed variable reference
- Mini ISO builds successfully via the script
- Changed make all -> make live to produce .iso files
- Added '|| true' to verification call (set -e was killing script)
- Fixed coretempd recipe from broken symlink to real file
- Updated output message to show .iso path
- Removed stale REDBEAR_RELEASE override code
- Fixed NO_CACHE initialization (was unbound CLEAN variable)
- Added --no-cache argument parsing and usage docs
- Auto-unset REDBEAR_RELEASE from .config during dev builds
- Stale-build detection now uses NO_CACHE variable correctly
- Updated help text with environment variable docs
Automatically detects when source repos (relibc, kernel, base,
bootloader, installer) have commits newer than their built pkgars.
If stale, forces a clean rebuild to prevent shipping old binaries.
Also: consolidated clean-rebuild logic into a single conditional.
Per AGENTS.md policy: local recipes ALWAYS supersede WIP packages.
Any WIP directory that shadows a local/recipes/ package is replaced
with a symlink to the local version.
Fixed shadows: bison, flex, m4, meson, ninja-build, libxcvt,
qt6-sensors, libepoxy, mc — all now symlinked to local/recipes/.
Added WIP-local enforcement to build-redbear.sh: auto-detects and
fixes WIP shadows at build time.
Extract protocol-agnostic FenceTimeline from Intel to shared
src/drivers/fence.rs — atomic-based fence tracking suitable
for Intel, VIRGL, and AMD drivers.
Extract protocol-agnostic SyncobjManager from Intel to shared
src/drivers/syncobj.rs — syncobj create/destroy/signal/reset/
wait/query and sync_file fd export/import.
Wire both into VirtioDriver:
- Add FenceTimeline + SyncobjManager fields
- Implement all 5 GpuDriver syncobj trait methods
(create, destroy, wait, export_fd, import_fd)
- Track fence seqnos in virgl_submit_3d (allocate
before submit, signal after completion)
Intel fence.rs and syncobj.rs converted to thin re-export
modules pointing at shared sources — no behavioral change
for Intel driver.
This gives Mesa VIRGL userspace the standard DRM syncobj
API for GPU/compositor synchronization.
Proven recipe pattern for gnulib cross-compilation on Redox:
1. fix_types.h with guarded typedefs for ALL sys/types.h types
2. Strip raw typedefs from GL_CFLAG_GNULIB_WARNINGS after configure
3. Set cache vars for functions gnulib can't detect
Remaining: __fseterr/__freadahead stubs for linker (need relibc-level
or recipe-level .o injection)
The cross-compiler's GCC built-in stddef.h is blocked by relibc's
_STDDEF_H guard, causing size_t/off_t/ptrdiff_t to be undefined.
Add fix_types.h with guarded typedefs and force-include via CPPFLAGS.
Also: comprehensive upstream relibc comparison for systematic import.
Remaining: redoxer env overrides CC, injecting broken stdint typedefs
from its toolchain. This needs a redoxer-level fix to clean the
injected flags before passing to build commands.
Replace 95-line manual symlink list with auto-discovery of all
local/recipes/<category>/<name>/ directories. This fixes 15 missing
symlinks that would have blocked the redbear-full build, including
critical packages: libdrm, qtbase, qtwayland, libinput, libevdev,
seatd, and wayland-protocols.
Special-case aliases preserved:
- kf6-kirigami → kirigami (KDE expects both names)
- wip/wayland/qt6-wayland-smoke (historical WIP path)
L1: Add make qemu-ram target — copies disk image to host tmpfs before
QEMU boots, eliminating host disk I/O during OS runtime.
Usage: make qemu-ram CONFIG_NAME=redbear-full QEMU_MEM=12288
L2: Create local/recipes/AGENTS.md — comprehensive catalog of all 165
custom recipes across 15 categories with descriptions.
L3: CollisionTracker already fully implemented and wired into installer
(recipes/core/installer/source/src/collision.rs, 267 lines).
L4: Add scripts/validate-collision-log.sh to make validate target —
scans build logs for [COLLISION-ERROR]/[COLLISION-WARN] markers
from the runtime CollisionTracker.
check-unwired-patches.sh: scans local/patches/ for .patch files not
referenced in any recipe.toml patches = [...] array. Detects 262
unwired patches (most intentionally kept for reference/rebase).
P2-rebrand-start-message.patch: minimal 39-line patch changing
'Redox OS starting' to 'RedBear OS starting' in x86_64, aarch64,
and riscv64 arch start files. Wired into kernel recipe after
P8-msi.patch. Verified: make r.kernel builds with all 3 patches.
Build system issues surfaced by the detector:
- 250+ kernel individual patches kept for reference (absorbed/)
- ~50 base individual patches — many intentionally unwired
- ~30 relibc patches — may need wiring into relibc recipe
- build-system patches applied by scripts, not recipes
Add redbear-usb-storage-check in-guest binary that validates USB mass
storage read and write I/O: discovers /scheme/disk/ devices, writes a
test pattern to sector 2048, reads it back, verifies match, restores
original content. Updates test-usb-storage-qemu.sh with write-proof
verification step.
Includes all accumulated Red Bear OS work: kernel patches, relibc
patches, driver infrastructure, DRM/GPU, KDE recipes, firmware,
validation tooling, build system hardening, and documentation.